summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Marangi <ansuelsmth@gmail.com>2022-10-18 22:26:34 +0200
committerChristian Marangi <ansuelsmth@gmail.com>2023-05-22 22:28:13 +0200
commitfa79baf4a6e2306d60ac4968fdd8935e64c358ab (patch)
treede53f6e86a88ff7eb4930e574200a0d850efa6a9
parent8fb9bbcf659ac6205554ed1680c57f3c4e0e0f54 (diff)
downloadopenwrt-fa79baf4a6e2306d60ac4968fdd8935e64c358ab.tar.gz
openwrt-fa79baf4a6e2306d60ac4968fdd8935e64c358ab.tar.bz2
openwrt-fa79baf4a6e2306d60ac4968fdd8935e64c358ab.zip
generic: copy backport, hack, pending patch and config from 5.15 to 6.1
Copy backport, hack, pending patch and config from 5.15 to 6.1. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
-rw-r--r--target/linux/generic/backport-6.1/005-v5.17-01-Kbuild-use-Wdeclaration-after-statement.patch73
-rw-r--r--target/linux/generic/backport-6.1/005-v5.17-02-Kbuild-move-to-std-gnu11.patch60
-rw-r--r--target/linux/generic/backport-6.1/005-v5.17-03-Kbuild-use-std-gnu11-for-KBUILD_USERCFLAGS.patch43
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch425
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch153
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch275
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-04-Revert-include-linux-mm_inline.h-fold-__update_lru_s.patch82
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-05-mm-multi-gen-LRU-groundwork.patch807
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-06-mm-multi-gen-LRU-minimal-implementation.patch1447
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-07-mm-multi-gen-LRU-exploit-locality-in-rmap.patch491
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-08-mm-multi-gen-LRU-support-page-table-walks.patch1687
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-09-mm-multi-gen-LRU-optimize-multiple-memcgs.patch315
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-10-mm-multi-gen-LRU-kill-switch.patch498
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-11-mm-multi-gen-LRU-thrashing-prevention.patch226
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-12-mm-multi-gen-LRU-debugfs-interface.patch579
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-13-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch32
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-14-mm-multi-gen-LRU-retry-pages-written-back-while-isol.patch124
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-15-mm-multi-gen-LRU-move-lru_gen_add_mm-out-of-IRQ-off-.patch49
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-17-mm-add-dummy-pmd_young-for-architectures-not-having-.patch96
-rw-r--r--target/linux/generic/backport-6.1/020-v6.1-18-mm-introduce-arch_has_hw_nonleaf_pmd_young.patch113
-rw-r--r--target/linux/generic/backport-6.1/020-v6.2-16-mm-multi-gen-LRU-fix-crash-during-cgroup-migration.patch56
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-19-mm-add-vma_has_recency.patch196
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-20-mm-support-POSIX_FADV_NOREUSE.patch125
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-21-mm-multi-gen-LRU-rename-lru_gen_struct-to-lru_gen_pa.patch348
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-22-mm-multi-gen-LRU-rename-lrugen-lists-to-lrugen-pages.patch162
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-23-mm-multi-gen-LRU-remove-eviction-fairness-safeguard.patch188
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-24-mm-multi-gen-LRU-remove-aging-fairness-safeguard.patch287
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-25-mm-multi-gen-LRU-shuffle-should_run_aging.patch161
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-26-mm-multi-gen-LRU-per-node-lru_gen_page-lists.patch868
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-27-mm-multi-gen-LRU-clarify-scan_control-flags.patch196
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-28-mm-multi-gen-LRU-simplify-arch_has_hw_pte_young-chec.patch34
-rw-r--r--target/linux/generic/backport-6.1/020-v6.3-29-mm-multi-gen-LRU-avoid-futile-retries.patch88
-rw-r--r--target/linux/generic/backport-6.1/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch65
-rw-r--r--target/linux/generic/backport-6.1/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch31
-rw-r--r--target/linux/generic/backport-6.1/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch3078
-rw-r--r--target/linux/generic/backport-6.1/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch1005
-rw-r--r--target/linux/generic/backport-6.1/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch120
-rw-r--r--target/linux/generic/backport-6.1/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch61
-rw-r--r--target/linux/generic/backport-6.1/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch387
-rw-r--r--target/linux/generic/backport-6.1/080-v5.17-clk-gate-Add-devm_clk_hw_register_gate.patch105
-rw-r--r--target/linux/generic/backport-6.1/081-v5.17-regmap-allow-to-define-reg_update_bits-for-no-bus.patch52
-rw-r--r--target/linux/generic/backport-6.1/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch37
-rw-r--r--target/linux/generic/backport-6.1/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch49
-rw-r--r--target/linux/generic/backport-6.1/201-v5.16-scripts-dtc-Update-to-upstream-version-v1.6.1-19-g0a.patch997
-rw-r--r--target/linux/generic/backport-6.1/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch48
-rw-r--r--target/linux/generic/backport-6.1/301-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch166
-rw-r--r--target/linux/generic/backport-6.1/302-v5.16-watchdog-bcm63xx_wdt-fix-fallthrough-warning.patch33
-rw-r--r--target/linux/generic/backport-6.1/330-v5.16-01-MIPS-kernel-proc-add-CPU-option-reporting.patch162
-rw-r--r--target/linux/generic/backport-6.1/330-v5.16-02-MIPS-Fix-using-smp_processor_id-in-preemptible-in-sh.patch62
-rw-r--r--target/linux/generic/backport-6.1/331-v5.19-mtd-spinand-Add-support-for-XTX-XT26G0xA.patch186
-rw-r--r--target/linux/generic/backport-6.1/344-v5.18-01-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch219
-rw-r--r--target/linux/generic/backport-6.1/344-v5.18-02-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch1552
-rw-r--r--target/linux/generic/backport-6.1/345-v5.17-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch32
-rw-r--r--target/linux/generic/backport-6.1/346-v5.18-01-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch64
-rw-r--r--target/linux/generic/backport-6.1/346-v5.18-02-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch166
-rw-r--r--target/linux/generic/backport-6.1/346-v5.18-03-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch39
-rw-r--r--target/linux/generic/backport-6.1/347-v6.0-phy-marvell-phy-mvebu-a3700-comphy-Remove-broken-res.patch194
-rw-r--r--target/linux/generic/backport-6.1/350-v5.18-regmap-add-configurable-downshift-for-addresses.patch90
-rw-r--r--target/linux/generic/backport-6.1/351-v5.18-regmap-allow-a-defined-reg_base-to-be-added-to-every.patch95
-rw-r--r--target/linux/generic/backport-6.1/352-v6.3-regmap-apply-reg_base-and-reg_downshift-for-single-r.patch57
-rw-r--r--target/linux/generic/backport-6.1/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch72
-rw-r--r--target/linux/generic/backport-6.1/401-v6.0-mtd-parsers-add-support-for-Sercomm-partitions.patch302
-rw-r--r--target/linux/generic/backport-6.1/402-v6.0-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch106
-rw-r--r--target/linux/generic/backport-6.1/403-v6.1-mtd-allow-getting-MTD-device-associated-with-a-speci.patch72
-rw-r--r--target/linux/generic/backport-6.1/404-v6.0-mtd-core-check-partition-before-dereference.patch30
-rw-r--r--target/linux/generic/backport-6.1/405-v6.1-mtd-core-add-missing-of_node_get-in-dynamic-partitio.patch101
-rw-r--r--target/linux/generic/backport-6.1/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch65
-rw-r--r--target/linux/generic/backport-6.1/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch84
-rw-r--r--target/linux/generic/backport-6.1/407-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch32
-rw-r--r--target/linux/generic/backport-6.1/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch47
-rw-r--r--target/linux/generic/backport-6.1/410-v5.18-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch33
-rw-r--r--target/linux/generic/backport-6.1/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch58
-rw-r--r--target/linux/generic/backport-6.1/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch33
-rw-r--r--target/linux/generic/backport-6.1/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch84
-rw-r--r--target/linux/generic/backport-6.1/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch91
-rw-r--r--target/linux/generic/backport-6.1/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch229
-rw-r--r--target/linux/generic/backport-6.1/422-v5.19-mtd-spi-nor-support-eon-en25qh256a.patch49
-rw-r--r--target/linux/generic/backport-6.1/423-v6.1-0001-mtd-track-maximum-number-of-bitflips-for-each-read-r.patch73
-rw-r--r--target/linux/generic/backport-6.1/423-v6.1-0002-mtd-always-initialize-stats-in-struct-mtd_oob_ops.patch325
-rw-r--r--target/linux/generic/backport-6.1/423-v6.1-0003-mtd-add-ECC-error-accounting-for-each-read-request.patch172
-rw-r--r--target/linux/generic/backport-6.1/423-v6.1-0004-mtdchar-add-MEMREAD-ioctl.patch321
-rw-r--r--target/linux/generic/backport-6.1/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch35
-rw-r--r--target/linux/generic/backport-6.1/424-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch47
-rw-r--r--target/linux/generic/backport-6.1/600-v5.18-page_pool-Add-allocation-stats.patch165
-rw-r--r--target/linux/generic/backport-6.1/601-v5.18-page_pool-Add-recycle-stats.patch140
-rw-r--r--target/linux/generic/backport-6.1/602-v5.18-page_pool-Add-function-to-batch-and-return-stats.patch77
-rw-r--r--target/linux/generic/backport-6.1/603-v5.19-page_pool-Add-recycle-stats-to-page_pool_put_page_bu.patch55
-rw-r--r--target/linux/generic/backport-6.1/604-v5.19-net-page_pool-introduce-ethtool-stats.patch147
-rw-r--r--target/linux/generic/backport-6.1/605-v5.18-xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch99
-rw-r--r--target/linux/generic/backport-6.1/606-v5.18-xdp-add-frags-support-to-xdp_return_-buff-frame.patch137
-rw-r--r--target/linux/generic/backport-6.1/607-v5.18-net-skbuff-add-size-metadata-to-skb_shared_info-for-.patch31
-rw-r--r--target/linux/generic/backport-6.1/608-v5.18-net-veth-Account-total-xdp_frame-len-running-ndo_xdp.patch65
-rw-r--r--target/linux/generic/backport-6.1/609-v5.18-veth-Allow-jumbo-frames-in-xdp-mode.patch40
-rw-r--r--target/linux/generic/backport-6.1/610-v6.3-net-page_pool-use-in_softirq-instead.patch56
-rw-r--r--target/linux/generic/backport-6.1/611-v6.3-net-add-helper-eth_addr_add.patch41
-rw-r--r--target/linux/generic/backport-6.1/700-v5.17-net-dsa-introduce-tagger-owned-storage-for-private.patch279
-rw-r--r--target/linux/generic/backport-6.1/701-v5.17-dsa-make-tagging-protocols-connect-to-individual-switches.patch274
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch327
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-01-arm64-dts-mediatek-mt7622-add-support-for-coherent-D.patch30
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch1679
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch269
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-04-arm64-dts-mediatek-mt7622-introduce-nodes-for-Wirele.patch62
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-05-net-ethernet-mtk_eth_soc-add-ipv6-flow-offload-suppo.patch79
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-06-net-ethernet-mtk_eth_soc-support-TC_SETUP_BLOCK-for-.patch29
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch159
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch424
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-09-net-ethernet-mtk_eth_soc-remove-bridge-flow-offload-.patch44
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch553
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-11-net-ethernet-mtk_eth_soc-wed-fix-sparse-endian-warni.patch56
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-12-net-ethernet-mtk_eth_soc-fix-return-value-check-in-m.patch25
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch35
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-14-net-ethernet-mtk_eth_soc-use-after-free-in-__mtk_ppe.patch33
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-15-net-ethernet-mtk_eth_soc-add-check-for-allocation-fa.patch22
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-16-eth-mtk_eth_soc-silence-the-GCC-12-array-bounds-warn.patch26
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch52
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch206
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch167
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch78
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch109
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch39
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch102
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch46
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch68
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch814
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch917
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch135
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch33
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch138
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-31-net-ethernet-mtk_eth_soc-fix-error-code-in-mtk_flow_.patch25
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch47
-rw-r--r--target/linux/generic/backport-6.1/702-v5.19-34-eth-mtk_ppe-fix-up-after-merge.patch28
-rw-r--r--target/linux/generic/backport-6.1/703-00-v5.16-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch948
-rw-r--r--target/linux/generic/backport-6.1/703-01-v5.16-net-phylink-add-MAC-phy_interface_t-bitmap.patch24
-rw-r--r--target/linux/generic/backport-6.1/703-02-v5.16-net-phylink-use-supported_interfaces-for-phylink-val.patch98
-rw-r--r--target/linux/generic/backport-6.1/703-03-v5.16-net-dsa-populate-supported_interfaces-member.patch63
-rw-r--r--target/linux/generic/backport-6.1/703-04-v5.17-net-dsa-consolidate-phylink-creation.patch149
-rw-r--r--target/linux/generic/backport-6.1/703-05-v5.17-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch51
-rw-r--r--target/linux/generic/backport-6.1/703-06-v5.18-net-dsa-add-support-for-phylink-mac_select_pcs.patch59
-rw-r--r--target/linux/generic/backport-6.1/703-07-v5.16-net-phy-add-phy_interface_t-bitmap-support.patch61
-rw-r--r--target/linux/generic/backport-6.1/703-08-v5.17-net-phylink-add-mac_select_pcs-method-to-phylink_mac.patch197
-rw-r--r--target/linux/generic/backport-6.1/703-09-v5.17-net-phylink-add-generic-validate-implementation.patch341
-rw-r--r--target/linux/generic/backport-6.1/703-10-v5.16-net-dsa-introduce-helpers-for-iterating-through-port.patch68
-rw-r--r--target/linux/generic/backport-6.1/703-11-v5.17-net-phylink-add-pcs_validate-method.patch106
-rw-r--r--target/linux/generic/backport-6.1/703-12-v5.17-net-phylink-add-legacy_pre_march2020-indicator.patch43
-rw-r--r--target/linux/generic/backport-6.1/703-13-v5.17-net-dsa-mark-DSA-phylink-as-legacy_pre_march2020.patch36
-rw-r--r--target/linux/generic/backport-6.1/703-14-v5.17-net-phylink-use-legacy_pre_march2020.patch115
-rw-r--r--target/linux/generic/backport-6.1/703-15-v5.18-net-phy-phylink-fix-DSA-mac_select_pcs-introduction.patch88
-rw-r--r--target/linux/generic/backport-6.1/703-16-v5.16-net-mvneta-populate-supported_interfaces-member.patch48
-rw-r--r--target/linux/generic/backport-6.1/703-17-v5.16-net-mvneta-remove-interface-checks-in-mvneta_validat.patch35
-rw-r--r--target/linux/generic/backport-6.1/703-18-v5.16-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch55
-rw-r--r--target/linux/generic/backport-6.1/703-19-v5.17-net-mvneta-use-phylink_generic_validate.patch72
-rw-r--r--target/linux/generic/backport-6.1/703-20-v5.17-net-mvneta-mark-as-a-legacy_pre_march2020-driver.patch29
-rw-r--r--target/linux/generic/backport-6.1/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch43
-rw-r--r--target/linux/generic/backport-6.1/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch75
-rw-r--r--target/linux/generic/backport-6.1/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch42
-rw-r--r--target/linux/generic/backport-6.1/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch84
-rw-r--r--target/linux/generic/backport-6.1/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch29
-rw-r--r--target/linux/generic/backport-6.1/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch40
-rw-r--r--target/linux/generic/backport-6.1/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch35
-rw-r--r--target/linux/generic/backport-6.1/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch40
-rw-r--r--target/linux/generic/backport-6.1/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch40
-rw-r--r--target/linux/generic/backport-6.1/704-10-v5.19-net-mtk_eth_soc-correct-802.3z-speed-setting.patch60
-rw-r--r--target/linux/generic/backport-6.1/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch101
-rw-r--r--target/linux/generic/backport-6.1/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch60
-rw-r--r--target/linux/generic/backport-6.1/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch89
-rw-r--r--target/linux/generic/backport-6.1/704-14-v5.19-net-mtk_eth_soc-add-fixme-comment-for-state-speed-us.patch38
-rw-r--r--target/linux/generic/backport-6.1/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch79
-rw-r--r--target/linux/generic/backport-6.1/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch57
-rw-r--r--target/linux/generic/backport-6.1/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch254
-rw-r--r--target/linux/generic/backport-6.1/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch262
-rw-r--r--target/linux/generic/backport-6.1/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch106
-rw-r--r--target/linux/generic/backport-6.1/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch166
-rw-r--r--target/linux/generic/backport-6.1/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch172
-rw-r--r--target/linux/generic/backport-6.1/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch34
-rw-r--r--target/linux/generic/backport-6.1/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch86
-rw-r--r--target/linux/generic/backport-6.1/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch131
-rw-r--r--target/linux/generic/backport-6.1/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch385
-rw-r--r--target/linux/generic/backport-6.1/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch80
-rw-r--r--target/linux/generic/backport-6.1/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch34
-rw-r--r--target/linux/generic/backport-6.1/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch116
-rw-r--r--target/linux/generic/backport-6.1/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch87
-rw-r--r--target/linux/generic/backport-6.1/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch75
-rw-r--r--target/linux/generic/backport-6.1/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch117
-rw-r--r--target/linux/generic/backport-6.1/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch330
-rw-r--r--target/linux/generic/backport-6.1/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch291
-rw-r--r--target/linux/generic/backport-6.1/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch110
-rw-r--r--target/linux/generic/backport-6.1/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch340
-rw-r--r--target/linux/generic/backport-6.1/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch95
-rw-r--r--target/linux/generic/backport-6.1/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch113
-rw-r--r--target/linux/generic/backport-6.1/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch218
-rw-r--r--target/linux/generic/backport-6.1/707-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch394
-rw-r--r--target/linux/generic/backport-6.1/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch74
-rw-r--r--target/linux/generic/backport-6.1/711-v6.0-01-net-ethernet-mtk_eth_soc-fix-off-by-one-check-of-ARR.patch31
-rw-r--r--target/linux/generic/backport-6.1/711-v6.0-02-net-ethernet-mtk_ppe-fix-possible-NULL-pointer-deref.patch27
-rw-r--r--target/linux/generic/backport-6.1/711-v6.0-03-net-ethernet-mtk-ppe-fix-traffic-offload-with-bridge.patch64
-rw-r--r--target/linux/generic/backport-6.1/711-v6.0-04-net-ethernet-mtk_eth_soc-remove-mtk_foe_entry_timest.patch33
-rw-r--r--target/linux/generic/backport-6.1/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch35
-rw-r--r--target/linux/generic/backport-6.1/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch127
-rw-r--r--target/linux/generic/backport-6.1/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch199
-rw-r--r--target/linux/generic/backport-6.1/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch318
-rw-r--r--target/linux/generic/backport-6.1/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch80
-rw-r--r--target/linux/generic/backport-6.1/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch251
-rw-r--r--target/linux/generic/backport-6.1/718-v6.0-net-ethernet-mtk_eth_soc-fix-typo-in-__mtk_foe_entry.patch27
-rw-r--r--target/linux/generic/backport-6.1/719-v6.0-net-ethernet-mtk_eth_soc-check-max-allowed-value-in-.patch28
-rw-r--r--target/linux/generic/backport-6.1/720-v6.0-net-ethernet-mtk_eth_wed-add-mtk_wed_configure_irq-a.patch189
-rw-r--r--target/linux/generic/backport-6.1/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch942
-rw-r--r--target/linux/generic/backport-6.1/722-v6.0-net-ethernet-mtk_eth_wed-add-axi-bus-support.patch237
-rw-r--r--target/linux/generic/backport-6.1/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch882
-rw-r--r--target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch26
-rw-r--r--target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-fix-wrong-use-of-new-helper.patch46
-rw-r--r--target/linux/generic/backport-6.1/725-v6.0-net-ethernet-mtk_eth_soc-fix-usage-of-foe_entry_size.patch49
-rw-r--r--target/linux/generic/backport-6.1/726-v6.0-net-ethernet-mtk_eth_soc-fix-mask-of-RX_DMA_GET_SPOR.patch32
-rw-r--r--target/linux/generic/backport-6.1/727-v6.1-net-ethernet-mtk_eth_soc-fix-state-in-__mtk_foe_entr.patch37
-rw-r--r--target/linux/generic/backport-6.1/728-v6.1-01-net-ethernet-mtk_eth_soc-fix-possible-memory-leak-in.patch73
-rw-r--r--target/linux/generic/backport-6.1/728-v6.1-02-net-ethernet-mtk_eth_wed-add-missing-put_device-in-m.patch47
-rw-r--r--target/linux/generic/backport-6.1/728-v6.1-03-net-ethernet-mtk_eth_wed-add-missing-of_node_put.patch43
-rw-r--r--target/linux/generic/backport-6.1/729-01-v6.1-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch591
-rw-r--r--target/linux/generic/backport-6.1/729-02-v6.1-net-ethernet-mtk_wed-introduce-wed-wo-support.patch737
-rw-r--r--target/linux/generic/backport-6.1/729-03-v6.1-net-ethernet-mtk_wed-rename-tx_wdma-array-in-rx_wdma.patch79
-rw-r--r--target/linux/generic/backport-6.1/729-04-v6.1-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch1521
-rw-r--r--target/linux/generic/backport-6.1/729-05-v6.1-net-ethernet-mtk_wed-add-rx-mib-counters.patch149
-rw-r--r--target/linux/generic/backport-6.1/729-06-v6.1-net-ethernet-mtk_eth_soc-do-not-overwrite-mtu-config.patch98
-rw-r--r--target/linux/generic/backport-6.1/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch36
-rw-r--r--target/linux/generic/backport-6.1/729-08-v6.2-net-ethernet-mtk_eth_soc-fix-RSTCTRL_PPE-0-1-definit.patch63
-rw-r--r--target/linux/generic/backport-6.1/729-09-v6.2-net-ethernet-mtk_wed-add-wcid-overwritten-support-fo.patch80
-rw-r--r--target/linux/generic/backport-6.1/729-10-v6.2-net-ethernet-mtk_wed-return-status-value-in-mtk_wdma.patch85
-rw-r--r--target/linux/generic/backport-6.1/729-11-v6.2-net-ethernet-mtk_wed-move-MTK_WDMA_RESET_IDX_TX-conf.patch52
-rw-r--r--target/linux/generic/backport-6.1/729-12-v6.2-net-ethernet-mtk_wed-update-mtk_wed_stop.patch98
-rw-r--r--target/linux/generic/backport-6.1/729-13-v6.2-net-ethernet-mtk_wed-add-mtk_wed_rx_reset-routine.patch309
-rw-r--r--target/linux/generic/backport-6.1/729-14-v6.2-net-ethernet-mtk_wed-add-reset-to-tx_ring_setup-call.patch103
-rw-r--r--target/linux/generic/backport-6.1/729-15-v6.2-net-ethernet-mtk_wed-fix-sleep-while-atomic-in-mtk_w.patch103
-rw-r--r--target/linux/generic/backport-6.1/729-16-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-rx-qu.patch52
-rw-r--r--target/linux/generic/backport-6.1/729-17-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-tx-qu.patch75
-rw-r--r--target/linux/generic/backport-6.1/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch70
-rw-r--r--target/linux/generic/backport-6.1/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch107
-rw-r--r--target/linux/generic/backport-6.1/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch262
-rw-r--r--target/linux/generic/backport-6.1/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch249
-rw-r--r--target/linux/generic/backport-6.1/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch124
-rw-r--r--target/linux/generic/backport-6.1/729-23-v6.3-net-ethernet-mtk_wed-add-reset-to-rx_ring_setup-call.patch106
-rw-r--r--target/linux/generic/backport-6.1/730-01-v6.3-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch22
-rw-r--r--target/linux/generic/backport-6.1/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch143
-rw-r--r--target/linux/generic/backport-6.1/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch52
-rw-r--r--target/linux/generic/backport-6.1/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch654
-rw-r--r--target/linux/generic/backport-6.1/730-05-v6.3-net-dsa-tag_mtk-assign-per-port-queues.patch20
-rw-r--r--target/linux/generic/backport-6.1/730-06-v6.3-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch93
-rw-r--r--target/linux/generic/backport-6.1/730-07-v6.3-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch28
-rw-r--r--target/linux/generic/backport-6.1/730-08-v6.3-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch72
-rw-r--r--target/linux/generic/backport-6.1/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch192
-rw-r--r--target/linux/generic/backport-6.1/730-10-v6.3-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch37
-rw-r--r--target/linux/generic/backport-6.1/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch42
-rw-r--r--target/linux/generic/backport-6.1/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch54
-rw-r--r--target/linux/generic/backport-6.1/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch129
-rw-r--r--target/linux/generic/backport-6.1/730-15-v6.3-net-ethernet-mtk_wed-No-need-to-clear-memory-after-a.patch26
-rw-r--r--target/linux/generic/backport-6.1/730-16-v6.3-net-ethernet-mtk_wed-fix-some-possible-NULL-pointer-.patch61
-rw-r--r--target/linux/generic/backport-6.1/730-17-v6.3-net-ethernet-mtk_wed-fix-possible-deadlock-if-mtk_we.patch58
-rw-r--r--target/linux/generic/backport-6.1/730-18-v6.3-net-ethernet-mtk_eth_soc-fix-tx-throughput-regressio.patch31
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-01-net-ethernet-mtk_eth_soc-Avoid-truncating-allocation.patch60
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch55
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch74
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch46
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch130
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch52
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch48
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch48
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch52
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch63
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch132
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch119
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch52
-rw-r--r--target/linux/generic/backport-6.1/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch33
-rw-r--r--target/linux/generic/backport-6.1/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch48
-rw-r--r--target/linux/generic/backport-6.1/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch103
-rw-r--r--target/linux/generic/backport-6.1/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch200
-rw-r--r--target/linux/generic/backport-6.1/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch76
-rw-r--r--target/linux/generic/backport-6.1/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch512
-rw-r--r--target/linux/generic/backport-6.1/733-v6.4-21-net-mtk_eth_soc-use-WO-firmware-for-MT7981.patch46
-rw-r--r--target/linux/generic/backport-6.1/733-v6.4-22-net-ethernet-mtk_eth_soc-fix-NULL-pointer-dereferenc.patch28
-rw-r--r--target/linux/generic/backport-6.1/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch84
-rw-r--r--target/linux/generic/backport-6.1/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch54
-rw-r--r--target/linux/generic/backport-6.1/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch48
-rw-r--r--target/linux/generic/backport-6.1/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch131
-rw-r--r--target/linux/generic/backport-6.1/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch42
-rw-r--r--target/linux/generic/backport-6.1/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch32
-rw-r--r--target/linux/generic/backport-6.1/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch205
-rw-r--r--target/linux/generic/backport-6.1/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch65
-rw-r--r--target/linux/generic/backport-6.1/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch45
-rw-r--r--target/linux/generic/backport-6.1/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch95
-rw-r--r--target/linux/generic/backport-6.1/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch131
-rw-r--r--target/linux/generic/backport-6.1/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch91
-rw-r--r--target/linux/generic/backport-6.1/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch27
-rw-r--r--target/linux/generic/backport-6.1/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch127
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch80
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch30
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch127
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch29
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch153
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch295
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch33
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch65
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch37
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch92
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch32
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch78
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch159
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch124
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch26
-rw-r--r--target/linux/generic/backport-6.1/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch631
-rw-r--r--target/linux/generic/backport-6.1/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch28
-rw-r--r--target/linux/generic/backport-6.1/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch151
-rw-r--r--target/linux/generic/backport-6.1/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch47
-rw-r--r--target/linux/generic/backport-6.1/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch48
-rw-r--r--target/linux/generic/backport-6.1/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch46
-rw-r--r--target/linux/generic/backport-6.1/753-v5.17-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch29
-rw-r--r--target/linux/generic/backport-6.1/754-v5.17-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch507
-rw-r--r--target/linux/generic/backport-6.1/755-v5.17-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch25
-rw-r--r--target/linux/generic/backport-6.1/756-v5.17-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch46
-rw-r--r--target/linux/generic/backport-6.1/757-v5.17-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch249
-rw-r--r--target/linux/generic/backport-6.1/758-v5.17-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch120
-rw-r--r--target/linux/generic/backport-6.1/759-v5.17-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch53
-rw-r--r--target/linux/generic/backport-6.1/760-v5.17-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch78
-rw-r--r--target/linux/generic/backport-6.1/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch142
-rw-r--r--target/linux/generic/backport-6.1/762-v5.17-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch155
-rw-r--r--target/linux/generic/backport-6.1/763-v5.17-net-next-net-dsa-qca8k-add-LAG-support.patch288
-rw-r--r--target/linux/generic/backport-6.1/764-v5.17-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch40
-rw-r--r--target/linux/generic/backport-6.1/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch52
-rw-r--r--target/linux/generic/backport-6.1/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch34
-rw-r--r--target/linux/generic/backport-6.1/765-v5.17-03-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch91
-rw-r--r--target/linux/generic/backport-6.1/765-v5.17-04-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch78
-rw-r--r--target/linux/generic/backport-6.1/765-v5.17-05-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch118
-rw-r--r--target/linux/generic/backport-6.1/765-v5.17-06-net-next-net-dsa-setup-master-before-ports.patch115
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch254
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch89
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch86
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch71
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch27
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch110
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch45
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch116
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch67
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch363
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch226
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch287
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch208
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch164
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch206
-rw-r--r--target/linux/generic/backport-6.1/766-v5.18-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch104
-rw-r--r--target/linux/generic/backport-6.1/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch28
-rw-r--r--target/linux/generic/backport-6.1/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch34
-rw-r--r--target/linux/generic/backport-6.1/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch79
-rw-r--r--target/linux/generic/backport-6.1/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch116
-rw-r--r--target/linux/generic/backport-6.1/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch173
-rw-r--r--target/linux/generic/backport-6.1/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch39
-rw-r--r--target/linux/generic/backport-6.1/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch33
-rw-r--r--target/linux/generic/backport-6.1/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch44
-rw-r--r--target/linux/generic/backport-6.1/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch7389
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch157
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch77
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch6532
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch135
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch145
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch137
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch281
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch237
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch227
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch704
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch232
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch448
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch384
-rw-r--r--target/linux/generic/backport-6.1/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch102
-rw-r--r--target/linux/generic/backport-6.1/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch29
-rw-r--r--target/linux/generic/backport-6.1/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch77
-rw-r--r--target/linux/generic/backport-6.1/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch52
-rw-r--r--target/linux/generic/backport-6.1/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch141
-rw-r--r--target/linux/generic/backport-6.1/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch118
-rw-r--r--target/linux/generic/backport-6.1/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch270
-rw-r--r--target/linux/generic/backport-6.1/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch51
-rw-r--r--target/linux/generic/backport-6.1/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch61
-rw-r--r--target/linux/generic/backport-6.1/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch115
-rw-r--r--target/linux/generic/backport-6.1/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch57
-rw-r--r--target/linux/generic/backport-6.1/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch107
-rw-r--r--target/linux/generic/backport-6.1/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch142
-rw-r--r--target/linux/generic/backport-6.1/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch37
-rw-r--r--target/linux/generic/backport-6.1/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch1289
-rw-r--r--target/linux/generic/backport-6.1/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch1161
-rw-r--r--target/linux/generic/backport-6.1/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch73
-rw-r--r--target/linux/generic/backport-6.1/776-v6.1-01-net-dsa-qca8k-fix-inband-mgmt-for-big-endian-systems.patch156
-rw-r--r--target/linux/generic/backport-6.1/776-v6.1-02-net-dsa-qca8k-fix-ethtool-autocast-mib-for-big-endia.patch81
-rw-r--r--target/linux/generic/backport-6.1/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch102
-rw-r--r--target/linux/generic/backport-6.1/777-v6.2-02-net-dsa-tag_qca-fix-wrong-MGMT_DATA2-size.patch34
-rw-r--r--target/linux/generic/backport-6.1/777-v6.2-03-Revert-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch172
-rw-r--r--target/linux/generic/backport-6.1/777-v6.2-04-net-dsa-qca8k-introduce-single-mii-read-write-lo-hi.patch150
-rw-r--r--target/linux/generic/backport-6.1/777-v6.2-05-net-dsa-qca8k-improve-mdio-master-read-write-by-usin.patch73
-rw-r--r--target/linux/generic/backport-6.1/778-v5.18-01-net-phy-at803x-add-fiber-support.patch193
-rw-r--r--target/linux/generic/backport-6.1/778-v5.18-02-net-phy-at803x-support-downstream-SFP-cage.patch95
-rw-r--r--target/linux/generic/backport-6.1/778-v5.18-03-net-phy-at803x-fix-NULL-pointer-dereference-on-AR9331-PHY.patch56
-rw-r--r--target/linux/generic/backport-6.1/778-v5.18-04-net-phy-at803x-fix-error-return-code-in-at803x_probe.patch31
-rw-r--r--target/linux/generic/backport-6.1/780-v5.16-bus-mhi-pci_generic-Introduce-Sierra-EM919X-support.patch80
-rw-r--r--target/linux/generic/backport-6.1/781-v6.1-bus-mhi-host-always-print-detected-modem-name.patch37
-rw-r--r--target/linux/generic/backport-6.1/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch130
-rw-r--r--target/linux/generic/backport-6.1/783-v6.1-net-sfp-re-implement-soft-state-polling-setup.patch98
-rw-r--r--target/linux/generic/backport-6.1/784-v6.1-net-sfp-move-quirk-handling-into-sfp.c.patch291
-rw-r--r--target/linux/generic/backport-6.1/785-v6.1-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch69
-rw-r--r--target/linux/generic/backport-6.1/786-v6.1-net-sfp-move-Huawei-MA5671A-fixup.patch48
-rw-r--r--target/linux/generic/backport-6.1/787-v6.1-net-sfp-add-support-for-HALNy-GPON-SFP.patch79
-rw-r--r--target/linux/generic/backport-6.1/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch514
-rw-r--r--target/linux/generic/backport-6.1/789-v6.3-net-sfp-add-quirk-enabling-2500Base-x-for-HG-MXPD-48.patch37
-rw-r--r--target/linux/generic/backport-6.1/790-v6.0-net-mii-add-mii_bmcr_encode_fixed.patch55
-rw-r--r--target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch1724
-rw-r--r--target/linux/generic/backport-6.1/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch49
-rw-r--r--target/linux/generic/backport-6.1/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch140
-rw-r--r--target/linux/generic/backport-6.1/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch26
-rw-r--r--target/linux/generic/backport-6.1/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch29
-rw-r--r--target/linux/generic/backport-6.1/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch107
-rw-r--r--target/linux/generic/backport-6.1/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch343
-rw-r--r--target/linux/generic/backport-6.1/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch100
-rw-r--r--target/linux/generic/backport-6.1/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch302
-rw-r--r--target/linux/generic/backport-6.1/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch34
-rw-r--r--target/linux/generic/backport-6.1/797-v5.17-net-usb-ax88179_178a-add-TSO-feature.patch68
-rw-r--r--target/linux/generic/backport-6.1/800-v6.0-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch125
-rw-r--r--target/linux/generic/backport-6.1/800-v6.0-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch356
-rw-r--r--target/linux/generic/backport-6.1/801-v6.0-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch30
-rw-r--r--target/linux/generic/backport-6.1/801-v6.0-0002-leds-add-help-info-about-BCM63138-module-name.patch28
-rw-r--r--target/linux/generic/backport-6.1/801-v6.0-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch30
-rw-r--r--target/linux/generic/backport-6.1/802-v5.16-0001-nvmem-core-rework-nvmem-cell-instance-creation.patch456
-rw-r--r--target/linux/generic/backport-6.1/802-v5.16-0002-nvmem-core-add-nvmem-cell-post-processing-callback.patch82
-rw-r--r--target/linux/generic/backport-6.1/802-v5.16-0003-nvmem-imx-ocotp-add-support-for-post-processing.patch92
-rw-r--r--target/linux/generic/backport-6.1/803-v5.17-0002-nvmem-mtk-efuse-support-minimum-one-byte-access-stri.patch47
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0001-nvmem-core-Remove-unused-devm_nvmem_unregister.patch72
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0002-nvmem-core-Use-devm_add_action_or_reset.patch58
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0003-nvmem-core-Check-input-parameter-for-NULL-in-nvmem_u.patch30
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0004-nvmem-qfprom-fix-kerneldoc-warning.patch29
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0005-nvmem-sunxi_sid-Add-support-for-D1-variant.patch38
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0006-nvmem-meson-mx-efuse-replace-unnecessary-devm_kstrdu.patch28
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0007-nvmem-add-driver-for-Layerscape-SFP-Security-Fuse-Pr.patch139
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0008-nvmem-qfprom-Increase-fuse-blow-timeout-to-prevent-w.patch32
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch291
-rw-r--r--target/linux/generic/backport-6.1/804-v5.18-0010-nvmem-brcm_nvram-parse-NVRAM-content-into-NVMEM-cell.patch146
-rw-r--r--target/linux/generic/backport-6.1/805-v5.19-0001-nvmem-bcm-ocotp-mark-ACPI-device-ID-table-as-maybe-u.patch32
-rw-r--r--target/linux/generic/backport-6.1/805-v5.19-0002-nvmem-sunplus-ocotp-staticize-sp_otp_v0.patch30
-rw-r--r--target/linux/generic/backport-6.1/805-v5.19-0003-nvmem-sunplus-ocotp-drop-useless-probe-confirmation.patch27
-rw-r--r--target/linux/generic/backport-6.1/805-v5.19-0004-nvmem-core-support-passing-DT-node-in-cell-info.patch41
-rw-r--r--target/linux/generic/backport-6.1/805-v5.19-0005-nvmem-brcm_nvram-find-Device-Tree-nodes-for-NVMEM-ce.patch38
-rw-r--r--target/linux/generic/backport-6.1/805-v5.19-0006-nvmem-Add-Apple-eFuse-driver.patch130
-rw-r--r--target/linux/generic/backport-6.1/805-v5.19-0007-nvmem-qfprom-using-pm_runtime_resume_and_get-instead.patch31
-rw-r--r--target/linux/generic/backport-6.1/805-v5.19-0008-nvmem-sfp-Use-regmap.patch109
-rw-r--r--target/linux/generic/backport-6.1/805-v5.19-0009-nvmem-sfp-Add-support-for-TA-2.1-devices.patch38
-rw-r--r--target/linux/generic/backport-6.1/806-v6.0-0001-nvmem-microchip-otpc-add-support.patch389
-rw-r--r--target/linux/generic/backport-6.1/806-v6.0-0002-nvmem-mtk-efuse-Simplify-with-devm_platform_get_and_.patch32
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0002-nvmem-add-driver-handling-U-Boot-environment-variabl.patch286
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0004-nvmem-brcm_nvram-Use-kzalloc-for-allocating-only-one.patch29
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0005-nvmem-prefix-all-symbols-with-NVMEM_.patch281
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0006-nvmem-sort-config-symbols-alphabetically.patch535
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0007-nvmem-u-boot-env-find-Device-Tree-nodes-for-NVMEM-ce.patch31
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0008-nvmem-lan9662-otp-add-support.patch274
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0009-nvmem-u-boot-env-fix-crc32-casting-type.patch32
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0010-nvmem-lan9662-otp-Fix-compatible-string.patch34
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0011-nvmem-u-boot-env-fix-crc32_data_offset-on-redundant-.patch59
-rw-r--r--target/linux/generic/backport-6.1/807-v6.1-0013-nvmem-lan9662-otp-Change-return-type-of-lan9662_otp_.patch35
-rw-r--r--target/linux/generic/backport-6.1/808-v6.2-0001-nvmem-stm32-move-STM32MP15_BSEC_NUM_LOWER-in-config.patch82
-rw-r--r--target/linux/generic/backport-6.1/808-v6.2-0002-nvmem-stm32-add-warning-when-upper-OTPs-are-updated.patch34
-rw-r--r--target/linux/generic/backport-6.1/808-v6.2-0003-nvmem-stm32-add-nvmem-type-attribute.patch26
-rw-r--r--target/linux/generic/backport-6.1/808-v6.2-0004-nvmem-stm32-fix-spelling-typo-in-comment.patch27
-rw-r--r--target/linux/generic/backport-6.1/808-v6.2-0005-nvmem-Kconfig-Fix-spelling-mistake-controlls-control.patch27
-rw-r--r--target/linux/generic/backport-6.1/808-v6.2-0006-nvmem-u-boot-env-add-Broadcom-format-support.patch67
-rw-r--r--target/linux/generic/backport-6.1/808-v6.2-0007-nvmem-brcm_nvram-Add-check-for-kzalloc.patch30
-rw-r--r--target/linux/generic/backport-6.1/808-v6.2-0008-nvmem-sunxi_sid-Always-use-32-bit-MMIO-reads.patch55
-rw-r--r--target/linux/generic/backport-6.1/808-v6.2-0013-nvmem-core-fix-device-node-refcounting.patch48
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0001-nvmem-core-remove-spurious-white-space.patch26
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0002-nvmem-core-add-an-index-parameter-to-the-cell.patch180
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0003-nvmem-core-move-struct-nvmem_cell_info-to-nvmem-prov.patch78
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0004-nvmem-core-drop-the-removal-of-the-cells-in-nvmem_ad.patch65
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0005-nvmem-core-add-nvmem_add_one_cell.patch122
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0006-nvmem-core-use-nvmem_add_one_cell-in-nvmem_add_cells.patch93
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0007-nvmem-stm32-add-OP-TEE-support-for-STM32MP13x.patch562
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0008-nvmem-stm32-detect-bsec-pta-presence-for-STM32MP15x.patch85
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0009-nvmem-rave-sp-eeprm-fix-kernel-doc-bad-line-warning.patch32
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0010-nvmem-qcom-spmi-sdam-register-at-device-init-time.patch43
-rw-r--r--target/linux/generic/backport-6.1/809-v6.3-0011-nvmem-stm32-fix-OPTEE-dependency.patch46
-rw-r--r--target/linux/generic/backport-6.1/810-v5.17-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch59
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0001-nvmem-xilinx-zynqmp-make-modular.patch35
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch387
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0003-nvmem-core-handle-the-absence-of-expected-layouts.patch61
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0004-nvmem-core-request-layout-modules-loading.patch52
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0005-nvmem-core-add-per-cell-post-processing.patch86
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0006-nvmem-core-allow-to-modify-a-cell-before-adding-it.patch59
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0007-nvmem-imx-ocotp-replace-global-post-processing-with-.patch81
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0008-nvmem-cell-drop-global-cell_post_process.patch68
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0009-nvmem-core-provide-own-priv-pointer-in-post-process-.patch76
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0010-nvmem-layouts-sl28vpd-Add-new-layout-driver.patch215
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0011-nvmem-layouts-onie-tlv-Add-new-layout-driver.patch306
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0012-nvmem-stm32-romem-mark-OF-related-data-as-maybe-unus.patch32
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0013-nvmem-mtk-efuse-Support-postprocessing-for-GPU-speed.patch120
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0014-nvmem-bcm-ocotp-Use-devm_platform_ioremap_resource.patch39
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0015-nvmem-nintendo-otp-Use-devm_platform_ioremap_resourc.patch39
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0016-nvmem-vf610-ocotp-Use-devm_platform_get_and_ioremap_.patch32
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0017-nvmem-core-support-specifying-both-cell-raw-data-pos.patch115
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0018-nvmem-u-boot-env-post-process-ethaddr-env-variable.patch81
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0019-nvmem-Add-macro-to-register-nvmem-layout-drivers.patch42
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0020-nvmem-layouts-sl28vpd-Use-module_nvmem_layout_driver.patch39
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0021-nvmem-layouts-onie-tlv-Use-module_nvmem_layout_drive.patch39
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0022-nvmem-layouts-onie-tlv-Drop-wrong-module-alias.patch24
-rw-r--r--target/linux/generic/backport-6.1/811-v6.4-0023-nvmem-layouts-sl28vpd-set-varaiable-sl28vpd_layout-s.patch31
-rw-r--r--target/linux/generic/backport-6.1/812-v6.2-firmware-nvram-bcm47xx-support-init-from-IO-memory.patch100
-rw-r--r--target/linux/generic/backport-6.1/813-v6.5-nvmem-brcm_nvram-add-.read_post_process-for-MACs.patch77
-rw-r--r--target/linux/generic/backport-6.1/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch51
-rw-r--r--target/linux/generic/backport-6.1/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch29
-rw-r--r--target/linux/generic/backport-6.1/823-v5.18-Bluetooth-btusb-Add-a-new-PID-VID-13d3-3567-for-MT79.patch70
-rw-r--r--target/linux/generic/backport-6.1/824-v5.19-Bluetooth-btusb-Add-a-new-PID-VID-0489-e0c8-for-MT79.patch68
-rw-r--r--target/linux/generic/backport-6.1/825-v6.1-Bluetooth-btusb-Add-a-new-VID-PID-0e8d-0608-for-MT79.patch66
-rw-r--r--target/linux/generic/backport-6.1/826-v5.17-of-base-make-small-of_parse_phandle-variants-static-.patch359
-rw-r--r--target/linux/generic/backport-6.1/827-v6.3-0001-of-base-add-of_parse_phandle_with_optional_args.patch58
-rw-r--r--target/linux/generic/backport-6.1/827-v6.3-0002-of-property-make-.-cells-optional-for-simple-props.patch34
-rw-r--r--target/linux/generic/backport-6.1/827-v6.3-0003-of-property-add-nvmem-cell-cells-property.patch30
-rw-r--r--target/linux/generic/backport-6.1/827-v6.3-0004-of-device-Ignore-modalias-of-reused-nodes.patch37
-rw-r--r--target/linux/generic/backport-6.1/827-v6.3-0005-of-device-Do-not-ignore-error-code-in-of_device_ueve.patch29
-rw-r--r--target/linux/generic/backport-6.1/828-v6.4-0002-of-Update-of_device_get_modalias.patch103
-rw-r--r--target/linux/generic/backport-6.1/828-v6.4-0003-of-Rename-of_modalias_node.patch173
-rw-r--r--target/linux/generic/backport-6.1/828-v6.4-0004-of-Move-of_modalias-to-module.c.patch160
-rw-r--r--target/linux/generic/backport-6.1/828-v6.4-0005-of-Move-the-request-module-helper-logic-to-module.c.patch131
-rw-r--r--target/linux/generic/backport-6.1/830-v6.2-ata-ahci-fix-enum-constants-for-gcc-13.patch348
-rw-r--r--target/linux/generic/backport-6.1/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch49
-rw-r--r--target/linux/generic/backport-6.1/870-v5.18-hwmon-lm70-Add-ti-tmp125-support.patch71
-rw-r--r--target/linux/generic/backport-6.1/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch58
-rw-r--r--target/linux/generic/backport-6.1/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch118
-rw-r--r--target/linux/generic/backport-6.1/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch63
-rw-r--r--target/linux/generic/backport-6.1/890-v6.1-mtd-spinand-winbond-fix-flash-detection.patch40
-rw-r--r--target/linux/generic/backport-6.1/891-v6.1-mtd-spinand-winbond-add-W25N02KV.patch106
-rw-r--r--target/linux/generic/config-6.17591
-rw-r--r--target/linux/generic/hack-6.1/204-module_strip.patch212
-rw-r--r--target/linux/generic/hack-6.1/205-kconfig-abort-configuration-on-unset-symbol.patch41
-rw-r--r--target/linux/generic/hack-6.1/210-darwin_scripts_include.patch3053
-rw-r--r--target/linux/generic/hack-6.1/211-darwin-uuid-typedef-clash.patch22
-rw-r--r--target/linux/generic/hack-6.1/212-tools_portability.patch110
-rw-r--r--target/linux/generic/hack-6.1/214-spidev_h_portability.patch24
-rw-r--r--target/linux/generic/hack-6.1/220-arm-gc_sections.patch123
-rw-r--r--target/linux/generic/hack-6.1/221-module_exports.patch126
-rw-r--r--target/linux/generic/hack-6.1/230-openwrt_lzma_options.patch34
-rw-r--r--target/linux/generic/hack-6.1/250-netfilter_depends.patch27
-rw-r--r--target/linux/generic/hack-6.1/251-kconfig.patch210
-rw-r--r--target/linux/generic/hack-6.1/253-ksmbd-config.patch32
-rw-r--r--target/linux/generic/hack-6.1/259-regmap_dynamic.patch144
-rw-r--r--target/linux/generic/hack-6.1/260-crypto_test_dependencies.patch52
-rw-r--r--target/linux/generic/hack-6.1/261-lib-arc4-unhide.patch24
-rw-r--r--target/linux/generic/hack-6.1/280-rfkill-stubs.patch84
-rw-r--r--target/linux/generic/hack-6.1/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch64
-rw-r--r--target/linux/generic/hack-6.1/321-powerpc_crtsavres_prereq.patch38
-rw-r--r--target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch98
-rw-r--r--target/linux/generic/hack-6.1/410-block-fit-partition-parser.patch214
-rw-r--r--target/linux/generic/hack-6.1/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch24
-rw-r--r--target/linux/generic/hack-6.1/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch120
-rw-r--r--target/linux/generic/hack-6.1/430-mtk-bmt-support.patch33
-rw-r--r--target/linux/generic/hack-6.1/600-bridge_offload.patch846
-rw-r--r--target/linux/generic/hack-6.1/601-of_net-add-mac-address-ascii-support.patch116
-rw-r--r--target/linux/generic/hack-6.1/645-netfilter-connmark-introduce-set-dscpmark.patch214
-rw-r--r--target/linux/generic/hack-6.1/650-netfilter-add-xt_FLOWOFFLOAD-target.patch861
-rw-r--r--target/linux/generic/hack-6.1/651-wireless_mesh_header.patch24
-rw-r--r--target/linux/generic/hack-6.1/660-fq_codel_defaults.patch27
-rw-r--r--target/linux/generic/hack-6.1/661-kernel-ct-size-the-hashtable-more-adequately.patch25
-rw-r--r--target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch131
-rw-r--r--target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch21
-rw-r--r--target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch142
-rw-r--r--target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch178
-rw-r--r--target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch154
-rw-r--r--target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch34
-rw-r--r--target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch43
-rw-r--r--target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch63
-rw-r--r--target/linux/generic/hack-6.1/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch53
-rw-r--r--target/linux/generic/hack-6.1/760-net-usb-r8152-add-LED-configuration-from-OF.patch74
-rw-r--r--target/linux/generic/hack-6.1/761-dt-bindings-net-add-RTL8152-binding-documentation.patch54
-rw-r--r--target/linux/generic/hack-6.1/765-mxl-gpy-control-LED-reg-from-DT.patch101
-rw-r--r--target/linux/generic/hack-6.1/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch72
-rw-r--r--target/linux/generic/hack-6.1/773-bgmac-add-srab-switch.patch98
-rw-r--r--target/linux/generic/hack-6.1/780-usb-net-MeigLink_modem_support.patch43
-rw-r--r--target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch65
-rw-r--r--target/linux/generic/hack-6.1/795-backport-phylink_pcs-helpers.patch167
-rw-r--r--target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch162
-rw-r--r--target/linux/generic/hack-6.1/810-bcma-ssb-fallback-sprom.patch187
-rw-r--r--target/linux/generic/hack-6.1/901-debloat_sock_diag.patch162
-rw-r--r--target/linux/generic/hack-6.1/902-debloat_proc.patch408
-rw-r--r--target/linux/generic/hack-6.1/904-debloat_dma_buf.patch92
-rw-r--r--target/linux/generic/hack-6.1/910-kobject_uevent.patch32
-rw-r--r--target/linux/generic/hack-6.1/911-kobject_add_broadcast_uevent.patch76
-rw-r--r--target/linux/generic/hack-6.1/920-device_tree_cmdline.patch21
-rw-r--r--target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch30
-rw-r--r--target/linux/generic/pending-6.1/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch29
-rw-r--r--target/linux/generic/pending-6.1/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch57
-rw-r--r--target/linux/generic/pending-6.1/103-kbuild-export-SUBARCH.patch21
-rw-r--r--target/linux/generic/pending-6.1/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch32
-rw-r--r--target/linux/generic/pending-6.1/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch32
-rw-r--r--target/linux/generic/pending-6.1/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch75
-rw-r--r--target/linux/generic/pending-6.1/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch82
-rw-r--r--target/linux/generic/pending-6.1/130-binfmt_elf-dynamically-allocate-note.data-in-parse_e.patch94
-rw-r--r--target/linux/generic/pending-6.1/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch81
-rw-r--r--target/linux/generic/pending-6.1/141-jffs2-add-RENAME_EXCHANGE-support.patch73
-rw-r--r--target/linux/generic/pending-6.1/142-jffs2-add-splice-ops.patch20
-rw-r--r--target/linux/generic/pending-6.1/143-jffs2-reduce-stack-usage-in-jffs2_build_xattr_subsys.patch121
-rw-r--r--target/linux/generic/pending-6.1/150-bridge_allow_receiption_on_disabled_port.patch45
-rw-r--r--target/linux/generic/pending-6.1/190-rtc-rs5c372-support_alarms_up_to_1_week.patch94
-rw-r--r--target/linux/generic/pending-6.1/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch70
-rw-r--r--target/linux/generic/pending-6.1/203-kallsyms_uncompressed.patch119
-rw-r--r--target/linux/generic/pending-6.1/205-backtrace_module_info.patch41
-rw-r--r--target/linux/generic/pending-6.1/240-remove-unsane-filenames-from-deps_initramfs-list.patch30
-rw-r--r--target/linux/generic/pending-6.1/261-enable_wilink_platform_without_drivers.patch20
-rw-r--r--target/linux/generic/pending-6.1/270-platform-mikrotik-build-bits.patch31
-rw-r--r--target/linux/generic/pending-6.1/300-mips_expose_boot_raw.patch40
-rw-r--r--target/linux/generic/pending-6.1/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch71
-rw-r--r--target/linux/generic/pending-6.1/302-mips_no_branch_likely.patch22
-rw-r--r--target/linux/generic/pending-6.1/305-mips_module_reloc.patch370
-rw-r--r--target/linux/generic/pending-6.1/307-mips_highmem_offset.patch19
-rw-r--r--target/linux/generic/pending-6.1/308-mips32r2_tune.patch22
-rw-r--r--target/linux/generic/pending-6.1/310-arm_module_unresolved_weak_sym.patch22
-rw-r--r--target/linux/generic/pending-6.1/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch282
-rw-r--r--target/linux/generic/pending-6.1/332-arc-add-OWRTDTB-section.patch84
-rw-r--r--target/linux/generic/pending-6.1/333-arc-enable-unaligned-access-in-kernel-mode.patch24
-rw-r--r--target/linux/generic/pending-6.1/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch25
-rw-r--r--target/linux/generic/pending-6.1/351-irqchip-bcm-6345-l1-request-memory-region.patch113
-rw-r--r--target/linux/generic/pending-6.1/400-mtd-mtdsplit-support.patch328
-rw-r--r--target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch245
-rw-r--r--target/linux/generic/pending-6.1/420-mtd-redboot_space.patch41
-rw-r--r--target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch229
-rw-r--r--target/linux/generic/pending-6.1/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch68
-rw-r--r--target/linux/generic/pending-6.1/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch37
-rw-r--r--target/linux/generic/pending-6.1/435-mtd-add-routerbootpart-parser-config.patch38
-rw-r--r--target/linux/generic/pending-6.1/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch25
-rw-r--r--target/linux/generic/pending-6.1/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch17
-rw-r--r--target/linux/generic/pending-6.1/465-m25p80-mx-disable-software-protection.patch18
-rw-r--r--target/linux/generic/pending-6.1/476-mtd-spi-nor-add-eon-en25q128.patch18
-rw-r--r--target/linux/generic/pending-6.1/477-mtd-spi-nor-add-eon-en25qx128a.patch21
-rw-r--r--target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch79
-rw-r--r--target/linux/generic/pending-6.1/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch22
-rw-r--r--target/linux/generic/pending-6.1/482-mtd-spi-nor-add-gd25q512.patch21
-rw-r--r--target/linux/generic/pending-6.1/484-mtd-spi-nor-add-esmt-f25l16pa.patch23
-rw-r--r--target/linux/generic/pending-6.1/485-mtd-spi-nor-add-xmc-xm25qh128c.patch24
-rw-r--r--target/linux/generic/pending-6.1/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch143
-rw-r--r--target/linux/generic/pending-6.1/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch168
-rw-r--r--target/linux/generic/pending-6.1/488-mtd-spi-nor-add-xmc-xm25qh64c.patch22
-rw-r--r--target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch97
-rw-r--r--target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch69
-rw-r--r--target/linux/generic/pending-6.1/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch53
-rw-r--r--target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch34
-rw-r--r--target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch60
-rw-r--r--target/linux/generic/pending-6.1/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch52
-rw-r--r--target/linux/generic/pending-6.1/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch216
-rw-r--r--target/linux/generic/pending-6.1/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch34
-rw-r--r--target/linux/generic/pending-6.1/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch30
-rw-r--r--target/linux/generic/pending-6.1/500-fs_cdrom_dependencies.patch52
-rw-r--r--target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch4581
-rw-r--r--target/linux/generic/pending-6.1/532-jffs2_eofdetect.patch65
-rw-r--r--target/linux/generic/pending-6.1/600-netfilter_conntrack_flush.patch88
-rw-r--r--target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch110
-rw-r--r--target/linux/generic/pending-6.1/611-netfilter_match_bypass_default_table.patch106
-rw-r--r--target/linux/generic/pending-6.1/612-netfilter_match_reduce_memory_access.patch22
-rw-r--r--target/linux/generic/pending-6.1/620-net_sched-codel-do-not-defer-queue-length-update.patch86
-rw-r--r--target/linux/generic/pending-6.1/630-packet_socket_type.patch138
-rw-r--r--target/linux/generic/pending-6.1/655-increase_skb_pad.patch20
-rw-r--r--target/linux/generic/pending-6.1/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch511
-rw-r--r--target/linux/generic/pending-6.1/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch263
-rw-r--r--target/linux/generic/pending-6.1/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch50
-rw-r--r--target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch149
-rw-r--r--target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch89
-rw-r--r--target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch55
-rw-r--r--target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch31
-rw-r--r--target/linux/generic/pending-6.1/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch106
-rw-r--r--target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch41
-rw-r--r--target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch38
-rw-r--r--target/linux/generic/pending-6.1/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch28
-rw-r--r--target/linux/generic/pending-6.1/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch174
-rw-r--r--target/linux/generic/pending-6.1/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch106
-rw-r--r--target/linux/generic/pending-6.1/722-net-phy-realtek-support-switching-between-SGMII-and-.patch61
-rw-r--r--target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch28
-rw-r--r--target/linux/generic/pending-6.1/724-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch65
-rw-r--r--target/linux/generic/pending-6.1/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch43
-rw-r--r--target/linux/generic/pending-6.1/726-net-phy-realtek-make-sure-paged-read-is-protected-by.patch35
-rw-r--r--target/linux/generic/pending-6.1/727-net-phy-realtek-use-inline-functions-for-10GbE-adver.patch60
-rw-r--r--target/linux/generic/pending-6.1/728-net-phy-realtek-check-validity-of-10GbE-link-partner.patch28
-rw-r--r--target/linux/generic/pending-6.1/729-net-phy-realtek-introduce-rtl822x_probe.patch100
-rw-r--r--target/linux/generic/pending-6.1/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch63
-rw-r--r--target/linux/generic/pending-6.1/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch428
-rw-r--r--target/linux/generic/pending-6.1/732-00-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch201
-rw-r--r--target/linux/generic/pending-6.1/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch94
-rw-r--r--target/linux/generic/pending-6.1/732-02-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch21
-rw-r--r--target/linux/generic/pending-6.1/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch42
-rw-r--r--target/linux/generic/pending-6.1/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch33
-rw-r--r--target/linux/generic/pending-6.1/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch266
-rw-r--r--target/linux/generic/pending-6.1/736-02-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch37
-rw-r--r--target/linux/generic/pending-6.1/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch331
-rw-r--r--target/linux/generic/pending-6.1/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch333
-rw-r--r--target/linux/generic/pending-6.1/736-05-net-ethernet-mtk_eth_soc-add-missing-ppe-cache-flush.patch21
-rw-r--r--target/linux/generic/pending-6.1/736-06-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch51
-rw-r--r--target/linux/generic/pending-6.1/750-skb-Do-mix-page-pool-and-page-referenced-frags-in-GR.patch35
-rw-r--r--target/linux/generic/pending-6.1/760-net-core-add-optional-threading-for-backlog-processi.patch232
-rw-r--r--target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch27
-rw-r--r--target/linux/generic/pending-6.1/772-net-dsa-b53-add-support-for-BCM63xx-RGMIIs.patch174
-rw-r--r--target/linux/generic/pending-6.1/773-net-dsa-b53-mmap-add-more-63xx-SoCs.patch108
-rw-r--r--target/linux/generic/pending-6.1/774-net-dsa-b53-mmap-allow-passing-a-chip-ID.patch195
-rw-r--r--target/linux/generic/pending-6.1/775-net-dsa-b53-add-BCM63268-RGMII-configuration.patch123
-rw-r--r--target/linux/generic/pending-6.1/777-net-dsa-b53-mdio-add-support-for-BCM53134.patch189
-rw-r--r--target/linux/generic/pending-6.1/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch61
-rw-r--r--target/linux/generic/pending-6.1/790-bus-mhi-core-add-SBL-state-callback.patch48
-rw-r--r--target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch43
-rw-r--r--target/linux/generic/pending-6.1/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch73
-rw-r--r--target/linux/generic/pending-6.1/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch222
-rw-r--r--target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch47
-rw-r--r--target/linux/generic/pending-6.1/810-pci_disable_common_quirks.patch62
-rw-r--r--target/linux/generic/pending-6.1/811-pci_disable_usb_common_quirks.patch115
-rw-r--r--target/linux/generic/pending-6.1/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch26
-rw-r--r--target/linux/generic/pending-6.1/834-ledtrig-libata.patch149
-rw-r--r--target/linux/generic/pending-6.1/840-hwrng-bcm2835-set-quality-to-1000.patch26
-rw-r--r--target/linux/generic/pending-6.1/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch102
-rw-r--r--target/linux/generic/pending-6.1/850-dt-bindings-clk-add-BCM63268-timer-clock-definitions.patch114
-rw-r--r--target/linux/generic/pending-6.1/851-dt-bindings-reset-add-BCM63268-timer-reset-definitions.patch107
-rw-r--r--target/linux/generic/pending-6.1/852-clk-bcm-Add-BCM63268-timer-clock-and-reset-driver.patch345
-rw-r--r--target/linux/generic/pending-6.1/920-mangle_bootargs.patch71
713 files changed, 130426 insertions, 0 deletions
diff --git a/target/linux/generic/backport-6.1/005-v5.17-01-Kbuild-use-Wdeclaration-after-statement.patch b/target/linux/generic/backport-6.1/005-v5.17-01-Kbuild-use-Wdeclaration-after-statement.patch
new file mode 100644
index 0000000000..b481dd3061
--- /dev/null
+++ b/target/linux/generic/backport-6.1/005-v5.17-01-Kbuild-use-Wdeclaration-after-statement.patch
@@ -0,0 +1,73 @@
+From 2fd7e7f9317d3048a14026816d081b08ba98ea8e Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Tue, 8 Mar 2022 22:56:13 +0100
+Subject: [PATCH 1/3] Kbuild: use -Wdeclaration-after-statement
+
+The kernel is moving from using `-std=gnu89` to `-std=gnu11`, permitting
+the use of additional C11 features such as for-loop initial declarations.
+
+One contentious aspect of C99 is that it permits mixed declarations and
+code, and for now at least, it seems preferable to enforce that
+declarations must come first.
+
+These warnings were already enabled in the kernel itself, but not
+for KBUILD_USERCFLAGS or the compat VDSO on arch/arm64, which uses
+a separate set of CFLAGS.
+
+This patch fixes an existing violation in modpost.c, which is not
+reported because of the missing flag in KBUILD_USERCFLAGS:
+
+| scripts/mod/modpost.c: In function ‘match’:
+| scripts/mod/modpost.c:837:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
+| 837 | const char *endp = p + strlen(p) - 1;
+| | ^~~~~
+
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+[arnd: don't add a duplicate flag to the default set, update changelog]
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Reviewed-by: Nathan Chancellor <nathan@kernel.org>
+Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
+Tested-by: Sedat Dilek <sedat.dilek@gmail.com> # LLVM/Clang v13.0.0 (x86-64)
+Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
+---
+ Makefile | 3 ++-
+ arch/arm64/kernel/vdso32/Makefile | 1 +
+ scripts/mod/modpost.c | 4 +++-
+ 3 files changed, 6 insertions(+), 2 deletions(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -440,7 +440,8 @@ endif
+ HOSTPKG_CONFIG = pkg-config
+
+ export KBUILD_USERCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes \
+- -O2 -fomit-frame-pointer -std=gnu89
++ -O2 -fomit-frame-pointer -std=gnu89 \
++ -Wdeclaration-after-statement
+ export KBUILD_USERLDFLAGS :=
+
+ KBUILD_HOSTCFLAGS := $(KBUILD_USERCFLAGS) $(HOST_LFS_CFLAGS) $(HOSTCFLAGS)
+--- a/arch/arm64/kernel/vdso32/Makefile
++++ b/arch/arm64/kernel/vdso32/Makefile
+@@ -76,6 +76,7 @@ VDSO_CFLAGS += -Wall -Wundef -Wstrict-pr
+ -fno-strict-aliasing -fno-common \
+ -Werror-implicit-function-declaration \
+ -Wno-format-security \
++ -Wdeclaration-after-statement \
+ -std=gnu89
+ VDSO_CFLAGS += -O2
+ # Some useful compiler-dependent flags from top-level Makefile
+--- a/scripts/mod/modpost.c
++++ b/scripts/mod/modpost.c
+@@ -833,8 +833,10 @@ static int match(const char *sym, const
+ {
+ const char *p;
+ while (*pat) {
++ const char *endp;
++
+ p = *pat++;
+- const char *endp = p + strlen(p) - 1;
++ endp = p + strlen(p) - 1;
+
+ /* "*foo*" */
+ if (*p == '*' && *endp == '*') {
diff --git a/target/linux/generic/backport-6.1/005-v5.17-02-Kbuild-move-to-std-gnu11.patch b/target/linux/generic/backport-6.1/005-v5.17-02-Kbuild-move-to-std-gnu11.patch
new file mode 100644
index 0000000000..94fc52fd8e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/005-v5.17-02-Kbuild-move-to-std-gnu11.patch
@@ -0,0 +1,60 @@
+From b810c8e719ea082e47c7a8f7cf878bc84fa2455d Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 8 Mar 2022 22:56:14 +0100
+Subject: [PATCH 2/3] Kbuild: move to -std=gnu11
+
+During a patch discussion, Linus brought up the option of changing
+the C standard version from gnu89 to gnu99, which allows using variable
+declaration inside of a for() loop. While the C99, C11 and later standards
+introduce many other features, most of these are already available in
+gnu89 as GNU extensions as well.
+
+An earlier attempt to do this when gcc-5 started defaulting to
+-std=gnu11 failed because at the time that caused warnings about
+designated initializers with older compilers. Now that gcc-5.1 is
+the minimum compiler version used for building kernels, that is no
+longer a concern. Similarly, the behavior of 'inline' functions changes
+between gnu89 using gnu_inline behavior and gnu11 using standard c99+
+behavior, but this was taken care of by defining 'inline' to include
+__attribute__((gnu_inline)) in order to allow building with clang a
+while ago.
+
+Nathan Chancellor reported a new -Wdeclaration-after-statement
+warning that appears in a system header on arm, this still needs a
+workaround.
+
+The differences between gnu99, gnu11, gnu1x and gnu17 are fairly
+minimal and mainly impact warnings at the -Wpedantic level that the
+kernel never enables. Between these, gnu11 is the newest version
+that is supported by all supported compiler versions, though it is
+only the default on gcc-5, while all other supported versions of
+gcc or clang default to gnu1x/gnu17.
+
+Link: https://lore.kernel.org/lkml/CAHk-=wiyCH7xeHcmiFJ-YgXUy2Jaj7pnkdKpcovt8fYbVFW3TA@mail.gmail.com/
+Link: https://github.com/ClangBuiltLinux/linux/issues/1603
+Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
+Acked-by: Marco Elver <elver@google.com>
+Acked-by: Jani Nikula <jani.nikula@intel.com>
+Acked-by: David Sterba <dsterba@suse.com>
+Tested-by: Sedat Dilek <sedat.dilek@gmail.com>
+Reviewed-by: Alex Shi <alexs@kernel.org>
+Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
+Reviewed-by: Miguel Ojeda <ojeda@kernel.org>
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Reviewed-by: Nathan Chancellor <nathan@kernel.org>
+Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
+---
+ Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -524,7 +524,7 @@ KBUILD_CFLAGS := -Wall -Wundef -Werror
+ -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE \
+ -Werror=implicit-function-declaration -Werror=implicit-int \
+ -Werror=return-type -Wno-format-security \
+- -std=gnu89
++ -std=gnu11
+ KBUILD_CPPFLAGS := -D__KERNEL__
+ KBUILD_AFLAGS_KERNEL :=
+ KBUILD_CFLAGS_KERNEL :=
diff --git a/target/linux/generic/backport-6.1/005-v5.17-03-Kbuild-use-std-gnu11-for-KBUILD_USERCFLAGS.patch b/target/linux/generic/backport-6.1/005-v5.17-03-Kbuild-use-std-gnu11-for-KBUILD_USERCFLAGS.patch
new file mode 100644
index 0000000000..e34acbba17
--- /dev/null
+++ b/target/linux/generic/backport-6.1/005-v5.17-03-Kbuild-use-std-gnu11-for-KBUILD_USERCFLAGS.patch
@@ -0,0 +1,43 @@
+From 40337d6f3d677aee7ad3052ae662d3f53dd4d5cb Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Tue, 8 Mar 2022 22:56:15 +0100
+Subject: [PATCH 3/3] Kbuild: use -std=gnu11 for KBUILD_USERCFLAGS
+
+As we change the C language standard for the kernel from gnu89 to
+gnu11, it makes sense to also update the version for user space
+compilation.
+
+Some users have older native compilers than what they use for
+kernel builds, so I considered using gnu99 as the default version
+for wider compatibility with gcc-4.6 and earlier.
+
+However, testing with older compilers showed that we already require
+HOSTCC version 5.1 as well because a lot of host tools include
+linux/compiler.h that uses __has_attribute():
+
+ CC tools/objtool/exec-cmd.o
+In file included from tools/include/linux/compiler_types.h:36:0,
+ from tools/include/linux/compiler.h:5,
+ from exec-cmd.c:2:
+tools/include/linux/compiler-gcc.h:19:5: error: "__has_attribute" is not defined [-Werror=undef]
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Reviewed-by: Nathan Chancellor <nathan@kernel.org>
+Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
+Tested-by: Sedat Dilek <sedat.dilek@gmail.com>
+Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
+---
+ Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -440,7 +440,7 @@ endif
+ HOSTPKG_CONFIG = pkg-config
+
+ export KBUILD_USERCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes \
+- -O2 -fomit-frame-pointer -std=gnu89 \
++ -O2 -fomit-frame-pointer -std=gnu11 \
+ -Wdeclaration-after-statement
+ export KBUILD_USERLDFLAGS :=
+
diff --git a/target/linux/generic/backport-6.1/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch b/target/linux/generic/backport-6.1/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch
new file mode 100644
index 0000000000..df854ffd3d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch
@@ -0,0 +1,425 @@
+From a4103262b01a1b8704b37c01c7c813df91b7b119 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 01:59:58 -0600
+Subject: [PATCH 01/29] mm: x86, arm64: add arch_has_hw_pte_young()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Patch series "Multi-Gen LRU Framework", v14.
+
+What's new
+==========
+1. OpenWrt, in addition to Android, Arch Linux Zen, Armbian, ChromeOS,
+ Liquorix, post-factum and XanMod, is now shipping MGLRU on 5.15.
+2. Fixed long-tailed direct reclaim latency seen on high-memory (TBs)
+ machines. The old direct reclaim backoff, which tries to enforce a
+ minimum fairness among all eligible memcgs, over-swapped by about
+ (total_mem>>DEF_PRIORITY)-nr_to_reclaim. The new backoff, which
+ pulls the plug on swapping once the target is met, trades some
+ fairness for curtailed latency:
+ https://lore.kernel.org/r/20220918080010.2920238-10-yuzhao@google.com/
+3. Fixed minior build warnings and conflicts. More comments and nits.
+
+TLDR
+====
+The current page reclaim is too expensive in terms of CPU usage and it
+often makes poor choices about what to evict. This patchset offers an
+alternative solution that is performant, versatile and
+straightforward.
+
+Patchset overview
+=================
+The design and implementation overview is in patch 14:
+https://lore.kernel.org/r/20220918080010.2920238-15-yuzhao@google.com/
+
+01. mm: x86, arm64: add arch_has_hw_pte_young()
+02. mm: x86: add CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG
+Take advantage of hardware features when trying to clear the accessed
+bit in many PTEs.
+
+03. mm/vmscan.c: refactor shrink_node()
+04. Revert "include/linux/mm_inline.h: fold __update_lru_size() into
+ its sole caller"
+Minor refactors to improve readability for the following patches.
+
+05. mm: multi-gen LRU: groundwork
+Adds the basic data structure and the functions that insert pages to
+and remove pages from the multi-gen LRU (MGLRU) lists.
+
+06. mm: multi-gen LRU: minimal implementation
+A minimal implementation without optimizations.
+
+07. mm: multi-gen LRU: exploit locality in rmap
+Exploits spatial locality to improve efficiency when using the rmap.
+
+08. mm: multi-gen LRU: support page table walks
+Further exploits spatial locality by optionally scanning page tables.
+
+09. mm: multi-gen LRU: optimize multiple memcgs
+Optimizes the overall performance for multiple memcgs running mixed
+types of workloads.
+
+10. mm: multi-gen LRU: kill switch
+Adds a kill switch to enable or disable MGLRU at runtime.
+
+11. mm: multi-gen LRU: thrashing prevention
+12. mm: multi-gen LRU: debugfs interface
+Provide userspace with features like thrashing prevention, working set
+estimation and proactive reclaim.
+
+13. mm: multi-gen LRU: admin guide
+14. mm: multi-gen LRU: design doc
+Add an admin guide and a design doc.
+
+Benchmark results
+=================
+Independent lab results
+-----------------------
+Based on the popularity of searches [01] and the memory usage in
+Google's public cloud, the most popular open-source memory-hungry
+applications, in alphabetical order, are:
+ Apache Cassandra Memcached
+ Apache Hadoop MongoDB
+ Apache Spark PostgreSQL
+ MariaDB (MySQL) Redis
+
+An independent lab evaluated MGLRU with the most widely used benchmark
+suites for the above applications. They posted 960 data points along
+with kernel metrics and perf profiles collected over more than 500
+hours of total benchmark time. Their final reports show that, with 95%
+confidence intervals (CIs), the above applications all performed
+significantly better for at least part of their benchmark matrices.
+
+On 5.14:
+1. Apache Spark [02] took 95% CIs [9.28, 11.19]% and [12.20, 14.93]%
+ less wall time to sort three billion random integers, respectively,
+ under the medium- and the high-concurrency conditions, when
+ overcommitting memory. There were no statistically significant
+ changes in wall time for the rest of the benchmark matrix.
+2. MariaDB [03] achieved 95% CIs [5.24, 10.71]% and [20.22, 25.97]%
+ more transactions per minute (TPM), respectively, under the medium-
+ and the high-concurrency conditions, when overcommitting memory.
+ There were no statistically significant changes in TPM for the rest
+ of the benchmark matrix.
+3. Memcached [04] achieved 95% CIs [23.54, 32.25]%, [20.76, 41.61]%
+ and [21.59, 30.02]% more operations per second (OPS), respectively,
+ for sequential access, random access and Gaussian (distribution)
+ access, when THP=always; 95% CIs [13.85, 15.97]% and
+ [23.94, 29.92]% more OPS, respectively, for random access and
+ Gaussian access, when THP=never. There were no statistically
+ significant changes in OPS for the rest of the benchmark matrix.
+4. MongoDB [05] achieved 95% CIs [2.23, 3.44]%, [6.97, 9.73]% and
+ [2.16, 3.55]% more operations per second (OPS), respectively, for
+ exponential (distribution) access, random access and Zipfian
+ (distribution) access, when underutilizing memory; 95% CIs
+ [8.83, 10.03]%, [21.12, 23.14]% and [5.53, 6.46]% more OPS,
+ respectively, for exponential access, random access and Zipfian
+ access, when overcommitting memory.
+
+On 5.15:
+5. Apache Cassandra [06] achieved 95% CIs [1.06, 4.10]%, [1.94, 5.43]%
+ and [4.11, 7.50]% more operations per second (OPS), respectively,
+ for exponential (distribution) access, random access and Zipfian
+ (distribution) access, when swap was off; 95% CIs [0.50, 2.60]%,
+ [6.51, 8.77]% and [3.29, 6.75]% more OPS, respectively, for
+ exponential access, random access and Zipfian access, when swap was
+ on.
+6. Apache Hadoop [07] took 95% CIs [5.31, 9.69]% and [2.02, 7.86]%
+ less average wall time to finish twelve parallel TeraSort jobs,
+ respectively, under the medium- and the high-concurrency
+ conditions, when swap was on. There were no statistically
+ significant changes in average wall time for the rest of the
+ benchmark matrix.
+7. PostgreSQL [08] achieved 95% CI [1.75, 6.42]% more transactions per
+ minute (TPM) under the high-concurrency condition, when swap was
+ off; 95% CIs [12.82, 18.69]% and [22.70, 46.86]% more TPM,
+ respectively, under the medium- and the high-concurrency
+ conditions, when swap was on. There were no statistically
+ significant changes in TPM for the rest of the benchmark matrix.
+8. Redis [09] achieved 95% CIs [0.58, 5.94]%, [6.55, 14.58]% and
+ [11.47, 19.36]% more total operations per second (OPS),
+ respectively, for sequential access, random access and Gaussian
+ (distribution) access, when THP=always; 95% CIs [1.27, 3.54]%,
+ [10.11, 14.81]% and [8.75, 13.64]% more total OPS, respectively,
+ for sequential access, random access and Gaussian access, when
+ THP=never.
+
+Our lab results
+---------------
+To supplement the above results, we ran the following benchmark suites
+on 5.16-rc7 and found no regressions [10].
+ fs_fio_bench_hdd_mq pft
+ fs_lmbench pgsql-hammerdb
+ fs_parallelio redis
+ fs_postmark stream
+ hackbench sysbenchthread
+ kernbench tpcc_spark
+ memcached unixbench
+ multichase vm-scalability
+ mutilate will-it-scale
+ nginx
+
+[01] https://trends.google.com
+[02] https://lore.kernel.org/r/20211102002002.92051-1-bot@edi.works/
+[03] https://lore.kernel.org/r/20211009054315.47073-1-bot@edi.works/
+[04] https://lore.kernel.org/r/20211021194103.65648-1-bot@edi.works/
+[05] https://lore.kernel.org/r/20211109021346.50266-1-bot@edi.works/
+[06] https://lore.kernel.org/r/20211202062806.80365-1-bot@edi.works/
+[07] https://lore.kernel.org/r/20211209072416.33606-1-bot@edi.works/
+[08] https://lore.kernel.org/r/20211218071041.24077-1-bot@edi.works/
+[09] https://lore.kernel.org/r/20211122053248.57311-1-bot@edi.works/
+[10] https://lore.kernel.org/r/20220104202247.2903702-1-yuzhao@google.com/
+
+Read-world applications
+=======================
+Third-party testimonials
+------------------------
+Konstantin reported [11]:
+ I have Archlinux with 8G RAM + zswap + swap. While developing, I
+ have lots of apps opened such as multiple LSP-servers for different
+ langs, chats, two browsers, etc... Usually, my system gets quickly
+ to a point of SWAP-storms, where I have to kill LSP-servers,
+ restart browsers to free memory, etc, otherwise the system lags
+ heavily and is barely usable.
+
+ 1.5 day ago I migrated from 5.11.15 kernel to 5.12 + the LRU
+ patchset, and I started up by opening lots of apps to create memory
+ pressure, and worked for a day like this. Till now I had not a
+ single SWAP-storm, and mind you I got 3.4G in SWAP. I was never
+ getting to the point of 3G in SWAP before without a single
+ SWAP-storm.
+
+Vaibhav from IBM reported [12]:
+ In a synthetic MongoDB Benchmark, seeing an average of ~19%
+ throughput improvement on POWER10(Radix MMU + 64K Page Size) with
+ MGLRU patches on top of 5.16 kernel for MongoDB + YCSB across
+ three different request distributions, namely, Exponential, Uniform
+ and Zipfan.
+
+Shuang from U of Rochester reported [13]:
+ With the MGLRU, fio achieved 95% CIs [38.95, 40.26]%, [4.12, 6.64]%
+ and [9.26, 10.36]% higher throughput, respectively, for random
+ access, Zipfian (distribution) access and Gaussian (distribution)
+ access, when the average number of jobs per CPU is 1; 95% CIs
+ [42.32, 49.15]%, [9.44, 9.89]% and [20.99, 22.86]% higher
+ throughput, respectively, for random access, Zipfian access and
+ Gaussian access, when the average number of jobs per CPU is 2.
+
+Daniel from Michigan Tech reported [14]:
+ With Memcached allocating ~100GB of byte-addressable Optante,
+ performance improvement in terms of throughput (measured as queries
+ per second) was about 10% for a series of workloads.
+
+Large-scale deployments
+-----------------------
+We've rolled out MGLRU to tens of millions of ChromeOS users and
+about a million Android users. Google's fleetwide profiling [15] shows
+an overall 40% decrease in kswapd CPU usage, in addition to
+improvements in other UX metrics, e.g., an 85% decrease in the number
+of low-memory kills at the 75th percentile and an 18% decrease in
+app launch time at the 50th percentile.
+
+The downstream kernels that have been using MGLRU include:
+1. Android [16]
+2. Arch Linux Zen [17]
+3. Armbian [18]
+4. ChromeOS [19]
+5. Liquorix [20]
+6. OpenWrt [21]
+7. post-factum [22]
+8. XanMod [23]
+
+[11] https://lore.kernel.org/r/140226722f2032c86301fbd326d91baefe3d7d23.camel@yandex.ru/
+[12] https://lore.kernel.org/r/87czj3mux0.fsf@vajain21.in.ibm.com/
+[13] https://lore.kernel.org/r/20220105024423.26409-1-szhai2@cs.rochester.edu/
+[14] https://lore.kernel.org/r/CA+4-3vksGvKd18FgRinxhqHetBS1hQekJE2gwco8Ja-bJWKtFw@mail.gmail.com/
+[15] https://dl.acm.org/doi/10.1145/2749469.2750392
+[16] https://android.com
+[17] https://archlinux.org
+[18] https://armbian.com
+[19] https://chromium.org
+[20] https://liquorix.net
+[21] https://openwrt.org
+[22] https://codeberg.org/pf-kernel
+[23] https://xanmod.org
+
+Summary
+=======
+The facts are:
+1. The independent lab results and the real-world applications
+ indicate substantial improvements; there are no known regressions.
+2. Thrashing prevention, working set estimation and proactive reclaim
+ work out of the box; there are no equivalent solutions.
+3. There is a lot of new code; no smaller changes have been
+ demonstrated similar effects.
+
+Our options, accordingly, are:
+1. Given the amount of evidence, the reported improvements will likely
+ materialize for a wide range of workloads.
+2. Gauging the interest from the past discussions, the new features
+ will likely be put to use for both personal computers and data
+ centers.
+3. Based on Google's track record, the new code will likely be well
+ maintained in the long term. It'd be more difficult if not
+ impossible to achieve similar effects with other approaches.
+
+This patch (of 14):
+
+Some architectures automatically set the accessed bit in PTEs, e.g., x86
+and arm64 v8.2. On architectures that do not have this capability,
+clearing the accessed bit in a PTE usually triggers a page fault following
+the TLB miss of this PTE (to emulate the accessed bit).
+
+Being aware of this capability can help make better decisions, e.g.,
+whether to spread the work out over a period of time to reduce bursty page
+faults when trying to clear the accessed bit in many PTEs.
+
+Note that theoretically this capability can be unreliable, e.g.,
+hotplugged CPUs might be different from builtin ones. Therefore it should
+not be used in architecture-independent code that involves correctness,
+e.g., to determine whether TLB flushes are required (in combination with
+the accessed bit).
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-1-yuzhao@google.com
+Link: https://lkml.kernel.org/r/20220918080010.2920238-2-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Reviewed-by: Barry Song <baohua@kernel.org>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Acked-by: Will Deacon <will@kernel.org>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: linux-arm-kernel@lists.infradead.org
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ arch/arm64/include/asm/pgtable.h | 14 ++------------
+ arch/x86/include/asm/pgtable.h | 6 +++---
+ include/linux/pgtable.h | 13 +++++++++++++
+ mm/memory.c | 14 +-------------
+ 4 files changed, 19 insertions(+), 28 deletions(-)
+
+--- a/arch/arm64/include/asm/pgtable.h
++++ b/arch/arm64/include/asm/pgtable.h
+@@ -999,23 +999,13 @@ static inline void update_mmu_cache(stru
+ * page after fork() + CoW for pfn mappings. We don't always have a
+ * hardware-managed access flag on arm64.
+ */
+-static inline bool arch_faults_on_old_pte(void)
+-{
+- WARN_ON(preemptible());
+-
+- return !cpu_has_hw_af();
+-}
+-#define arch_faults_on_old_pte arch_faults_on_old_pte
++#define arch_has_hw_pte_young cpu_has_hw_af
+
+ /*
+ * Experimentally, it's cheap to set the access flag in hardware and we
+ * benefit from prefaulting mappings as 'old' to start with.
+ */
+-static inline bool arch_wants_old_prefaulted_pte(void)
+-{
+- return !arch_faults_on_old_pte();
+-}
+-#define arch_wants_old_prefaulted_pte arch_wants_old_prefaulted_pte
++#define arch_wants_old_prefaulted_pte cpu_has_hw_af
+
+ #endif /* !__ASSEMBLY__ */
+
+--- a/arch/x86/include/asm/pgtable.h
++++ b/arch/x86/include/asm/pgtable.h
+@@ -1397,10 +1397,10 @@ static inline bool arch_has_pfn_modify_c
+ return boot_cpu_has_bug(X86_BUG_L1TF);
+ }
+
+-#define arch_faults_on_old_pte arch_faults_on_old_pte
+-static inline bool arch_faults_on_old_pte(void)
++#define arch_has_hw_pte_young arch_has_hw_pte_young
++static inline bool arch_has_hw_pte_young(void)
+ {
+- return false;
++ return true;
+ }
+
+ #endif /* __ASSEMBLY__ */
+--- a/include/linux/pgtable.h
++++ b/include/linux/pgtable.h
+@@ -259,6 +259,19 @@ static inline int pmdp_clear_flush_young
+ #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+ #endif
+
++#ifndef arch_has_hw_pte_young
++/*
++ * Return whether the accessed bit is supported on the local CPU.
++ *
++ * This stub assumes accessing through an old PTE triggers a page fault.
++ * Architectures that automatically set the access bit should overwrite it.
++ */
++static inline bool arch_has_hw_pte_young(void)
++{
++ return false;
++}
++#endif
++
+ #ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR
+ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
+ unsigned long address,
+--- a/mm/memory.c
++++ b/mm/memory.c
+@@ -121,18 +121,6 @@ int randomize_va_space __read_mostly =
+ 2;
+ #endif
+
+-#ifndef arch_faults_on_old_pte
+-static inline bool arch_faults_on_old_pte(void)
+-{
+- /*
+- * Those arches which don't have hw access flag feature need to
+- * implement their own helper. By default, "true" means pagefault
+- * will be hit on old pte.
+- */
+- return true;
+-}
+-#endif
+-
+ #ifndef arch_wants_old_prefaulted_pte
+ static inline bool arch_wants_old_prefaulted_pte(void)
+ {
+@@ -2782,7 +2770,7 @@ static inline bool cow_user_page(struct
+ * On architectures with software "accessed" bits, we would
+ * take a double page fault, so mark it accessed here.
+ */
+- if (arch_faults_on_old_pte() && !pte_young(vmf->orig_pte)) {
++ if (!arch_has_hw_pte_young() && !pte_young(vmf->orig_pte)) {
+ pte_t entry;
+
+ vmf->pte = pte_offset_map_lock(mm, vmf->pmd, addr, &vmf->ptl);
diff --git a/target/linux/generic/backport-6.1/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch b/target/linux/generic/backport-6.1/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch
new file mode 100644
index 0000000000..9e0430ea2a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch
@@ -0,0 +1,153 @@
+From 493de1c4b0f2cd909169401da8c445f6c8a7e29d Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 01:59:59 -0600
+Subject: [PATCH 02/29] mm: x86: add CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some architectures support the accessed bit in non-leaf PMD entries, e.g.,
+x86 sets the accessed bit in a non-leaf PMD entry when using it as part of
+linear address translation [1]. Page table walkers that clear the
+accessed bit may use this capability to reduce their search space.
+
+Note that:
+1. Although an inline function is preferable, this capability is added
+ as a configuration option for consistency with the existing macros.
+2. Due to the little interest in other varieties, this capability was
+ only tested on Intel and AMD CPUs.
+
+Thanks to the following developers for their efforts [2][3].
+ Randy Dunlap <rdunlap@infradead.org>
+ Stephen Rothwell <sfr@canb.auug.org.au>
+
+[1]: Intel 64 and IA-32 Architectures Software Developer's Manual
+ Volume 3 (June 2021), section 4.8
+[2] https://lore.kernel.org/r/bfdcc7c8-922f-61a9-aa15-7e7250f04af7@infradead.org/
+[3] https://lore.kernel.org/r/20220413151513.5a0d7a7e@canb.auug.org.au/
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-3-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Reviewed-by: Barry Song <baohua@kernel.org>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ arch/Kconfig | 8 ++++++++
+ arch/x86/Kconfig | 1 +
+ arch/x86/include/asm/pgtable.h | 3 ++-
+ arch/x86/mm/pgtable.c | 5 ++++-
+ include/linux/pgtable.h | 4 ++--
+ 5 files changed, 17 insertions(+), 4 deletions(-)
+
+--- a/arch/Kconfig
++++ b/arch/Kconfig
+@@ -1295,6 +1295,14 @@ config ARCH_HAS_ELFCORE_COMPAT
+ config ARCH_HAS_PARANOID_L1D_FLUSH
+ bool
+
++config ARCH_HAS_NONLEAF_PMD_YOUNG
++ bool
++ help
++ Architectures that select this option are capable of setting the
++ accessed bit in non-leaf PMD entries when using them as part of linear
++ address translations. Page table walkers that clear the accessed bit
++ may use this capability to reduce their search space.
++
+ source "kernel/gcov/Kconfig"
+
+ source "scripts/gcc-plugins/Kconfig"
+--- a/arch/x86/Kconfig
++++ b/arch/x86/Kconfig
+@@ -84,6 +84,7 @@ config X86
+ select ARCH_HAS_PMEM_API if X86_64
+ select ARCH_HAS_PTE_DEVMAP if X86_64
+ select ARCH_HAS_PTE_SPECIAL
++ select ARCH_HAS_NONLEAF_PMD_YOUNG if PGTABLE_LEVELS > 2
+ select ARCH_HAS_UACCESS_FLUSHCACHE if X86_64
+ select ARCH_HAS_COPY_MC if X86_64
+ select ARCH_HAS_SET_MEMORY
+--- a/arch/x86/include/asm/pgtable.h
++++ b/arch/x86/include/asm/pgtable.h
+@@ -817,7 +817,8 @@ static inline unsigned long pmd_page_vad
+
+ static inline int pmd_bad(pmd_t pmd)
+ {
+- return (pmd_flags(pmd) & ~_PAGE_USER) != _KERNPG_TABLE;
++ return (pmd_flags(pmd) & ~(_PAGE_USER | _PAGE_ACCESSED)) !=
++ (_KERNPG_TABLE & ~_PAGE_ACCESSED);
+ }
+
+ static inline unsigned long pages_to_mb(unsigned long npg)
+--- a/arch/x86/mm/pgtable.c
++++ b/arch/x86/mm/pgtable.c
+@@ -550,7 +550,7 @@ int ptep_test_and_clear_young(struct vm_
+ return ret;
+ }
+
+-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
++#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
+ int pmdp_test_and_clear_young(struct vm_area_struct *vma,
+ unsigned long addr, pmd_t *pmdp)
+ {
+@@ -562,6 +562,9 @@ int pmdp_test_and_clear_young(struct vm_
+
+ return ret;
+ }
++#endif
++
++#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ int pudp_test_and_clear_young(struct vm_area_struct *vma,
+ unsigned long addr, pud_t *pudp)
+ {
+--- a/include/linux/pgtable.h
++++ b/include/linux/pgtable.h
+@@ -212,7 +212,7 @@ static inline int ptep_test_and_clear_yo
+ #endif
+
+ #ifndef __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG
+-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
++#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
+ static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
+ unsigned long address,
+ pmd_t *pmdp)
+@@ -233,7 +233,7 @@ static inline int pmdp_test_and_clear_yo
+ BUILD_BUG();
+ return 0;
+ }
+-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
++#endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG */
+ #endif
+
+ #ifndef __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
diff --git a/target/linux/generic/backport-6.1/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch b/target/linux/generic/backport-6.1/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch
new file mode 100644
index 0000000000..b8d2917d26
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch
@@ -0,0 +1,275 @@
+From 9e17efd11450d3d2069adaa3c58db9ac8ebd1c66 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:00 -0600
+Subject: [PATCH 03/29] mm/vmscan.c: refactor shrink_node()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch refactors shrink_node() to improve readability for the upcoming
+changes to mm/vmscan.c.
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-4-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Reviewed-by: Barry Song <baohua@kernel.org>
+Reviewed-by: Miaohe Lin <linmiaohe@huawei.com>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 198 +++++++++++++++++++++++++++-------------------------
+ 1 file changed, 104 insertions(+), 94 deletions(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -2497,6 +2497,109 @@ enum scan_balance {
+ SCAN_FILE,
+ };
+
++static void prepare_scan_count(pg_data_t *pgdat, struct scan_control *sc)
++{
++ unsigned long file;
++ struct lruvec *target_lruvec;
++
++ target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat);
++
++ /*
++ * Flush the memory cgroup stats, so that we read accurate per-memcg
++ * lruvec stats for heuristics.
++ */
++ mem_cgroup_flush_stats();
++
++ /*
++ * Determine the scan balance between anon and file LRUs.
++ */
++ spin_lock_irq(&target_lruvec->lru_lock);
++ sc->anon_cost = target_lruvec->anon_cost;
++ sc->file_cost = target_lruvec->file_cost;
++ spin_unlock_irq(&target_lruvec->lru_lock);
++
++ /*
++ * Target desirable inactive:active list ratios for the anon
++ * and file LRU lists.
++ */
++ if (!sc->force_deactivate) {
++ unsigned long refaults;
++
++ refaults = lruvec_page_state(target_lruvec,
++ WORKINGSET_ACTIVATE_ANON);
++ if (refaults != target_lruvec->refaults[0] ||
++ inactive_is_low(target_lruvec, LRU_INACTIVE_ANON))
++ sc->may_deactivate |= DEACTIVATE_ANON;
++ else
++ sc->may_deactivate &= ~DEACTIVATE_ANON;
++
++ /*
++ * When refaults are being observed, it means a new
++ * workingset is being established. Deactivate to get
++ * rid of any stale active pages quickly.
++ */
++ refaults = lruvec_page_state(target_lruvec,
++ WORKINGSET_ACTIVATE_FILE);
++ if (refaults != target_lruvec->refaults[1] ||
++ inactive_is_low(target_lruvec, LRU_INACTIVE_FILE))
++ sc->may_deactivate |= DEACTIVATE_FILE;
++ else
++ sc->may_deactivate &= ~DEACTIVATE_FILE;
++ } else
++ sc->may_deactivate = DEACTIVATE_ANON | DEACTIVATE_FILE;
++
++ /*
++ * If we have plenty of inactive file pages that aren't
++ * thrashing, try to reclaim those first before touching
++ * anonymous pages.
++ */
++ file = lruvec_page_state(target_lruvec, NR_INACTIVE_FILE);
++ if (file >> sc->priority && !(sc->may_deactivate & DEACTIVATE_FILE))
++ sc->cache_trim_mode = 1;
++ else
++ sc->cache_trim_mode = 0;
++
++ /*
++ * Prevent the reclaimer from falling into the cache trap: as
++ * cache pages start out inactive, every cache fault will tip
++ * the scan balance towards the file LRU. And as the file LRU
++ * shrinks, so does the window for rotation from references.
++ * This means we have a runaway feedback loop where a tiny
++ * thrashing file LRU becomes infinitely more attractive than
++ * anon pages. Try to detect this based on file LRU size.
++ */
++ if (!cgroup_reclaim(sc)) {
++ unsigned long total_high_wmark = 0;
++ unsigned long free, anon;
++ int z;
++
++ free = sum_zone_node_page_state(pgdat->node_id, NR_FREE_PAGES);
++ file = node_page_state(pgdat, NR_ACTIVE_FILE) +
++ node_page_state(pgdat, NR_INACTIVE_FILE);
++
++ for (z = 0; z < MAX_NR_ZONES; z++) {
++ struct zone *zone = &pgdat->node_zones[z];
++
++ if (!managed_zone(zone))
++ continue;
++
++ total_high_wmark += high_wmark_pages(zone);
++ }
++
++ /*
++ * Consider anon: if that's low too, this isn't a
++ * runaway file reclaim problem, but rather just
++ * extreme pressure. Reclaim as per usual then.
++ */
++ anon = node_page_state(pgdat, NR_INACTIVE_ANON);
++
++ sc->file_is_tiny =
++ file + free <= total_high_wmark &&
++ !(sc->may_deactivate & DEACTIVATE_ANON) &&
++ anon >> sc->priority;
++ }
++}
++
+ /*
+ * Determine how aggressively the anon and file LRU lists should be
+ * scanned. The relative value of each set of LRU lists is determined
+@@ -2965,109 +3068,16 @@ static void shrink_node(pg_data_t *pgdat
+ unsigned long nr_reclaimed, nr_scanned;
+ struct lruvec *target_lruvec;
+ bool reclaimable = false;
+- unsigned long file;
+
+ target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat);
+
+ again:
+- /*
+- * Flush the memory cgroup stats, so that we read accurate per-memcg
+- * lruvec stats for heuristics.
+- */
+- mem_cgroup_flush_stats();
+-
+ memset(&sc->nr, 0, sizeof(sc->nr));
+
+ nr_reclaimed = sc->nr_reclaimed;
+ nr_scanned = sc->nr_scanned;
+
+- /*
+- * Determine the scan balance between anon and file LRUs.
+- */
+- spin_lock_irq(&target_lruvec->lru_lock);
+- sc->anon_cost = target_lruvec->anon_cost;
+- sc->file_cost = target_lruvec->file_cost;
+- spin_unlock_irq(&target_lruvec->lru_lock);
+-
+- /*
+- * Target desirable inactive:active list ratios for the anon
+- * and file LRU lists.
+- */
+- if (!sc->force_deactivate) {
+- unsigned long refaults;
+-
+- refaults = lruvec_page_state(target_lruvec,
+- WORKINGSET_ACTIVATE_ANON);
+- if (refaults != target_lruvec->refaults[0] ||
+- inactive_is_low(target_lruvec, LRU_INACTIVE_ANON))
+- sc->may_deactivate |= DEACTIVATE_ANON;
+- else
+- sc->may_deactivate &= ~DEACTIVATE_ANON;
+-
+- /*
+- * When refaults are being observed, it means a new
+- * workingset is being established. Deactivate to get
+- * rid of any stale active pages quickly.
+- */
+- refaults = lruvec_page_state(target_lruvec,
+- WORKINGSET_ACTIVATE_FILE);
+- if (refaults != target_lruvec->refaults[1] ||
+- inactive_is_low(target_lruvec, LRU_INACTIVE_FILE))
+- sc->may_deactivate |= DEACTIVATE_FILE;
+- else
+- sc->may_deactivate &= ~DEACTIVATE_FILE;
+- } else
+- sc->may_deactivate = DEACTIVATE_ANON | DEACTIVATE_FILE;
+-
+- /*
+- * If we have plenty of inactive file pages that aren't
+- * thrashing, try to reclaim those first before touching
+- * anonymous pages.
+- */
+- file = lruvec_page_state(target_lruvec, NR_INACTIVE_FILE);
+- if (file >> sc->priority && !(sc->may_deactivate & DEACTIVATE_FILE))
+- sc->cache_trim_mode = 1;
+- else
+- sc->cache_trim_mode = 0;
+-
+- /*
+- * Prevent the reclaimer from falling into the cache trap: as
+- * cache pages start out inactive, every cache fault will tip
+- * the scan balance towards the file LRU. And as the file LRU
+- * shrinks, so does the window for rotation from references.
+- * This means we have a runaway feedback loop where a tiny
+- * thrashing file LRU becomes infinitely more attractive than
+- * anon pages. Try to detect this based on file LRU size.
+- */
+- if (!cgroup_reclaim(sc)) {
+- unsigned long total_high_wmark = 0;
+- unsigned long free, anon;
+- int z;
+-
+- free = sum_zone_node_page_state(pgdat->node_id, NR_FREE_PAGES);
+- file = node_page_state(pgdat, NR_ACTIVE_FILE) +
+- node_page_state(pgdat, NR_INACTIVE_FILE);
+-
+- for (z = 0; z < MAX_NR_ZONES; z++) {
+- struct zone *zone = &pgdat->node_zones[z];
+- if (!managed_zone(zone))
+- continue;
+-
+- total_high_wmark += high_wmark_pages(zone);
+- }
+-
+- /*
+- * Consider anon: if that's low too, this isn't a
+- * runaway file reclaim problem, but rather just
+- * extreme pressure. Reclaim as per usual then.
+- */
+- anon = node_page_state(pgdat, NR_INACTIVE_ANON);
+-
+- sc->file_is_tiny =
+- file + free <= total_high_wmark &&
+- !(sc->may_deactivate & DEACTIVATE_ANON) &&
+- anon >> sc->priority;
+- }
++ prepare_scan_count(pgdat, sc);
+
+ shrink_node_memcgs(pgdat, sc);
+
diff --git a/target/linux/generic/backport-6.1/020-v6.1-04-Revert-include-linux-mm_inline.h-fold-__update_lru_s.patch b/target/linux/generic/backport-6.1/020-v6.1-04-Revert-include-linux-mm_inline.h-fold-__update_lru_s.patch
new file mode 100644
index 0000000000..2f277a56e1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-04-Revert-include-linux-mm_inline.h-fold-__update_lru_s.patch
@@ -0,0 +1,82 @@
+From 03705be42114db7cc5bd6eb7bf7e8703c94d4880 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:01 -0600
+Subject: [PATCH 04/29] Revert "include/linux/mm_inline.h: fold
+ __update_lru_size() into its sole caller"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch undoes the following refactor: commit 289ccba18af4
+("include/linux/mm_inline.h: fold __update_lru_size() into its sole
+caller")
+
+The upcoming changes to include/linux/mm_inline.h will reuse
+__update_lru_size().
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-5-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Reviewed-by: Miaohe Lin <linmiaohe@huawei.com>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Barry Song <baohua@kernel.org>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/mm_inline.h | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/include/linux/mm_inline.h
++++ b/include/linux/mm_inline.h
+@@ -24,7 +24,7 @@ static inline int page_is_file_lru(struc
+ return !PageSwapBacked(page);
+ }
+
+-static __always_inline void update_lru_size(struct lruvec *lruvec,
++static __always_inline void __update_lru_size(struct lruvec *lruvec,
+ enum lru_list lru, enum zone_type zid,
+ int nr_pages)
+ {
+@@ -33,6 +33,13 @@ static __always_inline void update_lru_s
+ __mod_lruvec_state(lruvec, NR_LRU_BASE + lru, nr_pages);
+ __mod_zone_page_state(&pgdat->node_zones[zid],
+ NR_ZONE_LRU_BASE + lru, nr_pages);
++}
++
++static __always_inline void update_lru_size(struct lruvec *lruvec,
++ enum lru_list lru, enum zone_type zid,
++ long nr_pages)
++{
++ __update_lru_size(lruvec, lru, zid, nr_pages);
+ #ifdef CONFIG_MEMCG
+ mem_cgroup_update_lru_size(lruvec, lru, zid, nr_pages);
+ #endif
diff --git a/target/linux/generic/backport-6.1/020-v6.1-05-mm-multi-gen-LRU-groundwork.patch b/target/linux/generic/backport-6.1/020-v6.1-05-mm-multi-gen-LRU-groundwork.patch
new file mode 100644
index 0000000000..237bd84875
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-05-mm-multi-gen-LRU-groundwork.patch
@@ -0,0 +1,807 @@
+From a9b328add8422921a0dbbef162730800e16e8cfd Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:02 -0600
+Subject: [PATCH 05/29] mm: multi-gen LRU: groundwork
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Evictable pages are divided into multiple generations for each lruvec.
+The youngest generation number is stored in lrugen->max_seq for both
+anon and file types as they are aged on an equal footing. The oldest
+generation numbers are stored in lrugen->min_seq[] separately for anon
+and file types as clean file pages can be evicted regardless of swap
+constraints. These three variables are monotonically increasing.
+
+Generation numbers are truncated into order_base_2(MAX_NR_GENS+1) bits
+in order to fit into the gen counter in page->flags. Each truncated
+generation number is an index to lrugen->lists[]. The sliding window
+technique is used to track at least MIN_NR_GENS and at most
+MAX_NR_GENS generations. The gen counter stores a value within [1,
+MAX_NR_GENS] while a page is on one of lrugen->lists[]. Otherwise it
+stores 0.
+
+There are two conceptually independent procedures: "the aging", which
+produces young generations, and "the eviction", which consumes old
+generations. They form a closed-loop system, i.e., "the page reclaim".
+Both procedures can be invoked from userspace for the purposes of working
+set estimation and proactive reclaim. These techniques are commonly used
+to optimize job scheduling (bin packing) in data centers [1][2].
+
+To avoid confusion, the terms "hot" and "cold" will be applied to the
+multi-gen LRU, as a new convention; the terms "active" and "inactive" will
+be applied to the active/inactive LRU, as usual.
+
+The protection of hot pages and the selection of cold pages are based
+on page access channels and patterns. There are two access channels:
+one through page tables and the other through file descriptors. The
+protection of the former channel is by design stronger because:
+1. The uncertainty in determining the access patterns of the former
+ channel is higher due to the approximation of the accessed bit.
+2. The cost of evicting the former channel is higher due to the TLB
+ flushes required and the likelihood of encountering the dirty bit.
+3. The penalty of underprotecting the former channel is higher because
+ applications usually do not prepare themselves for major page
+ faults like they do for blocked I/O. E.g., GUI applications
+ commonly use dedicated I/O threads to avoid blocking rendering
+ threads.
+
+There are also two access patterns: one with temporal locality and the
+other without. For the reasons listed above, the former channel is
+assumed to follow the former pattern unless VM_SEQ_READ or VM_RAND_READ is
+present; the latter channel is assumed to follow the latter pattern unless
+outlying refaults have been observed [3][4].
+
+The next patch will address the "outlying refaults". Three macros, i.e.,
+LRU_REFS_WIDTH, LRU_REFS_PGOFF and LRU_REFS_MASK, used later are added in
+this patch to make the entire patchset less diffy.
+
+A page is added to the youngest generation on faulting. The aging needs
+to check the accessed bit at least twice before handing this page over to
+the eviction. The first check takes care of the accessed bit set on the
+initial fault; the second check makes sure this page has not been used
+since then. This protocol, AKA second chance, requires a minimum of two
+generations, hence MIN_NR_GENS.
+
+[1] https://dl.acm.org/doi/10.1145/3297858.3304053
+[2] https://dl.acm.org/doi/10.1145/3503222.3507731
+[3] https://lwn.net/Articles/495543/
+[4] https://lwn.net/Articles/815342/
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-6-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Barry Song <baohua@kernel.org>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ fs/fuse/dev.c | 3 +-
+ include/linux/mm.h | 2 +
+ include/linux/mm_inline.h | 177 +++++++++++++++++++++++++++++-
+ include/linux/mmzone.h | 100 +++++++++++++++++
+ include/linux/page-flags-layout.h | 13 ++-
+ include/linux/page-flags.h | 4 +-
+ include/linux/sched.h | 4 +
+ kernel/bounds.c | 5 +
+ mm/Kconfig | 8 ++
+ mm/huge_memory.c | 3 +-
+ mm/memcontrol.c | 2 +
+ mm/memory.c | 25 +++++
+ mm/mm_init.c | 6 +-
+ mm/mmzone.c | 2 +
+ mm/swap.c | 10 +-
+ mm/vmscan.c | 75 +++++++++++++
+ 16 files changed, 425 insertions(+), 14 deletions(-)
+
+--- a/fs/fuse/dev.c
++++ b/fs/fuse/dev.c
+@@ -785,7 +785,8 @@ static int fuse_check_page(struct page *
+ 1 << PG_active |
+ 1 << PG_workingset |
+ 1 << PG_reclaim |
+- 1 << PG_waiters))) {
++ 1 << PG_waiters |
++ LRU_GEN_MASK | LRU_REFS_MASK))) {
+ dump_page(page, "fuse: trying to steal weird page");
+ return 1;
+ }
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -1093,6 +1093,8 @@ vm_fault_t finish_mkwrite_fault(struct v
+ #define ZONES_PGOFF (NODES_PGOFF - ZONES_WIDTH)
+ #define LAST_CPUPID_PGOFF (ZONES_PGOFF - LAST_CPUPID_WIDTH)
+ #define KASAN_TAG_PGOFF (LAST_CPUPID_PGOFF - KASAN_TAG_WIDTH)
++#define LRU_GEN_PGOFF (KASAN_TAG_PGOFF - LRU_GEN_WIDTH)
++#define LRU_REFS_PGOFF (LRU_GEN_PGOFF - LRU_REFS_WIDTH)
+
+ /*
+ * Define the bit shifts to access each section. For non-existent
+--- a/include/linux/mm_inline.h
++++ b/include/linux/mm_inline.h
+@@ -26,10 +26,13 @@ static inline int page_is_file_lru(struc
+
+ static __always_inline void __update_lru_size(struct lruvec *lruvec,
+ enum lru_list lru, enum zone_type zid,
+- int nr_pages)
++ long nr_pages)
+ {
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+
++ lockdep_assert_held(&lruvec->lru_lock);
++ WARN_ON_ONCE(nr_pages != (int)nr_pages);
++
+ __mod_lruvec_state(lruvec, NR_LRU_BASE + lru, nr_pages);
+ __mod_zone_page_state(&pgdat->node_zones[zid],
+ NR_ZONE_LRU_BASE + lru, nr_pages);
+@@ -86,11 +89,177 @@ static __always_inline enum lru_list pag
+ return lru;
+ }
+
++#ifdef CONFIG_LRU_GEN
++
++static inline bool lru_gen_enabled(void)
++{
++ return true;
++}
++
++static inline bool lru_gen_in_fault(void)
++{
++ return current->in_lru_fault;
++}
++
++static inline int lru_gen_from_seq(unsigned long seq)
++{
++ return seq % MAX_NR_GENS;
++}
++
++static inline int page_lru_gen(struct page *page)
++{
++ unsigned long flags = READ_ONCE(page->flags);
++
++ return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
++}
++
++static inline bool lru_gen_is_active(struct lruvec *lruvec, int gen)
++{
++ unsigned long max_seq = lruvec->lrugen.max_seq;
++
++ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS);
++
++ /* see the comment on MIN_NR_GENS */
++ return gen == lru_gen_from_seq(max_seq) || gen == lru_gen_from_seq(max_seq - 1);
++}
++
++static inline void lru_gen_update_size(struct lruvec *lruvec, struct page *page,
++ int old_gen, int new_gen)
++{
++ int type = page_is_file_lru(page);
++ int zone = page_zonenum(page);
++ int delta = thp_nr_pages(page);
++ enum lru_list lru = type * LRU_INACTIVE_FILE;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ VM_WARN_ON_ONCE(old_gen != -1 && old_gen >= MAX_NR_GENS);
++ VM_WARN_ON_ONCE(new_gen != -1 && new_gen >= MAX_NR_GENS);
++ VM_WARN_ON_ONCE(old_gen == -1 && new_gen == -1);
++
++ if (old_gen >= 0)
++ WRITE_ONCE(lrugen->nr_pages[old_gen][type][zone],
++ lrugen->nr_pages[old_gen][type][zone] - delta);
++ if (new_gen >= 0)
++ WRITE_ONCE(lrugen->nr_pages[new_gen][type][zone],
++ lrugen->nr_pages[new_gen][type][zone] + delta);
++
++ /* addition */
++ if (old_gen < 0) {
++ if (lru_gen_is_active(lruvec, new_gen))
++ lru += LRU_ACTIVE;
++ __update_lru_size(lruvec, lru, zone, delta);
++ return;
++ }
++
++ /* deletion */
++ if (new_gen < 0) {
++ if (lru_gen_is_active(lruvec, old_gen))
++ lru += LRU_ACTIVE;
++ __update_lru_size(lruvec, lru, zone, -delta);
++ return;
++ }
++}
++
++static inline bool lru_gen_add_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
++{
++ unsigned long seq;
++ unsigned long flags;
++ int gen = page_lru_gen(page);
++ int type = page_is_file_lru(page);
++ int zone = page_zonenum(page);
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ VM_WARN_ON_ONCE_PAGE(gen != -1, page);
++
++ if (PageUnevictable(page))
++ return false;
++ /*
++ * There are three common cases for this page:
++ * 1. If it's hot, e.g., freshly faulted in or previously hot and
++ * migrated, add it to the youngest generation.
++ * 2. If it's cold but can't be evicted immediately, i.e., an anon page
++ * not in swapcache or a dirty page pending writeback, add it to the
++ * second oldest generation.
++ * 3. Everything else (clean, cold) is added to the oldest generation.
++ */
++ if (PageActive(page))
++ seq = lrugen->max_seq;
++ else if ((type == LRU_GEN_ANON && !PageSwapCache(page)) ||
++ (PageReclaim(page) &&
++ (PageDirty(page) || PageWriteback(page))))
++ seq = lrugen->min_seq[type] + 1;
++ else
++ seq = lrugen->min_seq[type];
++
++ gen = lru_gen_from_seq(seq);
++ flags = (gen + 1UL) << LRU_GEN_PGOFF;
++ /* see the comment on MIN_NR_GENS about PG_active */
++ set_mask_bits(&page->flags, LRU_GEN_MASK | BIT(PG_active), flags);
++
++ lru_gen_update_size(lruvec, page, -1, gen);
++ /* for rotate_reclaimable_page() */
++ if (reclaiming)
++ list_add_tail(&page->lru, &lrugen->lists[gen][type][zone]);
++ else
++ list_add(&page->lru, &lrugen->lists[gen][type][zone]);
++
++ return true;
++}
++
++static inline bool lru_gen_del_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
++{
++ unsigned long flags;
++ int gen = page_lru_gen(page);
++
++ if (gen < 0)
++ return false;
++
++ VM_WARN_ON_ONCE_PAGE(PageActive(page), page);
++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page);
++
++ /* for migrate_page_states() */
++ flags = !reclaiming && lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;
++ flags = set_mask_bits(&page->flags, LRU_GEN_MASK, flags);
++ gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
++
++ lru_gen_update_size(lruvec, page, gen, -1);
++ list_del(&page->lru);
++
++ return true;
++}
++
++#else /* !CONFIG_LRU_GEN */
++
++static inline bool lru_gen_enabled(void)
++{
++ return false;
++}
++
++static inline bool lru_gen_in_fault(void)
++{
++ return false;
++}
++
++static inline bool lru_gen_add_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
++{
++ return false;
++}
++
++static inline bool lru_gen_del_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
++{
++ return false;
++}
++
++#endif /* CONFIG_LRU_GEN */
++
+ static __always_inline void add_page_to_lru_list(struct page *page,
+ struct lruvec *lruvec)
+ {
+ enum lru_list lru = page_lru(page);
+
++ if (lru_gen_add_page(lruvec, page, false))
++ return;
++
+ update_lru_size(lruvec, lru, page_zonenum(page), thp_nr_pages(page));
+ list_add(&page->lru, &lruvec->lists[lru]);
+ }
+@@ -100,6 +269,9 @@ static __always_inline void add_page_to_
+ {
+ enum lru_list lru = page_lru(page);
+
++ if (lru_gen_add_page(lruvec, page, true))
++ return;
++
+ update_lru_size(lruvec, lru, page_zonenum(page), thp_nr_pages(page));
+ list_add_tail(&page->lru, &lruvec->lists[lru]);
+ }
+@@ -107,6 +279,9 @@ static __always_inline void add_page_to_
+ static __always_inline void del_page_from_lru_list(struct page *page,
+ struct lruvec *lruvec)
+ {
++ if (lru_gen_del_page(lruvec, page, false))
++ return;
++
+ list_del(&page->lru);
+ update_lru_size(lruvec, page_lru(page), page_zonenum(page),
+ -thp_nr_pages(page));
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -294,6 +294,102 @@ enum lruvec_flags {
+ */
+ };
+
++#endif /* !__GENERATING_BOUNDS_H */
++
++/*
++ * Evictable pages are divided into multiple generations. The youngest and the
++ * oldest generation numbers, max_seq and min_seq, are monotonically increasing.
++ * They form a sliding window of a variable size [MIN_NR_GENS, MAX_NR_GENS]. An
++ * offset within MAX_NR_GENS, i.e., gen, indexes the LRU list of the
++ * corresponding generation. The gen counter in page->flags stores gen+1 while
++ * a page is on one of lrugen->lists[]. Otherwise it stores 0.
++ *
++ * A page is added to the youngest generation on faulting. The aging needs to
++ * check the accessed bit at least twice before handing this page over to the
++ * eviction. The first check takes care of the accessed bit set on the initial
++ * fault; the second check makes sure this page hasn't been used since then.
++ * This process, AKA second chance, requires a minimum of two generations,
++ * hence MIN_NR_GENS. And to maintain ABI compatibility with the active/inactive
++ * LRU, e.g., /proc/vmstat, these two generations are considered active; the
++ * rest of generations, if they exist, are considered inactive. See
++ * lru_gen_is_active().
++ *
++ * PG_active is always cleared while a page is on one of lrugen->lists[] so that
++ * the aging needs not to worry about it. And it's set again when a page
++ * considered active is isolated for non-reclaiming purposes, e.g., migration.
++ * See lru_gen_add_page() and lru_gen_del_page().
++ *
++ * MAX_NR_GENS is set to 4 so that the multi-gen LRU can support twice the
++ * number of categories of the active/inactive LRU when keeping track of
++ * accesses through page tables. This requires order_base_2(MAX_NR_GENS+1) bits
++ * in page->flags.
++ */
++#define MIN_NR_GENS 2U
++#define MAX_NR_GENS 4U
++
++#ifndef __GENERATING_BOUNDS_H
++
++struct lruvec;
++
++#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
++#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
++
++#ifdef CONFIG_LRU_GEN
++
++enum {
++ LRU_GEN_ANON,
++ LRU_GEN_FILE,
++};
++
++/*
++ * The youngest generation number is stored in max_seq for both anon and file
++ * types as they are aged on an equal footing. The oldest generation numbers are
++ * stored in min_seq[] separately for anon and file types as clean file pages
++ * can be evicted regardless of swap constraints.
++ *
++ * Normally anon and file min_seq are in sync. But if swapping is constrained,
++ * e.g., out of swap space, file min_seq is allowed to advance and leave anon
++ * min_seq behind.
++ *
++ * The number of pages in each generation is eventually consistent and therefore
++ * can be transiently negative.
++ */
++struct lru_gen_struct {
++ /* the aging increments the youngest generation number */
++ unsigned long max_seq;
++ /* the eviction increments the oldest generation numbers */
++ unsigned long min_seq[ANON_AND_FILE];
++ /* the multi-gen LRU lists, lazily sorted on eviction */
++ struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
++ /* the multi-gen LRU sizes, eventually consistent */
++ long nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
++};
++
++void lru_gen_init_lruvec(struct lruvec *lruvec);
++
++#ifdef CONFIG_MEMCG
++void lru_gen_init_memcg(struct mem_cgroup *memcg);
++void lru_gen_exit_memcg(struct mem_cgroup *memcg);
++#endif
++
++#else /* !CONFIG_LRU_GEN */
++
++static inline void lru_gen_init_lruvec(struct lruvec *lruvec)
++{
++}
++
++#ifdef CONFIG_MEMCG
++static inline void lru_gen_init_memcg(struct mem_cgroup *memcg)
++{
++}
++
++static inline void lru_gen_exit_memcg(struct mem_cgroup *memcg)
++{
++}
++#endif
++
++#endif /* CONFIG_LRU_GEN */
++
+ struct lruvec {
+ struct list_head lists[NR_LRU_LISTS];
+ /* per lruvec lru_lock for memcg */
+@@ -311,6 +407,10 @@ struct lruvec {
+ unsigned long refaults[ANON_AND_FILE];
+ /* Various lruvec state flags (enum lruvec_flags) */
+ unsigned long flags;
++#ifdef CONFIG_LRU_GEN
++ /* evictable pages divided into generations */
++ struct lru_gen_struct lrugen;
++#endif
+ #ifdef CONFIG_MEMCG
+ struct pglist_data *pgdat;
+ #endif
+--- a/include/linux/page-flags-layout.h
++++ b/include/linux/page-flags-layout.h
+@@ -55,7 +55,8 @@
+ #define SECTIONS_WIDTH 0
+ #endif
+
+-#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS
++#if ZONES_WIDTH + LRU_GEN_WIDTH + SECTIONS_WIDTH + NODES_SHIFT \
++ <= BITS_PER_LONG - NR_PAGEFLAGS
+ #define NODES_WIDTH NODES_SHIFT
+ #elif defined(CONFIG_SPARSEMEM_VMEMMAP)
+ #error "Vmemmap: No space for nodes field in page flags"
+@@ -89,8 +90,8 @@
+ #define LAST_CPUPID_SHIFT 0
+ #endif
+
+-#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + KASAN_TAG_WIDTH + LAST_CPUPID_SHIFT \
+- <= BITS_PER_LONG - NR_PAGEFLAGS
++#if ZONES_WIDTH + LRU_GEN_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \
++ KASAN_TAG_WIDTH + LAST_CPUPID_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS
+ #define LAST_CPUPID_WIDTH LAST_CPUPID_SHIFT
+ #else
+ #define LAST_CPUPID_WIDTH 0
+@@ -100,10 +101,12 @@
+ #define LAST_CPUPID_NOT_IN_PAGE_FLAGS
+ #endif
+
+-#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + KASAN_TAG_WIDTH + LAST_CPUPID_WIDTH \
+- > BITS_PER_LONG - NR_PAGEFLAGS
++#if ZONES_WIDTH + LRU_GEN_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \
++ KASAN_TAG_WIDTH + LAST_CPUPID_WIDTH > BITS_PER_LONG - NR_PAGEFLAGS
+ #error "Not enough bits in page flags"
+ #endif
+
++#define LRU_REFS_WIDTH 0
++
+ #endif
+ #endif /* _LINUX_PAGE_FLAGS_LAYOUT */
+--- a/include/linux/page-flags.h
++++ b/include/linux/page-flags.h
+@@ -845,7 +845,7 @@ static inline void ClearPageSlabPfmemall
+ 1UL << PG_private | 1UL << PG_private_2 | \
+ 1UL << PG_writeback | 1UL << PG_reserved | \
+ 1UL << PG_slab | 1UL << PG_active | \
+- 1UL << PG_unevictable | __PG_MLOCKED)
++ 1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK)
+
+ /*
+ * Flags checked when a page is prepped for return by the page allocator.
+@@ -856,7 +856,7 @@ static inline void ClearPageSlabPfmemall
+ * alloc-free cycle to prevent from reusing the page.
+ */
+ #define PAGE_FLAGS_CHECK_AT_PREP \
+- (PAGEFLAGS_MASK & ~__PG_HWPOISON)
++ ((PAGEFLAGS_MASK & ~__PG_HWPOISON) | LRU_GEN_MASK | LRU_REFS_MASK)
+
+ #define PAGE_FLAGS_PRIVATE \
+ (1UL << PG_private | 1UL << PG_private_2)
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -911,6 +911,10 @@ struct task_struct {
+ #ifdef CONFIG_MEMCG
+ unsigned in_user_fault:1;
+ #endif
++#ifdef CONFIG_LRU_GEN
++ /* whether the LRU algorithm may apply to this access */
++ unsigned in_lru_fault:1;
++#endif
+ #ifdef CONFIG_COMPAT_BRK
+ unsigned brk_randomized:1;
+ #endif
+--- a/kernel/bounds.c
++++ b/kernel/bounds.c
+@@ -22,6 +22,11 @@ int main(void)
+ DEFINE(NR_CPUS_BITS, ilog2(CONFIG_NR_CPUS));
+ #endif
+ DEFINE(SPINLOCK_SIZE, sizeof(spinlock_t));
++#ifdef CONFIG_LRU_GEN
++ DEFINE(LRU_GEN_WIDTH, order_base_2(MAX_NR_GENS + 1));
++#else
++ DEFINE(LRU_GEN_WIDTH, 0);
++#endif
+ /* End of constants */
+
+ return 0;
+--- a/mm/Kconfig
++++ b/mm/Kconfig
+@@ -897,6 +897,14 @@ config IO_MAPPING
+ config SECRETMEM
+ def_bool ARCH_HAS_SET_DIRECT_MAP && !EMBEDDED
+
++config LRU_GEN
++ bool "Multi-Gen LRU"
++ depends on MMU
++ # make sure page->flags has enough spare bits
++ depends on 64BIT || !SPARSEMEM || SPARSEMEM_VMEMMAP
++ help
++ A high performance LRU implementation to overcommit memory.
++
+ source "mm/damon/Kconfig"
+
+ endmenu
+--- a/mm/huge_memory.c
++++ b/mm/huge_memory.c
+@@ -2366,7 +2366,8 @@ static void __split_huge_page_tail(struc
+ #ifdef CONFIG_64BIT
+ (1L << PG_arch_2) |
+ #endif
+- (1L << PG_dirty)));
++ (1L << PG_dirty) |
++ LRU_GEN_MASK | LRU_REFS_MASK));
+
+ /* ->mapping in first tail page is compound_mapcount */
+ VM_BUG_ON_PAGE(tail > 2 && page_tail->mapping != TAIL_MAPPING,
+--- a/mm/memcontrol.c
++++ b/mm/memcontrol.c
+@@ -5178,6 +5178,7 @@ static void __mem_cgroup_free(struct mem
+
+ static void mem_cgroup_free(struct mem_cgroup *memcg)
+ {
++ lru_gen_exit_memcg(memcg);
+ memcg_wb_domain_exit(memcg);
+ __mem_cgroup_free(memcg);
+ }
+@@ -5241,6 +5242,7 @@ static struct mem_cgroup *mem_cgroup_all
+ memcg->deferred_split_queue.split_queue_len = 0;
+ #endif
+ idr_replace(&mem_cgroup_idr, memcg, memcg->id.id);
++ lru_gen_init_memcg(memcg);
+ return memcg;
+ fail:
+ mem_cgroup_id_remove(memcg);
+--- a/mm/memory.c
++++ b/mm/memory.c
+@@ -4792,6 +4792,27 @@ static inline void mm_account_fault(stru
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, address);
+ }
+
++#ifdef CONFIG_LRU_GEN
++static void lru_gen_enter_fault(struct vm_area_struct *vma)
++{
++ /* the LRU algorithm doesn't apply to sequential or random reads */
++ current->in_lru_fault = !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ));
++}
++
++static void lru_gen_exit_fault(void)
++{
++ current->in_lru_fault = false;
++}
++#else
++static void lru_gen_enter_fault(struct vm_area_struct *vma)
++{
++}
++
++static void lru_gen_exit_fault(void)
++{
++}
++#endif /* CONFIG_LRU_GEN */
++
+ /*
+ * By the time we get here, we already hold the mm semaphore
+ *
+@@ -4823,11 +4844,15 @@ vm_fault_t handle_mm_fault(struct vm_are
+ if (flags & FAULT_FLAG_USER)
+ mem_cgroup_enter_user_fault();
+
++ lru_gen_enter_fault(vma);
++
+ if (unlikely(is_vm_hugetlb_page(vma)))
+ ret = hugetlb_fault(vma->vm_mm, vma, address, flags);
+ else
+ ret = __handle_mm_fault(vma, address, flags);
+
++ lru_gen_exit_fault();
++
+ if (flags & FAULT_FLAG_USER) {
+ mem_cgroup_exit_user_fault();
+ /*
+--- a/mm/mm_init.c
++++ b/mm/mm_init.c
+@@ -65,14 +65,16 @@ void __init mminit_verify_pageflags_layo
+
+ shift = 8 * sizeof(unsigned long);
+ width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH
+- - LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH;
++ - LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH - LRU_GEN_WIDTH - LRU_REFS_WIDTH;
+ mminit_dprintk(MMINIT_TRACE, "pageflags_layout_widths",
+- "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Flags %d\n",
++ "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Gen %d Tier %d Flags %d\n",
+ SECTIONS_WIDTH,
+ NODES_WIDTH,
+ ZONES_WIDTH,
+ LAST_CPUPID_WIDTH,
+ KASAN_TAG_WIDTH,
++ LRU_GEN_WIDTH,
++ LRU_REFS_WIDTH,
+ NR_PAGEFLAGS);
+ mminit_dprintk(MMINIT_TRACE, "pageflags_layout_shifts",
+ "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d\n",
+--- a/mm/mmzone.c
++++ b/mm/mmzone.c
+@@ -81,6 +81,8 @@ void lruvec_init(struct lruvec *lruvec)
+
+ for_each_lru(lru)
+ INIT_LIST_HEAD(&lruvec->lists[lru]);
++
++ lru_gen_init_lruvec(lruvec);
+ }
+
+ #if defined(CONFIG_NUMA_BALANCING) && !defined(LAST_CPUPID_NOT_IN_PAGE_FLAGS)
+--- a/mm/swap.c
++++ b/mm/swap.c
+@@ -446,6 +446,11 @@ void lru_cache_add(struct page *page)
+ VM_BUG_ON_PAGE(PageActive(page) && PageUnevictable(page), page);
+ VM_BUG_ON_PAGE(PageLRU(page), page);
+
++ /* see the comment in lru_gen_add_page() */
++ if (lru_gen_enabled() && !PageUnevictable(page) &&
++ lru_gen_in_fault() && !(current->flags & PF_MEMALLOC))
++ SetPageActive(page);
++
+ get_page(page);
+ local_lock(&lru_pvecs.lock);
+ pvec = this_cpu_ptr(&lru_pvecs.lru_add);
+@@ -547,7 +552,7 @@ static void lru_deactivate_file_fn(struc
+
+ static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec)
+ {
+- if (PageActive(page) && !PageUnevictable(page)) {
++ if (!PageUnevictable(page) && (PageActive(page) || lru_gen_enabled())) {
+ int nr_pages = thp_nr_pages(page);
+
+ del_page_from_lru_list(page, lruvec);
+@@ -661,7 +666,8 @@ void deactivate_file_page(struct page *p
+ */
+ void deactivate_page(struct page *page)
+ {
+- if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) {
++ if (PageLRU(page) && !PageUnevictable(page) &&
++ (PageActive(page) || lru_gen_enabled())) {
+ struct pagevec *pvec;
+
+ local_lock(&lru_pvecs.lock);
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -2821,6 +2821,81 @@ static bool can_age_anon_pages(struct pg
+ return can_demote(pgdat->node_id, sc);
+ }
+
++#ifdef CONFIG_LRU_GEN
++
++/******************************************************************************
++ * shorthand helpers
++ ******************************************************************************/
++
++#define for_each_gen_type_zone(gen, type, zone) \
++ for ((gen) = 0; (gen) < MAX_NR_GENS; (gen)++) \
++ for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \
++ for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++)
++
++static struct lruvec __maybe_unused *get_lruvec(struct mem_cgroup *memcg, int nid)
++{
++ struct pglist_data *pgdat = NODE_DATA(nid);
++
++#ifdef CONFIG_MEMCG
++ if (memcg) {
++ struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec;
++
++ /* for hotadd_new_pgdat() */
++ if (!lruvec->pgdat)
++ lruvec->pgdat = pgdat;
++
++ return lruvec;
++ }
++#endif
++ VM_WARN_ON_ONCE(!mem_cgroup_disabled());
++
++ return pgdat ? &pgdat->__lruvec : NULL;
++}
++
++/******************************************************************************
++ * initialization
++ ******************************************************************************/
++
++void lru_gen_init_lruvec(struct lruvec *lruvec)
++{
++ int gen, type, zone;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ lrugen->max_seq = MIN_NR_GENS + 1;
++
++ for_each_gen_type_zone(gen, type, zone)
++ INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]);
++}
++
++#ifdef CONFIG_MEMCG
++void lru_gen_init_memcg(struct mem_cgroup *memcg)
++{
++}
++
++void lru_gen_exit_memcg(struct mem_cgroup *memcg)
++{
++ int nid;
++
++ for_each_node(nid) {
++ struct lruvec *lruvec = get_lruvec(memcg, nid);
++
++ VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0,
++ sizeof(lruvec->lrugen.nr_pages)));
++ }
++}
++#endif
++
++static int __init init_lru_gen(void)
++{
++ BUILD_BUG_ON(MIN_NR_GENS + 1 >= MAX_NR_GENS);
++ BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS);
++
++ return 0;
++};
++late_initcall(init_lru_gen);
++
++#endif /* CONFIG_LRU_GEN */
++
+ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
+ {
+ unsigned long nr[NR_LRU_LISTS];
diff --git a/target/linux/generic/backport-6.1/020-v6.1-06-mm-multi-gen-LRU-minimal-implementation.patch b/target/linux/generic/backport-6.1/020-v6.1-06-mm-multi-gen-LRU-minimal-implementation.patch
new file mode 100644
index 0000000000..f8a7d9bd7f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-06-mm-multi-gen-LRU-minimal-implementation.patch
@@ -0,0 +1,1447 @@
+From b564b9471cd60ef1ee3961a224898ce4a9620d84 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:03 -0600
+Subject: [PATCH 06/29] mm: multi-gen LRU: minimal implementation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+To avoid confusion, the terms "promotion" and "demotion" will be applied
+to the multi-gen LRU, as a new convention; the terms "activation" and
+"deactivation" will be applied to the active/inactive LRU, as usual.
+
+The aging produces young generations. Given an lruvec, it increments
+max_seq when max_seq-min_seq+1 approaches MIN_NR_GENS. The aging promotes
+hot pages to the youngest generation when it finds them accessed through
+page tables; the demotion of cold pages happens consequently when it
+increments max_seq. Promotion in the aging path does not involve any LRU
+list operations, only the updates of the gen counter and
+lrugen->nr_pages[]; demotion, unless as the result of the increment of
+max_seq, requires LRU list operations, e.g., lru_deactivate_fn(). The
+aging has the complexity O(nr_hot_pages), since it is only interested in
+hot pages.
+
+The eviction consumes old generations. Given an lruvec, it increments
+min_seq when lrugen->lists[] indexed by min_seq%MAX_NR_GENS becomes empty.
+A feedback loop modeled after the PID controller monitors refaults over
+anon and file types and decides which type to evict when both types are
+available from the same generation.
+
+The protection of pages accessed multiple times through file descriptors
+takes place in the eviction path. Each generation is divided into
+multiple tiers. A page accessed N times through file descriptors is in
+tier order_base_2(N). Tiers do not have dedicated lrugen->lists[], only
+bits in page->flags. The aforementioned feedback loop also monitors
+refaults over all tiers and decides when to protect pages in which tiers
+(N>1), using the first tier (N=0,1) as a baseline. The first tier
+contains single-use unmapped clean pages, which are most likely the best
+choices. In contrast to promotion in the aging path, the protection of a
+page in the eviction path is achieved by moving this page to the next
+generation, i.e., min_seq+1, if the feedback loop decides so. This
+approach has the following advantages:
+
+1. It removes the cost of activation in the buffered access path by
+ inferring whether pages accessed multiple times through file
+ descriptors are statistically hot and thus worth protecting in the
+ eviction path.
+2. It takes pages accessed through page tables into account and avoids
+ overprotecting pages accessed multiple times through file
+ descriptors. (Pages accessed through page tables are in the first
+ tier, since N=0.)
+3. More tiers provide better protection for pages accessed more than
+ twice through file descriptors, when under heavy buffered I/O
+ workloads.
+
+Server benchmark results:
+ Single workload:
+ fio (buffered I/O): +[30, 32]%
+ IOPS BW
+ 5.19-rc1: 2673k 10.2GiB/s
+ patch1-6: 3491k 13.3GiB/s
+
+ Single workload:
+ memcached (anon): -[4, 6]%
+ Ops/sec KB/sec
+ 5.19-rc1: 1161501.04 45177.25
+ patch1-6: 1106168.46 43025.04
+
+ Configurations:
+ CPU: two Xeon 6154
+ Mem: total 256G
+
+ Node 1 was only used as a ram disk to reduce the variance in the
+ results.
+
+ patch drivers/block/brd.c <<EOF
+ 99,100c99,100
+ < gfp_flags = GFP_NOIO | __GFP_ZERO | __GFP_HIGHMEM;
+ < page = alloc_page(gfp_flags);
+ ---
+ > gfp_flags = GFP_NOIO | __GFP_ZERO | __GFP_HIGHMEM | __GFP_THISNODE;
+ > page = alloc_pages_node(1, gfp_flags, 0);
+ EOF
+
+ cat >>/etc/systemd/system.conf <<EOF
+ CPUAffinity=numa
+ NUMAPolicy=bind
+ NUMAMask=0
+ EOF
+
+ cat >>/etc/memcached.conf <<EOF
+ -m 184320
+ -s /var/run/memcached/memcached.sock
+ -a 0766
+ -t 36
+ -B binary
+ EOF
+
+ cat fio.sh
+ modprobe brd rd_nr=1 rd_size=113246208
+ swapoff -a
+ mkfs.ext4 /dev/ram0
+ mount -t ext4 /dev/ram0 /mnt
+
+ mkdir /sys/fs/cgroup/user.slice/test
+ echo 38654705664 >/sys/fs/cgroup/user.slice/test/memory.max
+ echo $$ >/sys/fs/cgroup/user.slice/test/cgroup.procs
+ fio -name=mglru --numjobs=72 --directory=/mnt --size=1408m \
+ --buffered=1 --ioengine=io_uring --iodepth=128 \
+ --iodepth_batch_submit=32 --iodepth_batch_complete=32 \
+ --rw=randread --random_distribution=random --norandommap \
+ --time_based --ramp_time=10m --runtime=5m --group_reporting
+
+ cat memcached.sh
+ modprobe brd rd_nr=1 rd_size=113246208
+ swapoff -a
+ mkswap /dev/ram0
+ swapon /dev/ram0
+
+ memtier_benchmark -S /var/run/memcached/memcached.sock \
+ -P memcache_binary -n allkeys --key-minimum=1 \
+ --key-maximum=65000000 --key-pattern=P:P -c 1 -t 36 \
+ --ratio 1:0 --pipeline 8 -d 2000
+
+ memtier_benchmark -S /var/run/memcached/memcached.sock \
+ -P memcache_binary -n allkeys --key-minimum=1 \
+ --key-maximum=65000000 --key-pattern=R:R -c 1 -t 36 \
+ --ratio 0:1 --pipeline 8 --randomize --distinct-client-seed
+
+Client benchmark results:
+ kswapd profiles:
+ 5.19-rc1
+ 40.33% page_vma_mapped_walk (overhead)
+ 21.80% lzo1x_1_do_compress (real work)
+ 7.53% do_raw_spin_lock
+ 3.95% _raw_spin_unlock_irq
+ 2.52% vma_interval_tree_iter_next
+ 2.37% page_referenced_one
+ 2.28% vma_interval_tree_subtree_search
+ 1.97% anon_vma_interval_tree_iter_first
+ 1.60% ptep_clear_flush
+ 1.06% __zram_bvec_write
+
+ patch1-6
+ 39.03% lzo1x_1_do_compress (real work)
+ 18.47% page_vma_mapped_walk (overhead)
+ 6.74% _raw_spin_unlock_irq
+ 3.97% do_raw_spin_lock
+ 2.49% ptep_clear_flush
+ 2.48% anon_vma_interval_tree_iter_first
+ 1.92% page_referenced_one
+ 1.88% __zram_bvec_write
+ 1.48% memmove
+ 1.31% vma_interval_tree_iter_next
+
+ Configurations:
+ CPU: single Snapdragon 7c
+ Mem: total 4G
+
+ ChromeOS MemoryPressure [1]
+
+[1] https://chromium.googlesource.com/chromiumos/platform/tast-tests/
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-7-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Barry Song <baohua@kernel.org>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/mm_inline.h | 36 ++
+ include/linux/mmzone.h | 41 ++
+ include/linux/page-flags-layout.h | 5 +-
+ kernel/bounds.c | 2 +
+ mm/Kconfig | 11 +
+ mm/swap.c | 39 ++
+ mm/vmscan.c | 792 +++++++++++++++++++++++++++++-
+ mm/workingset.c | 110 ++++-
+ 8 files changed, 1025 insertions(+), 11 deletions(-)
+
+--- a/include/linux/mm_inline.h
++++ b/include/linux/mm_inline.h
+@@ -106,6 +106,33 @@ static inline int lru_gen_from_seq(unsig
+ return seq % MAX_NR_GENS;
+ }
+
++static inline int lru_hist_from_seq(unsigned long seq)
++{
++ return seq % NR_HIST_GENS;
++}
++
++static inline int lru_tier_from_refs(int refs)
++{
++ VM_WARN_ON_ONCE(refs > BIT(LRU_REFS_WIDTH));
++
++ /* see the comment in page_lru_refs() */
++ return order_base_2(refs + 1);
++}
++
++static inline int page_lru_refs(struct page *page)
++{
++ unsigned long flags = READ_ONCE(page->flags);
++ bool workingset = flags & BIT(PG_workingset);
++
++ /*
++ * Return the number of accesses beyond PG_referenced, i.e., N-1 if the
++ * total number of accesses is N>1, since N=0,1 both map to the first
++ * tier. lru_tier_from_refs() will account for this off-by-one. Also see
++ * the comment on MAX_NR_TIERS.
++ */
++ return ((flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF) + workingset;
++}
++
+ static inline int page_lru_gen(struct page *page)
+ {
+ unsigned long flags = READ_ONCE(page->flags);
+@@ -158,6 +185,15 @@ static inline void lru_gen_update_size(s
+ __update_lru_size(lruvec, lru, zone, -delta);
+ return;
+ }
++
++ /* promotion */
++ if (!lru_gen_is_active(lruvec, old_gen) && lru_gen_is_active(lruvec, new_gen)) {
++ __update_lru_size(lruvec, lru, zone, -delta);
++ __update_lru_size(lruvec, lru + LRU_ACTIVE, zone, delta);
++ }
++
++ /* demotion requires isolation, e.g., lru_deactivate_fn() */
++ VM_WARN_ON_ONCE(lru_gen_is_active(lruvec, old_gen) && !lru_gen_is_active(lruvec, new_gen));
+ }
+
+ static inline bool lru_gen_add_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -327,6 +327,28 @@ enum lruvec_flags {
+ #define MIN_NR_GENS 2U
+ #define MAX_NR_GENS 4U
+
++/*
++ * Each generation is divided into multiple tiers. A page accessed N times
++ * through file descriptors is in tier order_base_2(N). A page in the first tier
++ * (N=0,1) is marked by PG_referenced unless it was faulted in through page
++ * tables or read ahead. A page in any other tier (N>1) is marked by
++ * PG_referenced and PG_workingset. This implies a minimum of two tiers is
++ * supported without using additional bits in page->flags.
++ *
++ * In contrast to moving across generations which requires the LRU lock, moving
++ * across tiers only involves atomic operations on page->flags and therefore
++ * has a negligible cost in the buffered access path. In the eviction path,
++ * comparisons of refaulted/(evicted+protected) from the first tier and the
++ * rest infer whether pages accessed multiple times through file descriptors
++ * are statistically hot and thus worth protecting.
++ *
++ * MAX_NR_TIERS is set to 4 so that the multi-gen LRU can support twice the
++ * number of categories of the active/inactive LRU when keeping track of
++ * accesses through file descriptors. This uses MAX_NR_TIERS-2 spare bits in
++ * page->flags.
++ */
++#define MAX_NR_TIERS 4U
++
+ #ifndef __GENERATING_BOUNDS_H
+
+ struct lruvec;
+@@ -341,6 +363,16 @@ enum {
+ LRU_GEN_FILE,
+ };
+
++#define MIN_LRU_BATCH BITS_PER_LONG
++#define MAX_LRU_BATCH (MIN_LRU_BATCH * 64)
++
++/* whether to keep historical stats from evicted generations */
++#ifdef CONFIG_LRU_GEN_STATS
++#define NR_HIST_GENS MAX_NR_GENS
++#else
++#define NR_HIST_GENS 1U
++#endif
++
+ /*
+ * The youngest generation number is stored in max_seq for both anon and file
+ * types as they are aged on an equal footing. The oldest generation numbers are
+@@ -363,6 +395,15 @@ struct lru_gen_struct {
+ struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
+ /* the multi-gen LRU sizes, eventually consistent */
+ long nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
++ /* the exponential moving average of refaulted */
++ unsigned long avg_refaulted[ANON_AND_FILE][MAX_NR_TIERS];
++ /* the exponential moving average of evicted+protected */
++ unsigned long avg_total[ANON_AND_FILE][MAX_NR_TIERS];
++ /* the first tier doesn't need protection, hence the minus one */
++ unsigned long protected[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS - 1];
++ /* can be modified without holding the LRU lock */
++ atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
++ atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
+ };
+
+ void lru_gen_init_lruvec(struct lruvec *lruvec);
+--- a/include/linux/page-flags-layout.h
++++ b/include/linux/page-flags-layout.h
+@@ -106,7 +106,10 @@
+ #error "Not enough bits in page flags"
+ #endif
+
+-#define LRU_REFS_WIDTH 0
++/* see the comment on MAX_NR_TIERS */
++#define LRU_REFS_WIDTH min(__LRU_REFS_WIDTH, BITS_PER_LONG - NR_PAGEFLAGS - \
++ ZONES_WIDTH - LRU_GEN_WIDTH - SECTIONS_WIDTH - \
++ NODES_WIDTH - KASAN_TAG_WIDTH - LAST_CPUPID_WIDTH)
+
+ #endif
+ #endif /* _LINUX_PAGE_FLAGS_LAYOUT */
+--- a/kernel/bounds.c
++++ b/kernel/bounds.c
+@@ -24,8 +24,10 @@ int main(void)
+ DEFINE(SPINLOCK_SIZE, sizeof(spinlock_t));
+ #ifdef CONFIG_LRU_GEN
+ DEFINE(LRU_GEN_WIDTH, order_base_2(MAX_NR_GENS + 1));
++ DEFINE(__LRU_REFS_WIDTH, MAX_NR_TIERS - 2);
+ #else
+ DEFINE(LRU_GEN_WIDTH, 0);
++ DEFINE(__LRU_REFS_WIDTH, 0);
+ #endif
+ /* End of constants */
+
+--- a/mm/Kconfig
++++ b/mm/Kconfig
+@@ -897,6 +897,7 @@ config IO_MAPPING
+ config SECRETMEM
+ def_bool ARCH_HAS_SET_DIRECT_MAP && !EMBEDDED
+
++# multi-gen LRU {
+ config LRU_GEN
+ bool "Multi-Gen LRU"
+ depends on MMU
+@@ -905,6 +906,16 @@ config LRU_GEN
+ help
+ A high performance LRU implementation to overcommit memory.
+
++config LRU_GEN_STATS
++ bool "Full stats for debugging"
++ depends on LRU_GEN
++ help
++ Do not enable this option unless you plan to look at historical stats
++ from evicted generations for debugging purpose.
++
++ This option has a per-memcg and per-node memory overhead.
++# }
++
+ source "mm/damon/Kconfig"
+
+ endmenu
+--- a/mm/swap.c
++++ b/mm/swap.c
+@@ -389,6 +389,40 @@ static void __lru_cache_activate_page(st
+ local_unlock(&lru_pvecs.lock);
+ }
+
++#ifdef CONFIG_LRU_GEN
++static void page_inc_refs(struct page *page)
++{
++ unsigned long new_flags, old_flags = READ_ONCE(page->flags);
++
++ if (PageUnevictable(page))
++ return;
++
++ if (!PageReferenced(page)) {
++ SetPageReferenced(page);
++ return;
++ }
++
++ if (!PageWorkingset(page)) {
++ SetPageWorkingset(page);
++ return;
++ }
++
++ /* see the comment on MAX_NR_TIERS */
++ do {
++ new_flags = old_flags & LRU_REFS_MASK;
++ if (new_flags == LRU_REFS_MASK)
++ break;
++
++ new_flags += BIT(LRU_REFS_PGOFF);
++ new_flags |= old_flags & ~LRU_REFS_MASK;
++ } while (!try_cmpxchg(&page->flags, &old_flags, new_flags));
++}
++#else
++static void page_inc_refs(struct page *page)
++{
++}
++#endif /* CONFIG_LRU_GEN */
++
+ /*
+ * Mark a page as having seen activity.
+ *
+@@ -403,6 +437,11 @@ void mark_page_accessed(struct page *pag
+ {
+ page = compound_head(page);
+
++ if (lru_gen_enabled()) {
++ page_inc_refs(page);
++ return;
++ }
++
+ if (!PageReferenced(page)) {
+ SetPageReferenced(page);
+ } else if (PageUnevictable(page)) {
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -1142,9 +1142,11 @@ static int __remove_mapping(struct addre
+
+ if (PageSwapCache(page)) {
+ swp_entry_t swap = { .val = page_private(page) };
+- mem_cgroup_swapout(page, swap);
++
++ /* get a shadow entry before mem_cgroup_swapout() clears page_memcg() */
+ if (reclaimed && !mapping_exiting(mapping))
+ shadow = workingset_eviction(page, target_memcg);
++ mem_cgroup_swapout(page, swap);
+ __delete_from_swap_cache(page, swap, shadow);
+ xa_unlock_irq(&mapping->i_pages);
+ put_swap_page(page, swap);
+@@ -2502,6 +2504,9 @@ static void prepare_scan_count(pg_data_t
+ unsigned long file;
+ struct lruvec *target_lruvec;
+
++ if (lru_gen_enabled())
++ return;
++
+ target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat);
+
+ /*
+@@ -2827,6 +2832,17 @@ static bool can_age_anon_pages(struct pg
+ * shorthand helpers
+ ******************************************************************************/
+
++#define LRU_REFS_FLAGS (BIT(PG_referenced) | BIT(PG_workingset))
++
++#define DEFINE_MAX_SEQ(lruvec) \
++ unsigned long max_seq = READ_ONCE((lruvec)->lrugen.max_seq)
++
++#define DEFINE_MIN_SEQ(lruvec) \
++ unsigned long min_seq[ANON_AND_FILE] = { \
++ READ_ONCE((lruvec)->lrugen.min_seq[LRU_GEN_ANON]), \
++ READ_ONCE((lruvec)->lrugen.min_seq[LRU_GEN_FILE]), \
++ }
++
+ #define for_each_gen_type_zone(gen, type, zone) \
+ for ((gen) = 0; (gen) < MAX_NR_GENS; (gen)++) \
+ for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \
+@@ -2852,6 +2868,745 @@ static struct lruvec __maybe_unused *get
+ return pgdat ? &pgdat->__lruvec : NULL;
+ }
+
++static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc)
++{
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
++
++ if (!can_demote(pgdat->node_id, sc) &&
++ mem_cgroup_get_nr_swap_pages(memcg) < MIN_LRU_BATCH)
++ return 0;
++
++ return mem_cgroup_swappiness(memcg);
++}
++
++static int get_nr_gens(struct lruvec *lruvec, int type)
++{
++ return lruvec->lrugen.max_seq - lruvec->lrugen.min_seq[type] + 1;
++}
++
++static bool __maybe_unused seq_is_valid(struct lruvec *lruvec)
++{
++ /* see the comment on lru_gen_struct */
++ return get_nr_gens(lruvec, LRU_GEN_FILE) >= MIN_NR_GENS &&
++ get_nr_gens(lruvec, LRU_GEN_FILE) <= get_nr_gens(lruvec, LRU_GEN_ANON) &&
++ get_nr_gens(lruvec, LRU_GEN_ANON) <= MAX_NR_GENS;
++}
++
++/******************************************************************************
++ * refault feedback loop
++ ******************************************************************************/
++
++/*
++ * A feedback loop based on Proportional-Integral-Derivative (PID) controller.
++ *
++ * The P term is refaulted/(evicted+protected) from a tier in the generation
++ * currently being evicted; the I term is the exponential moving average of the
++ * P term over the generations previously evicted, using the smoothing factor
++ * 1/2; the D term isn't supported.
++ *
++ * The setpoint (SP) is always the first tier of one type; the process variable
++ * (PV) is either any tier of the other type or any other tier of the same
++ * type.
++ *
++ * The error is the difference between the SP and the PV; the correction is to
++ * turn off protection when SP>PV or turn on protection when SP<PV.
++ *
++ * For future optimizations:
++ * 1. The D term may discount the other two terms over time so that long-lived
++ * generations can resist stale information.
++ */
++struct ctrl_pos {
++ unsigned long refaulted;
++ unsigned long total;
++ int gain;
++};
++
++static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
++ struct ctrl_pos *pos)
++{
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ int hist = lru_hist_from_seq(lrugen->min_seq[type]);
++
++ pos->refaulted = lrugen->avg_refaulted[type][tier] +
++ atomic_long_read(&lrugen->refaulted[hist][type][tier]);
++ pos->total = lrugen->avg_total[type][tier] +
++ atomic_long_read(&lrugen->evicted[hist][type][tier]);
++ if (tier)
++ pos->total += lrugen->protected[hist][type][tier - 1];
++ pos->gain = gain;
++}
++
++static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover)
++{
++ int hist, tier;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ bool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS > 1;
++ unsigned long seq = carryover ? lrugen->min_seq[type] : lrugen->max_seq + 1;
++
++ lockdep_assert_held(&lruvec->lru_lock);
++
++ if (!carryover && !clear)
++ return;
++
++ hist = lru_hist_from_seq(seq);
++
++ for (tier = 0; tier < MAX_NR_TIERS; tier++) {
++ if (carryover) {
++ unsigned long sum;
++
++ sum = lrugen->avg_refaulted[type][tier] +
++ atomic_long_read(&lrugen->refaulted[hist][type][tier]);
++ WRITE_ONCE(lrugen->avg_refaulted[type][tier], sum / 2);
++
++ sum = lrugen->avg_total[type][tier] +
++ atomic_long_read(&lrugen->evicted[hist][type][tier]);
++ if (tier)
++ sum += lrugen->protected[hist][type][tier - 1];
++ WRITE_ONCE(lrugen->avg_total[type][tier], sum / 2);
++ }
++
++ if (clear) {
++ atomic_long_set(&lrugen->refaulted[hist][type][tier], 0);
++ atomic_long_set(&lrugen->evicted[hist][type][tier], 0);
++ if (tier)
++ WRITE_ONCE(lrugen->protected[hist][type][tier - 1], 0);
++ }
++ }
++}
++
++static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)
++{
++ /*
++ * Return true if the PV has a limited number of refaults or a lower
++ * refaulted/total than the SP.
++ */
++ return pv->refaulted < MIN_LRU_BATCH ||
++ pv->refaulted * (sp->total + MIN_LRU_BATCH) * sp->gain <=
++ (sp->refaulted + 1) * pv->total * pv->gain;
++}
++
++/******************************************************************************
++ * the aging
++ ******************************************************************************/
++
++/* protect pages accessed multiple times through file descriptors */
++static int page_inc_gen(struct lruvec *lruvec, struct page *page, bool reclaiming)
++{
++ int type = page_is_file_lru(page);
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
++ unsigned long new_flags, old_flags = READ_ONCE(page->flags);
++
++ VM_WARN_ON_ONCE_PAGE(!(old_flags & LRU_GEN_MASK), page);
++
++ do {
++ new_gen = (old_gen + 1) % MAX_NR_GENS;
++
++ new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_MASK | LRU_REFS_FLAGS);
++ new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF;
++ /* for end_page_writeback() */
++ if (reclaiming)
++ new_flags |= BIT(PG_reclaim);
++ } while (!try_cmpxchg(&page->flags, &old_flags, new_flags));
++
++ lru_gen_update_size(lruvec, page, old_gen, new_gen);
++
++ return new_gen;
++}
++
++static void inc_min_seq(struct lruvec *lruvec, int type)
++{
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ reset_ctrl_pos(lruvec, type, true);
++ WRITE_ONCE(lrugen->min_seq[type], lrugen->min_seq[type] + 1);
++}
++
++static bool try_to_inc_min_seq(struct lruvec *lruvec, bool can_swap)
++{
++ int gen, type, zone;
++ bool success = false;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ DEFINE_MIN_SEQ(lruvec);
++
++ VM_WARN_ON_ONCE(!seq_is_valid(lruvec));
++
++ /* find the oldest populated generation */
++ for (type = !can_swap; type < ANON_AND_FILE; type++) {
++ while (min_seq[type] + MIN_NR_GENS <= lrugen->max_seq) {
++ gen = lru_gen_from_seq(min_seq[type]);
++
++ for (zone = 0; zone < MAX_NR_ZONES; zone++) {
++ if (!list_empty(&lrugen->lists[gen][type][zone]))
++ goto next;
++ }
++
++ min_seq[type]++;
++ }
++next:
++ ;
++ }
++
++ /* see the comment on lru_gen_struct */
++ if (can_swap) {
++ min_seq[LRU_GEN_ANON] = min(min_seq[LRU_GEN_ANON], min_seq[LRU_GEN_FILE]);
++ min_seq[LRU_GEN_FILE] = max(min_seq[LRU_GEN_ANON], lrugen->min_seq[LRU_GEN_FILE]);
++ }
++
++ for (type = !can_swap; type < ANON_AND_FILE; type++) {
++ if (min_seq[type] == lrugen->min_seq[type])
++ continue;
++
++ reset_ctrl_pos(lruvec, type, true);
++ WRITE_ONCE(lrugen->min_seq[type], min_seq[type]);
++ success = true;
++ }
++
++ return success;
++}
++
++static void inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, bool can_swap)
++{
++ int prev, next;
++ int type, zone;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ spin_lock_irq(&lruvec->lru_lock);
++
++ VM_WARN_ON_ONCE(!seq_is_valid(lruvec));
++
++ if (max_seq != lrugen->max_seq)
++ goto unlock;
++
++ for (type = ANON_AND_FILE - 1; type >= 0; type--) {
++ if (get_nr_gens(lruvec, type) != MAX_NR_GENS)
++ continue;
++
++ VM_WARN_ON_ONCE(type == LRU_GEN_FILE || can_swap);
++
++ inc_min_seq(lruvec, type);
++ }
++
++ /*
++ * Update the active/inactive LRU sizes for compatibility. Both sides of
++ * the current max_seq need to be covered, since max_seq+1 can overlap
++ * with min_seq[LRU_GEN_ANON] if swapping is constrained. And if they do
++ * overlap, cold/hot inversion happens.
++ */
++ prev = lru_gen_from_seq(lrugen->max_seq - 1);
++ next = lru_gen_from_seq(lrugen->max_seq + 1);
++
++ for (type = 0; type < ANON_AND_FILE; type++) {
++ for (zone = 0; zone < MAX_NR_ZONES; zone++) {
++ enum lru_list lru = type * LRU_INACTIVE_FILE;
++ long delta = lrugen->nr_pages[prev][type][zone] -
++ lrugen->nr_pages[next][type][zone];
++
++ if (!delta)
++ continue;
++
++ __update_lru_size(lruvec, lru, zone, delta);
++ __update_lru_size(lruvec, lru + LRU_ACTIVE, zone, -delta);
++ }
++ }
++
++ for (type = 0; type < ANON_AND_FILE; type++)
++ reset_ctrl_pos(lruvec, type, false);
++
++ /* make sure preceding modifications appear */
++ smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1);
++unlock:
++ spin_unlock_irq(&lruvec->lru_lock);
++}
++
++static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsigned long *min_seq,
++ struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
++{
++ int gen, type, zone;
++ unsigned long old = 0;
++ unsigned long young = 0;
++ unsigned long total = 0;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++
++ for (type = !can_swap; type < ANON_AND_FILE; type++) {
++ unsigned long seq;
++
++ for (seq = min_seq[type]; seq <= max_seq; seq++) {
++ unsigned long size = 0;
++
++ gen = lru_gen_from_seq(seq);
++
++ for (zone = 0; zone < MAX_NR_ZONES; zone++)
++ size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
++
++ total += size;
++ if (seq == max_seq)
++ young += size;
++ else if (seq + MIN_NR_GENS == max_seq)
++ old += size;
++ }
++ }
++
++ /* try to scrape all its memory if this memcg was deleted */
++ *nr_to_scan = mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
++
++ /*
++ * The aging tries to be lazy to reduce the overhead, while the eviction
++ * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
++ * ideal number of generations is MIN_NR_GENS+1.
++ */
++ if (min_seq[!can_swap] + MIN_NR_GENS > max_seq)
++ return true;
++ if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
++ return false;
++
++ /*
++ * It's also ideal to spread pages out evenly, i.e., 1/(MIN_NR_GENS+1)
++ * of the total number of pages for each generation. A reasonable range
++ * for this average portion is [1/MIN_NR_GENS, 1/(MIN_NR_GENS+2)]. The
++ * aging cares about the upper bound of hot pages, while the eviction
++ * cares about the lower bound of cold pages.
++ */
++ if (young * MIN_NR_GENS > total)
++ return true;
++ if (old * (MIN_NR_GENS + 2) < total)
++ return true;
++
++ return false;
++}
++
++static void age_lruvec(struct lruvec *lruvec, struct scan_control *sc)
++{
++ bool need_aging;
++ unsigned long nr_to_scan;
++ int swappiness = get_swappiness(lruvec, sc);
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ DEFINE_MAX_SEQ(lruvec);
++ DEFINE_MIN_SEQ(lruvec);
++
++ VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
++
++ mem_cgroup_calculate_protection(NULL, memcg);
++
++ if (mem_cgroup_below_min(memcg))
++ return;
++
++ need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan);
++ if (need_aging)
++ inc_max_seq(lruvec, max_seq, swappiness);
++}
++
++static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)
++{
++ struct mem_cgroup *memcg;
++
++ VM_WARN_ON_ONCE(!current_is_kswapd());
++
++ memcg = mem_cgroup_iter(NULL, NULL, NULL);
++ do {
++ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat);
++
++ age_lruvec(lruvec, sc);
++
++ cond_resched();
++ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
++}
++
++/******************************************************************************
++ * the eviction
++ ******************************************************************************/
++
++static bool sort_page(struct lruvec *lruvec, struct page *page, int tier_idx)
++{
++ bool success;
++ int gen = page_lru_gen(page);
++ int type = page_is_file_lru(page);
++ int zone = page_zonenum(page);
++ int delta = thp_nr_pages(page);
++ int refs = page_lru_refs(page);
++ int tier = lru_tier_from_refs(refs);
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ VM_WARN_ON_ONCE_PAGE(gen >= MAX_NR_GENS, page);
++
++ /* unevictable */
++ if (!page_evictable(page)) {
++ success = lru_gen_del_page(lruvec, page, true);
++ VM_WARN_ON_ONCE_PAGE(!success, page);
++ SetPageUnevictable(page);
++ add_page_to_lru_list(page, lruvec);
++ __count_vm_events(UNEVICTABLE_PGCULLED, delta);
++ return true;
++ }
++
++ /* dirty lazyfree */
++ if (type == LRU_GEN_FILE && PageAnon(page) && PageDirty(page)) {
++ success = lru_gen_del_page(lruvec, page, true);
++ VM_WARN_ON_ONCE_PAGE(!success, page);
++ SetPageSwapBacked(page);
++ add_page_to_lru_list_tail(page, lruvec);
++ return true;
++ }
++
++ /* protected */
++ if (tier > tier_idx) {
++ int hist = lru_hist_from_seq(lrugen->min_seq[type]);
++
++ gen = page_inc_gen(lruvec, page, false);
++ list_move_tail(&page->lru, &lrugen->lists[gen][type][zone]);
++
++ WRITE_ONCE(lrugen->protected[hist][type][tier - 1],
++ lrugen->protected[hist][type][tier - 1] + delta);
++ __mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta);
++ return true;
++ }
++
++ /* waiting for writeback */
++ if (PageLocked(page) || PageWriteback(page) ||
++ (type == LRU_GEN_FILE && PageDirty(page))) {
++ gen = page_inc_gen(lruvec, page, true);
++ list_move(&page->lru, &lrugen->lists[gen][type][zone]);
++ return true;
++ }
++
++ return false;
++}
++
++static bool isolate_page(struct lruvec *lruvec, struct page *page, struct scan_control *sc)
++{
++ bool success;
++
++ /* unmapping inhibited */
++ if (!sc->may_unmap && page_mapped(page))
++ return false;
++
++ /* swapping inhibited */
++ if (!(sc->may_writepage && (sc->gfp_mask & __GFP_IO)) &&
++ (PageDirty(page) ||
++ (PageAnon(page) && !PageSwapCache(page))))
++ return false;
++
++ /* raced with release_pages() */
++ if (!get_page_unless_zero(page))
++ return false;
++
++ /* raced with another isolation */
++ if (!TestClearPageLRU(page)) {
++ put_page(page);
++ return false;
++ }
++
++ /* see the comment on MAX_NR_TIERS */
++ if (!PageReferenced(page))
++ set_mask_bits(&page->flags, LRU_REFS_MASK | LRU_REFS_FLAGS, 0);
++
++ /* for shrink_page_list() */
++ ClearPageReclaim(page);
++ ClearPageReferenced(page);
++
++ success = lru_gen_del_page(lruvec, page, true);
++ VM_WARN_ON_ONCE_PAGE(!success, page);
++
++ return true;
++}
++
++static int scan_pages(struct lruvec *lruvec, struct scan_control *sc,
++ int type, int tier, struct list_head *list)
++{
++ int gen, zone;
++ enum vm_event_item item;
++ int sorted = 0;
++ int scanned = 0;
++ int isolated = 0;
++ int remaining = MAX_LRU_BATCH;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++
++ VM_WARN_ON_ONCE(!list_empty(list));
++
++ if (get_nr_gens(lruvec, type) == MIN_NR_GENS)
++ return 0;
++
++ gen = lru_gen_from_seq(lrugen->min_seq[type]);
++
++ for (zone = sc->reclaim_idx; zone >= 0; zone--) {
++ LIST_HEAD(moved);
++ int skipped = 0;
++ struct list_head *head = &lrugen->lists[gen][type][zone];
++
++ while (!list_empty(head)) {
++ struct page *page = lru_to_page(head);
++ int delta = thp_nr_pages(page);
++
++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page);
++ VM_WARN_ON_ONCE_PAGE(PageActive(page), page);
++ VM_WARN_ON_ONCE_PAGE(page_is_file_lru(page) != type, page);
++ VM_WARN_ON_ONCE_PAGE(page_zonenum(page) != zone, page);
++
++ scanned += delta;
++
++ if (sort_page(lruvec, page, tier))
++ sorted += delta;
++ else if (isolate_page(lruvec, page, sc)) {
++ list_add(&page->lru, list);
++ isolated += delta;
++ } else {
++ list_move(&page->lru, &moved);
++ skipped += delta;
++ }
++
++ if (!--remaining || max(isolated, skipped) >= MIN_LRU_BATCH)
++ break;
++ }
++
++ if (skipped) {
++ list_splice(&moved, head);
++ __count_zid_vm_events(PGSCAN_SKIP, zone, skipped);
++ }
++
++ if (!remaining || isolated >= MIN_LRU_BATCH)
++ break;
++ }
++
++ item = current_is_kswapd() ? PGSCAN_KSWAPD : PGSCAN_DIRECT;
++ if (!cgroup_reclaim(sc)) {
++ __count_vm_events(item, isolated);
++ __count_vm_events(PGREFILL, sorted);
++ }
++ __count_memcg_events(memcg, item, isolated);
++ __count_memcg_events(memcg, PGREFILL, sorted);
++ __count_vm_events(PGSCAN_ANON + type, isolated);
++
++ /*
++ * There might not be eligible pages due to reclaim_idx, may_unmap and
++ * may_writepage. Check the remaining to prevent livelock if it's not
++ * making progress.
++ */
++ return isolated || !remaining ? scanned : 0;
++}
++
++static int get_tier_idx(struct lruvec *lruvec, int type)
++{
++ int tier;
++ struct ctrl_pos sp, pv;
++
++ /*
++ * To leave a margin for fluctuations, use a larger gain factor (1:2).
++ * This value is chosen because any other tier would have at least twice
++ * as many refaults as the first tier.
++ */
++ read_ctrl_pos(lruvec, type, 0, 1, &sp);
++ for (tier = 1; tier < MAX_NR_TIERS; tier++) {
++ read_ctrl_pos(lruvec, type, tier, 2, &pv);
++ if (!positive_ctrl_err(&sp, &pv))
++ break;
++ }
++
++ return tier - 1;
++}
++
++static int get_type_to_scan(struct lruvec *lruvec, int swappiness, int *tier_idx)
++{
++ int type, tier;
++ struct ctrl_pos sp, pv;
++ int gain[ANON_AND_FILE] = { swappiness, 200 - swappiness };
++
++ /*
++ * Compare the first tier of anon with that of file to determine which
++ * type to scan. Also need to compare other tiers of the selected type
++ * with the first tier of the other type to determine the last tier (of
++ * the selected type) to evict.
++ */
++ read_ctrl_pos(lruvec, LRU_GEN_ANON, 0, gain[LRU_GEN_ANON], &sp);
++ read_ctrl_pos(lruvec, LRU_GEN_FILE, 0, gain[LRU_GEN_FILE], &pv);
++ type = positive_ctrl_err(&sp, &pv);
++
++ read_ctrl_pos(lruvec, !type, 0, gain[!type], &sp);
++ for (tier = 1; tier < MAX_NR_TIERS; tier++) {
++ read_ctrl_pos(lruvec, type, tier, gain[type], &pv);
++ if (!positive_ctrl_err(&sp, &pv))
++ break;
++ }
++
++ *tier_idx = tier - 1;
++
++ return type;
++}
++
++static int isolate_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness,
++ int *type_scanned, struct list_head *list)
++{
++ int i;
++ int type;
++ int scanned;
++ int tier = -1;
++ DEFINE_MIN_SEQ(lruvec);
++
++ /*
++ * Try to make the obvious choice first. When anon and file are both
++ * available from the same generation, interpret swappiness 1 as file
++ * first and 200 as anon first.
++ */
++ if (!swappiness)
++ type = LRU_GEN_FILE;
++ else if (min_seq[LRU_GEN_ANON] < min_seq[LRU_GEN_FILE])
++ type = LRU_GEN_ANON;
++ else if (swappiness == 1)
++ type = LRU_GEN_FILE;
++ else if (swappiness == 200)
++ type = LRU_GEN_ANON;
++ else
++ type = get_type_to_scan(lruvec, swappiness, &tier);
++
++ for (i = !swappiness; i < ANON_AND_FILE; i++) {
++ if (tier < 0)
++ tier = get_tier_idx(lruvec, type);
++
++ scanned = scan_pages(lruvec, sc, type, tier, list);
++ if (scanned)
++ break;
++
++ type = !type;
++ tier = -1;
++ }
++
++ *type_scanned = type;
++
++ return scanned;
++}
++
++static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness)
++{
++ int type;
++ int scanned;
++ int reclaimed;
++ LIST_HEAD(list);
++ struct page *page;
++ enum vm_event_item item;
++ struct reclaim_stat stat;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
++
++ spin_lock_irq(&lruvec->lru_lock);
++
++ scanned = isolate_pages(lruvec, sc, swappiness, &type, &list);
++
++ scanned += try_to_inc_min_seq(lruvec, swappiness);
++
++ if (get_nr_gens(lruvec, !swappiness) == MIN_NR_GENS)
++ scanned = 0;
++
++ spin_unlock_irq(&lruvec->lru_lock);
++
++ if (list_empty(&list))
++ return scanned;
++
++ reclaimed = shrink_page_list(&list, pgdat, sc, &stat, false);
++
++ list_for_each_entry(page, &list, lru) {
++ /* restore LRU_REFS_FLAGS cleared by isolate_page() */
++ if (PageWorkingset(page))
++ SetPageReferenced(page);
++
++ /* don't add rejected pages to the oldest generation */
++ if (PageReclaim(page) &&
++ (PageDirty(page) || PageWriteback(page)))
++ ClearPageActive(page);
++ else
++ SetPageActive(page);
++ }
++
++ spin_lock_irq(&lruvec->lru_lock);
++
++ move_pages_to_lru(lruvec, &list);
++
++ item = current_is_kswapd() ? PGSTEAL_KSWAPD : PGSTEAL_DIRECT;
++ if (!cgroup_reclaim(sc))
++ __count_vm_events(item, reclaimed);
++ __count_memcg_events(memcg, item, reclaimed);
++ __count_vm_events(PGSTEAL_ANON + type, reclaimed);
++
++ spin_unlock_irq(&lruvec->lru_lock);
++
++ mem_cgroup_uncharge_list(&list);
++ free_unref_page_list(&list);
++
++ sc->nr_reclaimed += reclaimed;
++
++ return scanned;
++}
++
++static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
++ bool can_swap)
++{
++ bool need_aging;
++ unsigned long nr_to_scan;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ DEFINE_MAX_SEQ(lruvec);
++ DEFINE_MIN_SEQ(lruvec);
++
++ if (mem_cgroup_below_min(memcg) ||
++ (mem_cgroup_below_low(memcg) && !sc->memcg_low_reclaim))
++ return 0;
++
++ need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, can_swap, &nr_to_scan);
++ if (!need_aging)
++ return nr_to_scan;
++
++ /* skip the aging path at the default priority */
++ if (sc->priority == DEF_PRIORITY)
++ goto done;
++
++ /* leave the work to lru_gen_age_node() */
++ if (current_is_kswapd())
++ return 0;
++
++ inc_max_seq(lruvec, max_seq, can_swap);
++done:
++ return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
++}
++
++static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
++{
++ struct blk_plug plug;
++ unsigned long scanned = 0;
++
++ lru_add_drain();
++
++ blk_start_plug(&plug);
++
++ while (true) {
++ int delta;
++ int swappiness;
++ unsigned long nr_to_scan;
++
++ if (sc->may_swap)
++ swappiness = get_swappiness(lruvec, sc);
++ else if (!cgroup_reclaim(sc) && get_swappiness(lruvec, sc))
++ swappiness = 1;
++ else
++ swappiness = 0;
++
++ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
++ if (!nr_to_scan)
++ break;
++
++ delta = evict_pages(lruvec, sc, swappiness);
++ if (!delta)
++ break;
++
++ scanned += delta;
++ if (scanned >= nr_to_scan)
++ break;
++
++ cond_resched();
++ }
++
++ blk_finish_plug(&plug);
++}
++
+ /******************************************************************************
+ * initialization
+ ******************************************************************************/
+@@ -2894,6 +3649,16 @@ static int __init init_lru_gen(void)
+ };
+ late_initcall(init_lru_gen);
+
++#else /* !CONFIG_LRU_GEN */
++
++static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)
++{
++}
++
++static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
++{
++}
++
+ #endif /* CONFIG_LRU_GEN */
+
+ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
+@@ -2907,6 +3672,11 @@ static void shrink_lruvec(struct lruvec
+ bool proportional_reclaim;
+ struct blk_plug plug;
+
++ if (lru_gen_enabled()) {
++ lru_gen_shrink_lruvec(lruvec, sc);
++ return;
++ }
++
+ get_scan_count(lruvec, sc, nr);
+
+ /* Record the original scan target for proportional adjustments later */
+@@ -3372,6 +4142,9 @@ static void snapshot_refaults(struct mem
+ struct lruvec *target_lruvec;
+ unsigned long refaults;
+
++ if (lru_gen_enabled())
++ return;
++
+ target_lruvec = mem_cgroup_lruvec(target_memcg, pgdat);
+ refaults = lruvec_page_state(target_lruvec, WORKINGSET_ACTIVATE_ANON);
+ target_lruvec->refaults[0] = refaults;
+@@ -3736,12 +4509,16 @@ unsigned long try_to_free_mem_cgroup_pag
+ }
+ #endif
+
+-static void age_active_anon(struct pglist_data *pgdat,
+- struct scan_control *sc)
++static void kswapd_age_node(struct pglist_data *pgdat, struct scan_control *sc)
+ {
+ struct mem_cgroup *memcg;
+ struct lruvec *lruvec;
+
++ if (lru_gen_enabled()) {
++ lru_gen_age_node(pgdat, sc);
++ return;
++ }
++
+ if (!can_age_anon_pages(pgdat, sc))
+ return;
+
+@@ -4058,12 +4835,11 @@ restart:
+ sc.may_swap = !nr_boost_reclaim;
+
+ /*
+- * Do some background aging of the anon list, to give
+- * pages a chance to be referenced before reclaiming. All
+- * pages are rotated regardless of classzone as this is
+- * about consistent aging.
++ * Do some background aging, to give pages a chance to be
++ * referenced before reclaiming. All pages are rotated
++ * regardless of classzone as this is about consistent aging.
+ */
+- age_active_anon(pgdat, &sc);
++ kswapd_age_node(pgdat, &sc);
+
+ /*
+ * If we're getting trouble reclaiming, start doing writepage
+--- a/mm/workingset.c
++++ b/mm/workingset.c
+@@ -187,7 +187,6 @@ static unsigned int bucket_order __read_
+ static void *pack_shadow(int memcgid, pg_data_t *pgdat, unsigned long eviction,
+ bool workingset)
+ {
+- eviction >>= bucket_order;
+ eviction &= EVICTION_MASK;
+ eviction = (eviction << MEM_CGROUP_ID_SHIFT) | memcgid;
+ eviction = (eviction << NODES_SHIFT) | pgdat->node_id;
+@@ -212,10 +211,107 @@ static void unpack_shadow(void *shadow,
+
+ *memcgidp = memcgid;
+ *pgdat = NODE_DATA(nid);
+- *evictionp = entry << bucket_order;
++ *evictionp = entry;
+ *workingsetp = workingset;
+ }
+
++#ifdef CONFIG_LRU_GEN
++
++static void *lru_gen_eviction(struct page *page)
++{
++ int hist;
++ unsigned long token;
++ unsigned long min_seq;
++ struct lruvec *lruvec;
++ struct lru_gen_struct *lrugen;
++ int type = page_is_file_lru(page);
++ int delta = thp_nr_pages(page);
++ int refs = page_lru_refs(page);
++ int tier = lru_tier_from_refs(refs);
++ struct mem_cgroup *memcg = page_memcg(page);
++ struct pglist_data *pgdat = page_pgdat(page);
++
++ BUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_WIDTH > BITS_PER_LONG - EVICTION_SHIFT);
++
++ lruvec = mem_cgroup_lruvec(memcg, pgdat);
++ lrugen = &lruvec->lrugen;
++ min_seq = READ_ONCE(lrugen->min_seq[type]);
++ token = (min_seq << LRU_REFS_WIDTH) | max(refs - 1, 0);
++
++ hist = lru_hist_from_seq(min_seq);
++ atomic_long_add(delta, &lrugen->evicted[hist][type][tier]);
++
++ return pack_shadow(mem_cgroup_id(memcg), pgdat, token, refs);
++}
++
++static void lru_gen_refault(struct page *page, void *shadow)
++{
++ int hist, tier, refs;
++ int memcg_id;
++ bool workingset;
++ unsigned long token;
++ unsigned long min_seq;
++ struct lruvec *lruvec;
++ struct lru_gen_struct *lrugen;
++ struct mem_cgroup *memcg;
++ struct pglist_data *pgdat;
++ int type = page_is_file_lru(page);
++ int delta = thp_nr_pages(page);
++
++ unpack_shadow(shadow, &memcg_id, &pgdat, &token, &workingset);
++
++ if (pgdat != page_pgdat(page))
++ return;
++
++ rcu_read_lock();
++
++ memcg = page_memcg_rcu(page);
++ if (memcg_id != mem_cgroup_id(memcg))
++ goto unlock;
++
++ lruvec = mem_cgroup_lruvec(memcg, pgdat);
++ lrugen = &lruvec->lrugen;
++
++ min_seq = READ_ONCE(lrugen->min_seq[type]);
++ if ((token >> LRU_REFS_WIDTH) != (min_seq & (EVICTION_MASK >> LRU_REFS_WIDTH)))
++ goto unlock;
++
++ hist = lru_hist_from_seq(min_seq);
++ /* see the comment in page_lru_refs() */
++ refs = (token & (BIT(LRU_REFS_WIDTH) - 1)) + workingset;
++ tier = lru_tier_from_refs(refs);
++
++ atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]);
++ mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + type, delta);
++
++ /*
++ * Count the following two cases as stalls:
++ * 1. For pages accessed through page tables, hotter pages pushed out
++ * hot pages which refaulted immediately.
++ * 2. For pages accessed multiple times through file descriptors,
++ * numbers of accesses might have been out of the range.
++ */
++ if (lru_gen_in_fault() || refs == BIT(LRU_REFS_WIDTH)) {
++ SetPageWorkingset(page);
++ mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + type, delta);
++ }
++unlock:
++ rcu_read_unlock();
++}
++
++#else /* !CONFIG_LRU_GEN */
++
++static void *lru_gen_eviction(struct page *page)
++{
++ return NULL;
++}
++
++static void lru_gen_refault(struct page *page, void *shadow)
++{
++}
++
++#endif /* CONFIG_LRU_GEN */
++
+ /**
+ * workingset_age_nonresident - age non-resident entries as LRU ages
+ * @lruvec: the lruvec that was aged
+@@ -264,10 +360,14 @@ void *workingset_eviction(struct page *p
+ VM_BUG_ON_PAGE(page_count(page), page);
+ VM_BUG_ON_PAGE(!PageLocked(page), page);
+
++ if (lru_gen_enabled())
++ return lru_gen_eviction(page);
++
+ lruvec = mem_cgroup_lruvec(target_memcg, pgdat);
+ /* XXX: target_memcg can be NULL, go through lruvec */
+ memcgid = mem_cgroup_id(lruvec_memcg(lruvec));
+ eviction = atomic_long_read(&lruvec->nonresident_age);
++ eviction >>= bucket_order;
+ workingset_age_nonresident(lruvec, thp_nr_pages(page));
+ return pack_shadow(memcgid, pgdat, eviction, PageWorkingset(page));
+ }
+@@ -296,7 +396,13 @@ void workingset_refault(struct page *pag
+ bool workingset;
+ int memcgid;
+
++ if (lru_gen_enabled()) {
++ lru_gen_refault(page, shadow);
++ return;
++ }
++
+ unpack_shadow(shadow, &memcgid, &pgdat, &eviction, &workingset);
++ eviction <<= bucket_order;
+
+ rcu_read_lock();
+ /*
diff --git a/target/linux/generic/backport-6.1/020-v6.1-07-mm-multi-gen-LRU-exploit-locality-in-rmap.patch b/target/linux/generic/backport-6.1/020-v6.1-07-mm-multi-gen-LRU-exploit-locality-in-rmap.patch
new file mode 100644
index 0000000000..5cd6e03dc6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-07-mm-multi-gen-LRU-exploit-locality-in-rmap.patch
@@ -0,0 +1,491 @@
+From e4277535f6d6708bb19b88c4bad155832671d69b Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:04 -0600
+Subject: [PATCH 07/29] mm: multi-gen LRU: exploit locality in rmap
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Searching the rmap for PTEs mapping each page on an LRU list (to test and
+clear the accessed bit) can be expensive because pages from different VMAs
+(PA space) are not cache friendly to the rmap (VA space). For workloads
+mostly using mapped pages, searching the rmap can incur the highest CPU
+cost in the reclaim path.
+
+This patch exploits spatial locality to reduce the trips into the rmap.
+When shrink_page_list() walks the rmap and finds a young PTE, a new
+function lru_gen_look_around() scans at most BITS_PER_LONG-1 adjacent
+PTEs. On finding another young PTE, it clears the accessed bit and
+updates the gen counter of the page mapped by this PTE to
+(max_seq%MAX_NR_GENS)+1.
+
+Server benchmark results:
+ Single workload:
+ fio (buffered I/O): no change
+
+ Single workload:
+ memcached (anon): +[3, 5]%
+ Ops/sec KB/sec
+ patch1-6: 1106168.46 43025.04
+ patch1-7: 1147696.57 44640.29
+
+ Configurations:
+ no change
+
+Client benchmark results:
+ kswapd profiles:
+ patch1-6
+ 39.03% lzo1x_1_do_compress (real work)
+ 18.47% page_vma_mapped_walk (overhead)
+ 6.74% _raw_spin_unlock_irq
+ 3.97% do_raw_spin_lock
+ 2.49% ptep_clear_flush
+ 2.48% anon_vma_interval_tree_iter_first
+ 1.92% page_referenced_one
+ 1.88% __zram_bvec_write
+ 1.48% memmove
+ 1.31% vma_interval_tree_iter_next
+
+ patch1-7
+ 48.16% lzo1x_1_do_compress (real work)
+ 8.20% page_vma_mapped_walk (overhead)
+ 7.06% _raw_spin_unlock_irq
+ 2.92% ptep_clear_flush
+ 2.53% __zram_bvec_write
+ 2.11% do_raw_spin_lock
+ 2.02% memmove
+ 1.93% lru_gen_look_around
+ 1.56% free_unref_page_list
+ 1.40% memset
+
+ Configurations:
+ no change
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-8-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Acked-by: Barry Song <baohua@kernel.org>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/memcontrol.h | 31 +++++++
+ include/linux/mmzone.h | 6 ++
+ mm/internal.h | 1 +
+ mm/memcontrol.c | 1 +
+ mm/rmap.c | 7 ++
+ mm/swap.c | 4 +-
+ mm/vmscan.c | 184 +++++++++++++++++++++++++++++++++++++
+ 7 files changed, 232 insertions(+), 2 deletions(-)
+
+--- a/include/linux/memcontrol.h
++++ b/include/linux/memcontrol.h
+@@ -442,6 +442,7 @@ static inline struct obj_cgroup *__page_
+ * - LRU isolation
+ * - lock_page_memcg()
+ * - exclusive reference
++ * - mem_cgroup_trylock_pages()
+ *
+ * For a kmem page a caller should hold an rcu read lock to protect memcg
+ * associated with a kmem page from being released.
+@@ -497,6 +498,7 @@ static inline struct mem_cgroup *page_me
+ * - LRU isolation
+ * - lock_page_memcg()
+ * - exclusive reference
++ * - mem_cgroup_trylock_pages()
+ *
+ * For a kmem page a caller should hold an rcu read lock to protect memcg
+ * associated with a kmem page from being released.
+@@ -953,6 +955,23 @@ void unlock_page_memcg(struct page *page
+
+ void __mod_memcg_state(struct mem_cgroup *memcg, int idx, int val);
+
++/* try to stablize page_memcg() for all the pages in a memcg */
++static inline bool mem_cgroup_trylock_pages(struct mem_cgroup *memcg)
++{
++ rcu_read_lock();
++
++ if (mem_cgroup_disabled() || !atomic_read(&memcg->moving_account))
++ return true;
++
++ rcu_read_unlock();
++ return false;
++}
++
++static inline void mem_cgroup_unlock_pages(void)
++{
++ rcu_read_unlock();
++}
++
+ /* idx can be of type enum memcg_stat_item or node_stat_item */
+ static inline void mod_memcg_state(struct mem_cgroup *memcg,
+ int idx, int val)
+@@ -1369,6 +1388,18 @@ static inline void unlock_page_memcg(str
+ {
+ }
+
++static inline bool mem_cgroup_trylock_pages(struct mem_cgroup *memcg)
++{
++ /* to match page_memcg_rcu() */
++ rcu_read_lock();
++ return true;
++}
++
++static inline void mem_cgroup_unlock_pages(void)
++{
++ rcu_read_unlock();
++}
++
+ static inline void mem_cgroup_handle_over_high(void)
+ {
+ }
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -352,6 +352,7 @@ enum lruvec_flags {
+ #ifndef __GENERATING_BOUNDS_H
+
+ struct lruvec;
++struct page_vma_mapped_walk;
+
+ #define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
+ #define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
+@@ -407,6 +408,7 @@ struct lru_gen_struct {
+ };
+
+ void lru_gen_init_lruvec(struct lruvec *lruvec);
++void lru_gen_look_around(struct page_vma_mapped_walk *pvmw);
+
+ #ifdef CONFIG_MEMCG
+ void lru_gen_init_memcg(struct mem_cgroup *memcg);
+@@ -419,6 +421,10 @@ static inline void lru_gen_init_lruvec(s
+ {
+ }
+
++static inline void lru_gen_look_around(struct page_vma_mapped_walk *pvmw)
++{
++}
++
+ #ifdef CONFIG_MEMCG
+ static inline void lru_gen_init_memcg(struct mem_cgroup *memcg)
+ {
+--- a/mm/internal.h
++++ b/mm/internal.h
+@@ -35,6 +35,7 @@
+ void page_writeback_init(void);
+
+ vm_fault_t do_swap_page(struct vm_fault *vmf);
++void activate_page(struct page *page);
+
+ void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *start_vma,
+ unsigned long floor, unsigned long ceiling);
+--- a/mm/memcontrol.c
++++ b/mm/memcontrol.c
+@@ -2798,6 +2798,7 @@ static void commit_charge(struct page *p
+ * - LRU isolation
+ * - lock_page_memcg()
+ * - exclusive reference
++ * - mem_cgroup_trylock_pages()
+ */
+ page->memcg_data = (unsigned long)memcg;
+ }
+--- a/mm/rmap.c
++++ b/mm/rmap.c
+@@ -73,6 +73,7 @@
+ #include <linux/page_idle.h>
+ #include <linux/memremap.h>
+ #include <linux/userfaultfd_k.h>
++#include <linux/mm_inline.h>
+
+ #include <asm/tlbflush.h>
+
+@@ -793,6 +794,12 @@ static bool page_referenced_one(struct p
+ }
+
+ if (pvmw.pte) {
++ if (lru_gen_enabled() && pte_young(*pvmw.pte) &&
++ !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))) {
++ lru_gen_look_around(&pvmw);
++ referenced++;
++ }
++
+ if (ptep_clear_flush_young_notify(vma, address,
+ pvmw.pte)) {
+ /*
+--- a/mm/swap.c
++++ b/mm/swap.c
+@@ -325,7 +325,7 @@ static bool need_activate_page_drain(int
+ return pagevec_count(&per_cpu(lru_pvecs.activate_page, cpu)) != 0;
+ }
+
+-static void activate_page(struct page *page)
++void activate_page(struct page *page)
+ {
+ page = compound_head(page);
+ if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {
+@@ -345,7 +345,7 @@ static inline void activate_page_drain(i
+ {
+ }
+
+-static void activate_page(struct page *page)
++void activate_page(struct page *page)
+ {
+ struct lruvec *lruvec;
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -1409,6 +1409,11 @@ retry:
+ if (!sc->may_unmap && page_mapped(page))
+ goto keep_locked;
+
++ /* page_update_gen() tried to promote this page? */
++ if (lru_gen_enabled() && !ignore_references &&
++ page_mapped(page) && PageReferenced(page))
++ goto keep_locked;
++
+ may_enter_fs = (sc->gfp_mask & __GFP_FS) ||
+ (PageSwapCache(page) && (sc->gfp_mask & __GFP_IO));
+
+@@ -2990,6 +2995,29 @@ static bool positive_ctrl_err(struct ctr
+ * the aging
+ ******************************************************************************/
+
++/* promote pages accessed through page tables */
++static int page_update_gen(struct page *page, int gen)
++{
++ unsigned long new_flags, old_flags = READ_ONCE(page->flags);
++
++ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS);
++ VM_WARN_ON_ONCE(!rcu_read_lock_held());
++
++ do {
++ /* lru_gen_del_page() has isolated this page? */
++ if (!(old_flags & LRU_GEN_MASK)) {
++ /* for shrink_page_list() */
++ new_flags = old_flags | BIT(PG_referenced);
++ continue;
++ }
++
++ new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_MASK | LRU_REFS_FLAGS);
++ new_flags |= (gen + 1UL) << LRU_GEN_PGOFF;
++ } while (!try_cmpxchg(&page->flags, &old_flags, new_flags));
++
++ return ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
++}
++
+ /* protect pages accessed multiple times through file descriptors */
+ static int page_inc_gen(struct lruvec *lruvec, struct page *page, bool reclaiming)
+ {
+@@ -3001,6 +3029,11 @@ static int page_inc_gen(struct lruvec *l
+ VM_WARN_ON_ONCE_PAGE(!(old_flags & LRU_GEN_MASK), page);
+
+ do {
++ new_gen = ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
++ /* page_update_gen() has promoted this page? */
++ if (new_gen >= 0 && new_gen != old_gen)
++ return new_gen;
++
+ new_gen = (old_gen + 1) % MAX_NR_GENS;
+
+ new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_MASK | LRU_REFS_FLAGS);
+@@ -3015,6 +3048,43 @@ static int page_inc_gen(struct lruvec *l
+ return new_gen;
+ }
+
++static unsigned long get_pte_pfn(pte_t pte, struct vm_area_struct *vma, unsigned long addr)
++{
++ unsigned long pfn = pte_pfn(pte);
++
++ VM_WARN_ON_ONCE(addr < vma->vm_start || addr >= vma->vm_end);
++
++ if (!pte_present(pte) || is_zero_pfn(pfn))
++ return -1;
++
++ if (WARN_ON_ONCE(pte_devmap(pte) || pte_special(pte)))
++ return -1;
++
++ if (WARN_ON_ONCE(!pfn_valid(pfn)))
++ return -1;
++
++ return pfn;
++}
++
++static struct page *get_pfn_page(unsigned long pfn, struct mem_cgroup *memcg,
++ struct pglist_data *pgdat)
++{
++ struct page *page;
++
++ /* try to avoid unnecessary memory loads */
++ if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat))
++ return NULL;
++
++ page = compound_head(pfn_to_page(pfn));
++ if (page_to_nid(page) != pgdat->node_id)
++ return NULL;
++
++ if (page_memcg_rcu(page) != memcg)
++ return NULL;
++
++ return page;
++}
++
+ static void inc_min_seq(struct lruvec *lruvec, int type)
+ {
+ struct lru_gen_struct *lrugen = &lruvec->lrugen;
+@@ -3214,6 +3284,114 @@ static void lru_gen_age_node(struct pgli
+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
+ }
+
++/*
++ * This function exploits spatial locality when shrink_page_list() walks the
++ * rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages.
++ */
++void lru_gen_look_around(struct page_vma_mapped_walk *pvmw)
++{
++ int i;
++ pte_t *pte;
++ unsigned long start;
++ unsigned long end;
++ unsigned long addr;
++ unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {};
++ struct page *page = pvmw->page;
++ struct mem_cgroup *memcg = page_memcg(page);
++ struct pglist_data *pgdat = page_pgdat(page);
++ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat);
++ DEFINE_MAX_SEQ(lruvec);
++ int old_gen, new_gen = lru_gen_from_seq(max_seq);
++
++ lockdep_assert_held(pvmw->ptl);
++ VM_WARN_ON_ONCE_PAGE(PageLRU(page), page);
++
++ if (spin_is_contended(pvmw->ptl))
++ return;
++
++ start = max(pvmw->address & PMD_MASK, pvmw->vma->vm_start);
++ end = min(pvmw->address | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1;
++
++ if (end - start > MIN_LRU_BATCH * PAGE_SIZE) {
++ if (pvmw->address - start < MIN_LRU_BATCH * PAGE_SIZE / 2)
++ end = start + MIN_LRU_BATCH * PAGE_SIZE;
++ else if (end - pvmw->address < MIN_LRU_BATCH * PAGE_SIZE / 2)
++ start = end - MIN_LRU_BATCH * PAGE_SIZE;
++ else {
++ start = pvmw->address - MIN_LRU_BATCH * PAGE_SIZE / 2;
++ end = pvmw->address + MIN_LRU_BATCH * PAGE_SIZE / 2;
++ }
++ }
++
++ pte = pvmw->pte - (pvmw->address - start) / PAGE_SIZE;
++
++ rcu_read_lock();
++ arch_enter_lazy_mmu_mode();
++
++ for (i = 0, addr = start; addr != end; i++, addr += PAGE_SIZE) {
++ unsigned long pfn;
++
++ pfn = get_pte_pfn(pte[i], pvmw->vma, addr);
++ if (pfn == -1)
++ continue;
++
++ if (!pte_young(pte[i]))
++ continue;
++
++ page = get_pfn_page(pfn, memcg, pgdat);
++ if (!page)
++ continue;
++
++ if (!ptep_test_and_clear_young(pvmw->vma, addr, pte + i))
++ VM_WARN_ON_ONCE(true);
++
++ if (pte_dirty(pte[i]) && !PageDirty(page) &&
++ !(PageAnon(page) && PageSwapBacked(page) &&
++ !PageSwapCache(page)))
++ set_page_dirty(page);
++
++ old_gen = page_lru_gen(page);
++ if (old_gen < 0)
++ SetPageReferenced(page);
++ else if (old_gen != new_gen)
++ __set_bit(i, bitmap);
++ }
++
++ arch_leave_lazy_mmu_mode();
++ rcu_read_unlock();
++
++ if (bitmap_weight(bitmap, MIN_LRU_BATCH) < PAGEVEC_SIZE) {
++ for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
++ page = pte_page(pte[i]);
++ activate_page(page);
++ }
++ return;
++ }
++
++ /* page_update_gen() requires stable page_memcg() */
++ if (!mem_cgroup_trylock_pages(memcg))
++ return;
++
++ spin_lock_irq(&lruvec->lru_lock);
++ new_gen = lru_gen_from_seq(lruvec->lrugen.max_seq);
++
++ for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
++ page = compound_head(pte_page(pte[i]));
++ if (page_memcg_rcu(page) != memcg)
++ continue;
++
++ old_gen = page_update_gen(page, new_gen);
++ if (old_gen < 0 || old_gen == new_gen)
++ continue;
++
++ lru_gen_update_size(lruvec, page, old_gen, new_gen);
++ }
++
++ spin_unlock_irq(&lruvec->lru_lock);
++
++ mem_cgroup_unlock_pages();
++}
++
+ /******************************************************************************
+ * the eviction
+ ******************************************************************************/
+@@ -3250,6 +3428,12 @@ static bool sort_page(struct lruvec *lru
+ return true;
+ }
+
++ /* promoted */
++ if (gen != lru_gen_from_seq(lrugen->min_seq[type])) {
++ list_move(&page->lru, &lrugen->lists[gen][type][zone]);
++ return true;
++ }
++
+ /* protected */
+ if (tier > tier_idx) {
+ int hist = lru_hist_from_seq(lrugen->min_seq[type]);
diff --git a/target/linux/generic/backport-6.1/020-v6.1-08-mm-multi-gen-LRU-support-page-table-walks.patch b/target/linux/generic/backport-6.1/020-v6.1-08-mm-multi-gen-LRU-support-page-table-walks.patch
new file mode 100644
index 0000000000..dad21dccb5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-08-mm-multi-gen-LRU-support-page-table-walks.patch
@@ -0,0 +1,1687 @@
+From 05223c4e80b34e29f2255c04ffebc2c4475e7593 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:05 -0600
+Subject: [PATCH 08/29] mm: multi-gen LRU: support page table walks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+To further exploit spatial locality, the aging prefers to walk page tables
+to search for young PTEs and promote hot pages. A kill switch will be
+added in the next patch to disable this behavior. When disabled, the
+aging relies on the rmap only.
+
+NB: this behavior has nothing similar with the page table scanning in the
+2.4 kernel [1], which searches page tables for old PTEs, adds cold pages
+to swapcache and unmaps them.
+
+To avoid confusion, the term "iteration" specifically means the traversal
+of an entire mm_struct list; the term "walk" will be applied to page
+tables and the rmap, as usual.
+
+An mm_struct list is maintained for each memcg, and an mm_struct follows
+its owner task to the new memcg when this task is migrated. Given an
+lruvec, the aging iterates lruvec_memcg()->mm_list and calls
+walk_page_range() with each mm_struct on this list to promote hot pages
+before it increments max_seq.
+
+When multiple page table walkers iterate the same list, each of them gets
+a unique mm_struct; therefore they can run concurrently. Page table
+walkers ignore any misplaced pages, e.g., if an mm_struct was migrated,
+pages it left in the previous memcg will not be promoted when its current
+memcg is under reclaim. Similarly, page table walkers will not promote
+pages from nodes other than the one under reclaim.
+
+This patch uses the following optimizations when walking page tables:
+1. It tracks the usage of mm_struct's between context switches so that
+ page table walkers can skip processes that have been sleeping since
+ the last iteration.
+2. It uses generational Bloom filters to record populated branches so
+ that page table walkers can reduce their search space based on the
+ query results, e.g., to skip page tables containing mostly holes or
+ misplaced pages.
+3. It takes advantage of the accessed bit in non-leaf PMD entries when
+ CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y.
+4. It does not zigzag between a PGD table and the same PMD table
+ spanning multiple VMAs. IOW, it finishes all the VMAs within the
+ range of the same PMD table before it returns to a PGD table. This
+ improves the cache performance for workloads that have large
+ numbers of tiny VMAs [2], especially when CONFIG_PGTABLE_LEVELS=5.
+
+Server benchmark results:
+ Single workload:
+ fio (buffered I/O): no change
+
+ Single workload:
+ memcached (anon): +[8, 10]%
+ Ops/sec KB/sec
+ patch1-7: 1147696.57 44640.29
+ patch1-8: 1245274.91 48435.66
+
+ Configurations:
+ no change
+
+Client benchmark results:
+ kswapd profiles:
+ patch1-7
+ 48.16% lzo1x_1_do_compress (real work)
+ 8.20% page_vma_mapped_walk (overhead)
+ 7.06% _raw_spin_unlock_irq
+ 2.92% ptep_clear_flush
+ 2.53% __zram_bvec_write
+ 2.11% do_raw_spin_lock
+ 2.02% memmove
+ 1.93% lru_gen_look_around
+ 1.56% free_unref_page_list
+ 1.40% memset
+
+ patch1-8
+ 49.44% lzo1x_1_do_compress (real work)
+ 6.19% page_vma_mapped_walk (overhead)
+ 5.97% _raw_spin_unlock_irq
+ 3.13% get_pfn_page
+ 2.85% ptep_clear_flush
+ 2.42% __zram_bvec_write
+ 2.08% do_raw_spin_lock
+ 1.92% memmove
+ 1.44% alloc_zspage
+ 1.36% memset
+
+ Configurations:
+ no change
+
+Thanks to the following developers for their efforts [3].
+ kernel test robot <lkp@intel.com>
+
+[1] https://lwn.net/Articles/23732/
+[2] https://llvm.org/docs/ScudoHardenedAllocator.html
+[3] https://lore.kernel.org/r/202204160827.ekEARWQo-lkp@intel.com/
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-9-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Barry Song <baohua@kernel.org>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ fs/exec.c | 2 +
+ include/linux/memcontrol.h | 5 +
+ include/linux/mm_types.h | 76 +++
+ include/linux/mmzone.h | 56 +-
+ include/linux/swap.h | 4 +
+ kernel/exit.c | 1 +
+ kernel/fork.c | 9 +
+ kernel/sched/core.c | 1 +
+ mm/memcontrol.c | 25 +
+ mm/vmscan.c | 1010 +++++++++++++++++++++++++++++++++++-
+ 10 files changed, 1172 insertions(+), 17 deletions(-)
+
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1013,6 +1013,7 @@ static int exec_mmap(struct mm_struct *m
+ active_mm = tsk->active_mm;
+ tsk->active_mm = mm;
+ tsk->mm = mm;
++ lru_gen_add_mm(mm);
+ /*
+ * This prevents preemption while active_mm is being loaded and
+ * it and mm are being updated, which could cause problems for
+@@ -1028,6 +1029,7 @@ static int exec_mmap(struct mm_struct *m
+ tsk->mm->vmacache_seqnum = 0;
+ vmacache_flush(tsk);
+ task_unlock(tsk);
++ lru_gen_use_mm(mm);
+ if (old_mm) {
+ mmap_read_unlock(old_mm);
+ BUG_ON(active_mm != old_mm);
+--- a/include/linux/memcontrol.h
++++ b/include/linux/memcontrol.h
+@@ -348,6 +348,11 @@ struct mem_cgroup {
+ struct deferred_split deferred_split_queue;
+ #endif
+
++#ifdef CONFIG_LRU_GEN
++ /* per-memcg mm_struct list */
++ struct lru_gen_mm_list mm_list;
++#endif
++
+ struct mem_cgroup_per_node *nodeinfo[];
+ };
+
+--- a/include/linux/mm_types.h
++++ b/include/linux/mm_types.h
+@@ -580,6 +580,22 @@ struct mm_struct {
+ #ifdef CONFIG_IOMMU_SUPPORT
+ u32 pasid;
+ #endif
++#ifdef CONFIG_LRU_GEN
++ struct {
++ /* this mm_struct is on lru_gen_mm_list */
++ struct list_head list;
++ /*
++ * Set when switching to this mm_struct, as a hint of
++ * whether it has been used since the last time per-node
++ * page table walkers cleared the corresponding bits.
++ */
++ unsigned long bitmap;
++#ifdef CONFIG_MEMCG
++ /* points to the memcg of "owner" above */
++ struct mem_cgroup *memcg;
++#endif
++ } lru_gen;
++#endif /* CONFIG_LRU_GEN */
+ } __randomize_layout;
+
+ /*
+@@ -606,6 +622,66 @@ static inline cpumask_t *mm_cpumask(stru
+ return (struct cpumask *)&mm->cpu_bitmap;
+ }
+
++#ifdef CONFIG_LRU_GEN
++
++struct lru_gen_mm_list {
++ /* mm_struct list for page table walkers */
++ struct list_head fifo;
++ /* protects the list above */
++ spinlock_t lock;
++};
++
++void lru_gen_add_mm(struct mm_struct *mm);
++void lru_gen_del_mm(struct mm_struct *mm);
++#ifdef CONFIG_MEMCG
++void lru_gen_migrate_mm(struct mm_struct *mm);
++#endif
++
++static inline void lru_gen_init_mm(struct mm_struct *mm)
++{
++ INIT_LIST_HEAD(&mm->lru_gen.list);
++ mm->lru_gen.bitmap = 0;
++#ifdef CONFIG_MEMCG
++ mm->lru_gen.memcg = NULL;
++#endif
++}
++
++static inline void lru_gen_use_mm(struct mm_struct *mm)
++{
++ /*
++ * When the bitmap is set, page reclaim knows this mm_struct has been
++ * used since the last time it cleared the bitmap. So it might be worth
++ * walking the page tables of this mm_struct to clear the accessed bit.
++ */
++ WRITE_ONCE(mm->lru_gen.bitmap, -1);
++}
++
++#else /* !CONFIG_LRU_GEN */
++
++static inline void lru_gen_add_mm(struct mm_struct *mm)
++{
++}
++
++static inline void lru_gen_del_mm(struct mm_struct *mm)
++{
++}
++
++#ifdef CONFIG_MEMCG
++static inline void lru_gen_migrate_mm(struct mm_struct *mm)
++{
++}
++#endif
++
++static inline void lru_gen_init_mm(struct mm_struct *mm)
++{
++}
++
++static inline void lru_gen_use_mm(struct mm_struct *mm)
++{
++}
++
++#endif /* CONFIG_LRU_GEN */
++
+ struct mmu_gather;
+ extern void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm);
+ extern void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm);
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -385,7 +385,7 @@ enum {
+ * min_seq behind.
+ *
+ * The number of pages in each generation is eventually consistent and therefore
+- * can be transiently negative.
++ * can be transiently negative when reset_batch_size() is pending.
+ */
+ struct lru_gen_struct {
+ /* the aging increments the youngest generation number */
+@@ -407,6 +407,53 @@ struct lru_gen_struct {
+ atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
+ };
+
++enum {
++ MM_LEAF_TOTAL, /* total leaf entries */
++ MM_LEAF_OLD, /* old leaf entries */
++ MM_LEAF_YOUNG, /* young leaf entries */
++ MM_NONLEAF_TOTAL, /* total non-leaf entries */
++ MM_NONLEAF_FOUND, /* non-leaf entries found in Bloom filters */
++ MM_NONLEAF_ADDED, /* non-leaf entries added to Bloom filters */
++ NR_MM_STATS
++};
++
++/* double-buffering Bloom filters */
++#define NR_BLOOM_FILTERS 2
++
++struct lru_gen_mm_state {
++ /* set to max_seq after each iteration */
++ unsigned long seq;
++ /* where the current iteration continues (inclusive) */
++ struct list_head *head;
++ /* where the last iteration ended (exclusive) */
++ struct list_head *tail;
++ /* to wait for the last page table walker to finish */
++ struct wait_queue_head wait;
++ /* Bloom filters flip after each iteration */
++ unsigned long *filters[NR_BLOOM_FILTERS];
++ /* the mm stats for debugging */
++ unsigned long stats[NR_HIST_GENS][NR_MM_STATS];
++ /* the number of concurrent page table walkers */
++ int nr_walkers;
++};
++
++struct lru_gen_mm_walk {
++ /* the lruvec under reclaim */
++ struct lruvec *lruvec;
++ /* unstable max_seq from lru_gen_struct */
++ unsigned long max_seq;
++ /* the next address within an mm to scan */
++ unsigned long next_addr;
++ /* to batch promoted pages */
++ int nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
++ /* to batch the mm stats */
++ int mm_stats[NR_MM_STATS];
++ /* total batched items */
++ int batched;
++ bool can_swap;
++ bool force_scan;
++};
++
+ void lru_gen_init_lruvec(struct lruvec *lruvec);
+ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw);
+
+@@ -457,6 +504,8 @@ struct lruvec {
+ #ifdef CONFIG_LRU_GEN
+ /* evictable pages divided into generations */
+ struct lru_gen_struct lrugen;
++ /* to concurrently iterate lru_gen_mm_list */
++ struct lru_gen_mm_state mm_state;
+ #endif
+ #ifdef CONFIG_MEMCG
+ struct pglist_data *pgdat;
+@@ -1042,6 +1091,11 @@ typedef struct pglist_data {
+
+ unsigned long flags;
+
++#ifdef CONFIG_LRU_GEN
++ /* kswap mm walk data */
++ struct lru_gen_mm_walk mm_walk;
++#endif
++
+ ZONE_PADDING(_pad2_)
+
+ /* Per-node vmstats */
+--- a/include/linux/swap.h
++++ b/include/linux/swap.h
+@@ -137,6 +137,10 @@ union swap_header {
+ */
+ struct reclaim_state {
+ unsigned long reclaimed_slab;
++#ifdef CONFIG_LRU_GEN
++ /* per-thread mm walk data */
++ struct lru_gen_mm_walk *mm_walk;
++#endif
+ };
+
+ #ifdef __KERNEL__
+--- a/kernel/exit.c
++++ b/kernel/exit.c
+@@ -469,6 +469,7 @@ assign_new_owner:
+ goto retry;
+ }
+ WRITE_ONCE(mm->owner, c);
++ lru_gen_migrate_mm(mm);
+ task_unlock(c);
+ put_task_struct(c);
+ }
+--- a/kernel/fork.c
++++ b/kernel/fork.c
+@@ -1083,6 +1083,7 @@ static struct mm_struct *mm_init(struct
+ goto fail_nocontext;
+
+ mm->user_ns = get_user_ns(user_ns);
++ lru_gen_init_mm(mm);
+ return mm;
+
+ fail_nocontext:
+@@ -1125,6 +1126,7 @@ static inline void __mmput(struct mm_str
+ }
+ if (mm->binfmt)
+ module_put(mm->binfmt->module);
++ lru_gen_del_mm(mm);
+ mmdrop(mm);
+ }
+
+@@ -2622,6 +2624,13 @@ pid_t kernel_clone(struct kernel_clone_a
+ get_task_struct(p);
+ }
+
++ if (IS_ENABLED(CONFIG_LRU_GEN) && !(clone_flags & CLONE_VM)) {
++ /* lock the task to synchronize with memcg migration */
++ task_lock(p);
++ lru_gen_add_mm(p->mm);
++ task_unlock(p);
++ }
++
+ wake_up_new_task(p);
+
+ /* forking complete and child started to run, tell ptracer */
+--- a/kernel/sched/core.c
++++ b/kernel/sched/core.c
+@@ -5010,6 +5010,7 @@ context_switch(struct rq *rq, struct tas
+ * finish_task_switch()'s mmdrop().
+ */
+ switch_mm_irqs_off(prev->active_mm, next->mm, next);
++ lru_gen_use_mm(next->mm);
+
+ if (!prev->mm) { // from kernel
+ /* will mmdrop() in finish_task_switch(). */
+--- a/mm/memcontrol.c
++++ b/mm/memcontrol.c
+@@ -6212,6 +6212,30 @@ static void mem_cgroup_move_task(void)
+ }
+ #endif
+
++#ifdef CONFIG_LRU_GEN
++static void mem_cgroup_attach(struct cgroup_taskset *tset)
++{
++ struct task_struct *task;
++ struct cgroup_subsys_state *css;
++
++ /* find the first leader if there is any */
++ cgroup_taskset_for_each_leader(task, css, tset)
++ break;
++
++ if (!task)
++ return;
++
++ task_lock(task);
++ if (task->mm && READ_ONCE(task->mm->owner) == task)
++ lru_gen_migrate_mm(task->mm);
++ task_unlock(task);
++}
++#else
++static void mem_cgroup_attach(struct cgroup_taskset *tset)
++{
++}
++#endif /* CONFIG_LRU_GEN */
++
+ static int seq_puts_memcg_tunable(struct seq_file *m, unsigned long value)
+ {
+ if (value == PAGE_COUNTER_MAX)
+@@ -6555,6 +6579,7 @@ struct cgroup_subsys memory_cgrp_subsys
+ .css_reset = mem_cgroup_css_reset,
+ .css_rstat_flush = mem_cgroup_css_rstat_flush,
+ .can_attach = mem_cgroup_can_attach,
++ .attach = mem_cgroup_attach,
+ .cancel_attach = mem_cgroup_cancel_attach,
+ .post_attach = mem_cgroup_move_task,
+ .dfl_cftypes = memory_files,
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -50,6 +50,8 @@
+ #include <linux/printk.h>
+ #include <linux/dax.h>
+ #include <linux/psi.h>
++#include <linux/pagewalk.h>
++#include <linux/shmem_fs.h>
+
+ #include <asm/tlbflush.h>
+ #include <asm/div64.h>
+@@ -2853,7 +2855,7 @@ static bool can_age_anon_pages(struct pg
+ for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \
+ for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++)
+
+-static struct lruvec __maybe_unused *get_lruvec(struct mem_cgroup *memcg, int nid)
++static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid)
+ {
+ struct pglist_data *pgdat = NODE_DATA(nid);
+
+@@ -2899,6 +2901,371 @@ static bool __maybe_unused seq_is_valid(
+ }
+
+ /******************************************************************************
++ * mm_struct list
++ ******************************************************************************/
++
++static struct lru_gen_mm_list *get_mm_list(struct mem_cgroup *memcg)
++{
++ static struct lru_gen_mm_list mm_list = {
++ .fifo = LIST_HEAD_INIT(mm_list.fifo),
++ .lock = __SPIN_LOCK_UNLOCKED(mm_list.lock),
++ };
++
++#ifdef CONFIG_MEMCG
++ if (memcg)
++ return &memcg->mm_list;
++#endif
++ VM_WARN_ON_ONCE(!mem_cgroup_disabled());
++
++ return &mm_list;
++}
++
++void lru_gen_add_mm(struct mm_struct *mm)
++{
++ int nid;
++ struct mem_cgroup *memcg = get_mem_cgroup_from_mm(mm);
++ struct lru_gen_mm_list *mm_list = get_mm_list(memcg);
++
++ VM_WARN_ON_ONCE(!list_empty(&mm->lru_gen.list));
++#ifdef CONFIG_MEMCG
++ VM_WARN_ON_ONCE(mm->lru_gen.memcg);
++ mm->lru_gen.memcg = memcg;
++#endif
++ spin_lock(&mm_list->lock);
++
++ for_each_node_state(nid, N_MEMORY) {
++ struct lruvec *lruvec = get_lruvec(memcg, nid);
++
++ if (!lruvec)
++ continue;
++
++ /* the first addition since the last iteration */
++ if (lruvec->mm_state.tail == &mm_list->fifo)
++ lruvec->mm_state.tail = &mm->lru_gen.list;
++ }
++
++ list_add_tail(&mm->lru_gen.list, &mm_list->fifo);
++
++ spin_unlock(&mm_list->lock);
++}
++
++void lru_gen_del_mm(struct mm_struct *mm)
++{
++ int nid;
++ struct lru_gen_mm_list *mm_list;
++ struct mem_cgroup *memcg = NULL;
++
++ if (list_empty(&mm->lru_gen.list))
++ return;
++
++#ifdef CONFIG_MEMCG
++ memcg = mm->lru_gen.memcg;
++#endif
++ mm_list = get_mm_list(memcg);
++
++ spin_lock(&mm_list->lock);
++
++ for_each_node(nid) {
++ struct lruvec *lruvec = get_lruvec(memcg, nid);
++
++ if (!lruvec)
++ continue;
++
++ /* where the last iteration ended (exclusive) */
++ if (lruvec->mm_state.tail == &mm->lru_gen.list)
++ lruvec->mm_state.tail = lruvec->mm_state.tail->next;
++
++ /* where the current iteration continues (inclusive) */
++ if (lruvec->mm_state.head != &mm->lru_gen.list)
++ continue;
++
++ lruvec->mm_state.head = lruvec->mm_state.head->next;
++ /* the deletion ends the current iteration */
++ if (lruvec->mm_state.head == &mm_list->fifo)
++ WRITE_ONCE(lruvec->mm_state.seq, lruvec->mm_state.seq + 1);
++ }
++
++ list_del_init(&mm->lru_gen.list);
++
++ spin_unlock(&mm_list->lock);
++
++#ifdef CONFIG_MEMCG
++ mem_cgroup_put(mm->lru_gen.memcg);
++ mm->lru_gen.memcg = NULL;
++#endif
++}
++
++#ifdef CONFIG_MEMCG
++void lru_gen_migrate_mm(struct mm_struct *mm)
++{
++ struct mem_cgroup *memcg;
++ struct task_struct *task = rcu_dereference_protected(mm->owner, true);
++
++ VM_WARN_ON_ONCE(task->mm != mm);
++ lockdep_assert_held(&task->alloc_lock);
++
++ /* for mm_update_next_owner() */
++ if (mem_cgroup_disabled())
++ return;
++
++ rcu_read_lock();
++ memcg = mem_cgroup_from_task(task);
++ rcu_read_unlock();
++ if (memcg == mm->lru_gen.memcg)
++ return;
++
++ VM_WARN_ON_ONCE(!mm->lru_gen.memcg);
++ VM_WARN_ON_ONCE(list_empty(&mm->lru_gen.list));
++
++ lru_gen_del_mm(mm);
++ lru_gen_add_mm(mm);
++}
++#endif
++
++/*
++ * Bloom filters with m=1<<15, k=2 and the false positive rates of ~1/5 when
++ * n=10,000 and ~1/2 when n=20,000, where, conventionally, m is the number of
++ * bits in a bitmap, k is the number of hash functions and n is the number of
++ * inserted items.
++ *
++ * Page table walkers use one of the two filters to reduce their search space.
++ * To get rid of non-leaf entries that no longer have enough leaf entries, the
++ * aging uses the double-buffering technique to flip to the other filter each
++ * time it produces a new generation. For non-leaf entries that have enough
++ * leaf entries, the aging carries them over to the next generation in
++ * walk_pmd_range(); the eviction also report them when walking the rmap
++ * in lru_gen_look_around().
++ *
++ * For future optimizations:
++ * 1. It's not necessary to keep both filters all the time. The spare one can be
++ * freed after the RCU grace period and reallocated if needed again.
++ * 2. And when reallocating, it's worth scaling its size according to the number
++ * of inserted entries in the other filter, to reduce the memory overhead on
++ * small systems and false positives on large systems.
++ * 3. Jenkins' hash function is an alternative to Knuth's.
++ */
++#define BLOOM_FILTER_SHIFT 15
++
++static inline int filter_gen_from_seq(unsigned long seq)
++{
++ return seq % NR_BLOOM_FILTERS;
++}
++
++static void get_item_key(void *item, int *key)
++{
++ u32 hash = hash_ptr(item, BLOOM_FILTER_SHIFT * 2);
++
++ BUILD_BUG_ON(BLOOM_FILTER_SHIFT * 2 > BITS_PER_TYPE(u32));
++
++ key[0] = hash & (BIT(BLOOM_FILTER_SHIFT) - 1);
++ key[1] = hash >> BLOOM_FILTER_SHIFT;
++}
++
++static void reset_bloom_filter(struct lruvec *lruvec, unsigned long seq)
++{
++ unsigned long *filter;
++ int gen = filter_gen_from_seq(seq);
++
++ filter = lruvec->mm_state.filters[gen];
++ if (filter) {
++ bitmap_clear(filter, 0, BIT(BLOOM_FILTER_SHIFT));
++ return;
++ }
++
++ filter = bitmap_zalloc(BIT(BLOOM_FILTER_SHIFT),
++ __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
++ WRITE_ONCE(lruvec->mm_state.filters[gen], filter);
++}
++
++static void update_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
++{
++ int key[2];
++ unsigned long *filter;
++ int gen = filter_gen_from_seq(seq);
++
++ filter = READ_ONCE(lruvec->mm_state.filters[gen]);
++ if (!filter)
++ return;
++
++ get_item_key(item, key);
++
++ if (!test_bit(key[0], filter))
++ set_bit(key[0], filter);
++ if (!test_bit(key[1], filter))
++ set_bit(key[1], filter);
++}
++
++static bool test_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
++{
++ int key[2];
++ unsigned long *filter;
++ int gen = filter_gen_from_seq(seq);
++
++ filter = READ_ONCE(lruvec->mm_state.filters[gen]);
++ if (!filter)
++ return true;
++
++ get_item_key(item, key);
++
++ return test_bit(key[0], filter) && test_bit(key[1], filter);
++}
++
++static void reset_mm_stats(struct lruvec *lruvec, struct lru_gen_mm_walk *walk, bool last)
++{
++ int i;
++ int hist;
++
++ lockdep_assert_held(&get_mm_list(lruvec_memcg(lruvec))->lock);
++
++ if (walk) {
++ hist = lru_hist_from_seq(walk->max_seq);
++
++ for (i = 0; i < NR_MM_STATS; i++) {
++ WRITE_ONCE(lruvec->mm_state.stats[hist][i],
++ lruvec->mm_state.stats[hist][i] + walk->mm_stats[i]);
++ walk->mm_stats[i] = 0;
++ }
++ }
++
++ if (NR_HIST_GENS > 1 && last) {
++ hist = lru_hist_from_seq(lruvec->mm_state.seq + 1);
++
++ for (i = 0; i < NR_MM_STATS; i++)
++ WRITE_ONCE(lruvec->mm_state.stats[hist][i], 0);
++ }
++}
++
++static bool should_skip_mm(struct mm_struct *mm, struct lru_gen_mm_walk *walk)
++{
++ int type;
++ unsigned long size = 0;
++ struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
++ int key = pgdat->node_id % BITS_PER_TYPE(mm->lru_gen.bitmap);
++
++ if (!walk->force_scan && !test_bit(key, &mm->lru_gen.bitmap))
++ return true;
++
++ clear_bit(key, &mm->lru_gen.bitmap);
++
++ for (type = !walk->can_swap; type < ANON_AND_FILE; type++) {
++ size += type ? get_mm_counter(mm, MM_FILEPAGES) :
++ get_mm_counter(mm, MM_ANONPAGES) +
++ get_mm_counter(mm, MM_SHMEMPAGES);
++ }
++
++ if (size < MIN_LRU_BATCH)
++ return true;
++
++ return !mmget_not_zero(mm);
++}
++
++static bool iterate_mm_list(struct lruvec *lruvec, struct lru_gen_mm_walk *walk,
++ struct mm_struct **iter)
++{
++ bool first = false;
++ bool last = true;
++ struct mm_struct *mm = NULL;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ struct lru_gen_mm_list *mm_list = get_mm_list(memcg);
++ struct lru_gen_mm_state *mm_state = &lruvec->mm_state;
++
++ /*
++ * There are four interesting cases for this page table walker:
++ * 1. It tries to start a new iteration of mm_list with a stale max_seq;
++ * there is nothing left to do.
++ * 2. It's the first of the current generation, and it needs to reset
++ * the Bloom filter for the next generation.
++ * 3. It reaches the end of mm_list, and it needs to increment
++ * mm_state->seq; the iteration is done.
++ * 4. It's the last of the current generation, and it needs to reset the
++ * mm stats counters for the next generation.
++ */
++ spin_lock(&mm_list->lock);
++
++ VM_WARN_ON_ONCE(mm_state->seq + 1 < walk->max_seq);
++ VM_WARN_ON_ONCE(*iter && mm_state->seq > walk->max_seq);
++ VM_WARN_ON_ONCE(*iter && !mm_state->nr_walkers);
++
++ if (walk->max_seq <= mm_state->seq) {
++ if (!*iter)
++ last = false;
++ goto done;
++ }
++
++ if (!mm_state->nr_walkers) {
++ VM_WARN_ON_ONCE(mm_state->head && mm_state->head != &mm_list->fifo);
++
++ mm_state->head = mm_list->fifo.next;
++ first = true;
++ }
++
++ while (!mm && mm_state->head != &mm_list->fifo) {
++ mm = list_entry(mm_state->head, struct mm_struct, lru_gen.list);
++
++ mm_state->head = mm_state->head->next;
++
++ /* force scan for those added after the last iteration */
++ if (!mm_state->tail || mm_state->tail == &mm->lru_gen.list) {
++ mm_state->tail = mm_state->head;
++ walk->force_scan = true;
++ }
++
++ if (should_skip_mm(mm, walk))
++ mm = NULL;
++ }
++
++ if (mm_state->head == &mm_list->fifo)
++ WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
++done:
++ if (*iter && !mm)
++ mm_state->nr_walkers--;
++ if (!*iter && mm)
++ mm_state->nr_walkers++;
++
++ if (mm_state->nr_walkers)
++ last = false;
++
++ if (*iter || last)
++ reset_mm_stats(lruvec, walk, last);
++
++ spin_unlock(&mm_list->lock);
++
++ if (mm && first)
++ reset_bloom_filter(lruvec, walk->max_seq + 1);
++
++ if (*iter)
++ mmput_async(*iter);
++
++ *iter = mm;
++
++ return last;
++}
++
++static bool iterate_mm_list_nowalk(struct lruvec *lruvec, unsigned long max_seq)
++{
++ bool success = false;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ struct lru_gen_mm_list *mm_list = get_mm_list(memcg);
++ struct lru_gen_mm_state *mm_state = &lruvec->mm_state;
++
++ spin_lock(&mm_list->lock);
++
++ VM_WARN_ON_ONCE(mm_state->seq + 1 < max_seq);
++
++ if (max_seq > mm_state->seq && !mm_state->nr_walkers) {
++ VM_WARN_ON_ONCE(mm_state->head && mm_state->head != &mm_list->fifo);
++
++ WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
++ reset_mm_stats(lruvec, NULL, true);
++ success = true;
++ }
++
++ spin_unlock(&mm_list->lock);
++
++ return success;
++}
++
++/******************************************************************************
+ * refault feedback loop
+ ******************************************************************************/
+
+@@ -3048,6 +3415,118 @@ static int page_inc_gen(struct lruvec *l
+ return new_gen;
+ }
+
++static void update_batch_size(struct lru_gen_mm_walk *walk, struct page *page,
++ int old_gen, int new_gen)
++{
++ int type = page_is_file_lru(page);
++ int zone = page_zonenum(page);
++ int delta = thp_nr_pages(page);
++
++ VM_WARN_ON_ONCE(old_gen >= MAX_NR_GENS);
++ VM_WARN_ON_ONCE(new_gen >= MAX_NR_GENS);
++
++ walk->batched++;
++
++ walk->nr_pages[old_gen][type][zone] -= delta;
++ walk->nr_pages[new_gen][type][zone] += delta;
++}
++
++static void reset_batch_size(struct lruvec *lruvec, struct lru_gen_mm_walk *walk)
++{
++ int gen, type, zone;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ walk->batched = 0;
++
++ for_each_gen_type_zone(gen, type, zone) {
++ enum lru_list lru = type * LRU_INACTIVE_FILE;
++ int delta = walk->nr_pages[gen][type][zone];
++
++ if (!delta)
++ continue;
++
++ walk->nr_pages[gen][type][zone] = 0;
++ WRITE_ONCE(lrugen->nr_pages[gen][type][zone],
++ lrugen->nr_pages[gen][type][zone] + delta);
++
++ if (lru_gen_is_active(lruvec, gen))
++ lru += LRU_ACTIVE;
++ __update_lru_size(lruvec, lru, zone, delta);
++ }
++}
++
++static int should_skip_vma(unsigned long start, unsigned long end, struct mm_walk *args)
++{
++ struct address_space *mapping;
++ struct vm_area_struct *vma = args->vma;
++ struct lru_gen_mm_walk *walk = args->private;
++
++ if (!vma_is_accessible(vma))
++ return true;
++
++ if (is_vm_hugetlb_page(vma))
++ return true;
++
++ if (vma->vm_flags & (VM_LOCKED | VM_SPECIAL | VM_SEQ_READ | VM_RAND_READ))
++ return true;
++
++ if (vma == get_gate_vma(vma->vm_mm))
++ return true;
++
++ if (vma_is_anonymous(vma))
++ return !walk->can_swap;
++
++ if (WARN_ON_ONCE(!vma->vm_file || !vma->vm_file->f_mapping))
++ return true;
++
++ mapping = vma->vm_file->f_mapping;
++ if (mapping_unevictable(mapping))
++ return true;
++
++ if (shmem_mapping(mapping))
++ return !walk->can_swap;
++
++ /* to exclude special mappings like dax, etc. */
++ return !mapping->a_ops->readpage;
++}
++
++/*
++ * Some userspace memory allocators map many single-page VMAs. Instead of
++ * returning back to the PGD table for each of such VMAs, finish an entire PMD
++ * table to reduce zigzags and improve cache performance.
++ */
++static bool get_next_vma(unsigned long mask, unsigned long size, struct mm_walk *args,
++ unsigned long *vm_start, unsigned long *vm_end)
++{
++ unsigned long start = round_up(*vm_end, size);
++ unsigned long end = (start | ~mask) + 1;
++
++ VM_WARN_ON_ONCE(mask & size);
++ VM_WARN_ON_ONCE((start & mask) != (*vm_start & mask));
++
++ while (args->vma) {
++ if (start >= args->vma->vm_end) {
++ args->vma = args->vma->vm_next;
++ continue;
++ }
++
++ if (end && end <= args->vma->vm_start)
++ return false;
++
++ if (should_skip_vma(args->vma->vm_start, args->vma->vm_end, args)) {
++ args->vma = args->vma->vm_next;
++ continue;
++ }
++
++ *vm_start = max(start, args->vma->vm_start);
++ *vm_end = min(end - 1, args->vma->vm_end - 1) + 1;
++
++ return true;
++ }
++
++ return false;
++}
++
+ static unsigned long get_pte_pfn(pte_t pte, struct vm_area_struct *vma, unsigned long addr)
+ {
+ unsigned long pfn = pte_pfn(pte);
+@@ -3066,8 +3545,28 @@ static unsigned long get_pte_pfn(pte_t p
+ return pfn;
+ }
+
++#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
++static unsigned long get_pmd_pfn(pmd_t pmd, struct vm_area_struct *vma, unsigned long addr)
++{
++ unsigned long pfn = pmd_pfn(pmd);
++
++ VM_WARN_ON_ONCE(addr < vma->vm_start || addr >= vma->vm_end);
++
++ if (!pmd_present(pmd) || is_huge_zero_pmd(pmd))
++ return -1;
++
++ if (WARN_ON_ONCE(pmd_devmap(pmd)))
++ return -1;
++
++ if (WARN_ON_ONCE(!pfn_valid(pfn)))
++ return -1;
++
++ return pfn;
++}
++#endif
++
+ static struct page *get_pfn_page(unsigned long pfn, struct mem_cgroup *memcg,
+- struct pglist_data *pgdat)
++ struct pglist_data *pgdat, bool can_swap)
+ {
+ struct page *page;
+
+@@ -3082,9 +3581,375 @@ static struct page *get_pfn_page(unsigne
+ if (page_memcg_rcu(page) != memcg)
+ return NULL;
+
++ /* file VMAs can contain anon pages from COW */
++ if (!page_is_file_lru(page) && !can_swap)
++ return NULL;
++
+ return page;
+ }
+
++static bool suitable_to_scan(int total, int young)
++{
++ int n = clamp_t(int, cache_line_size() / sizeof(pte_t), 2, 8);
++
++ /* suitable if the average number of young PTEs per cacheline is >=1 */
++ return young * n >= total;
++}
++
++static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
++ struct mm_walk *args)
++{
++ int i;
++ pte_t *pte;
++ spinlock_t *ptl;
++ unsigned long addr;
++ int total = 0;
++ int young = 0;
++ struct lru_gen_mm_walk *walk = args->private;
++ struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
++ struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
++ int old_gen, new_gen = lru_gen_from_seq(walk->max_seq);
++
++ VM_WARN_ON_ONCE(pmd_leaf(*pmd));
++
++ ptl = pte_lockptr(args->mm, pmd);
++ if (!spin_trylock(ptl))
++ return false;
++
++ arch_enter_lazy_mmu_mode();
++
++ pte = pte_offset_map(pmd, start & PMD_MASK);
++restart:
++ for (i = pte_index(start), addr = start; addr != end; i++, addr += PAGE_SIZE) {
++ unsigned long pfn;
++ struct page *page;
++
++ total++;
++ walk->mm_stats[MM_LEAF_TOTAL]++;
++
++ pfn = get_pte_pfn(pte[i], args->vma, addr);
++ if (pfn == -1)
++ continue;
++
++ if (!pte_young(pte[i])) {
++ walk->mm_stats[MM_LEAF_OLD]++;
++ continue;
++ }
++
++ page = get_pfn_page(pfn, memcg, pgdat, walk->can_swap);
++ if (!page)
++ continue;
++
++ if (!ptep_test_and_clear_young(args->vma, addr, pte + i))
++ VM_WARN_ON_ONCE(true);
++
++ young++;
++ walk->mm_stats[MM_LEAF_YOUNG]++;
++
++ if (pte_dirty(pte[i]) && !PageDirty(page) &&
++ !(PageAnon(page) && PageSwapBacked(page) &&
++ !PageSwapCache(page)))
++ set_page_dirty(page);
++
++ old_gen = page_update_gen(page, new_gen);
++ if (old_gen >= 0 && old_gen != new_gen)
++ update_batch_size(walk, page, old_gen, new_gen);
++ }
++
++ if (i < PTRS_PER_PTE && get_next_vma(PMD_MASK, PAGE_SIZE, args, &start, &end))
++ goto restart;
++
++ pte_unmap(pte);
++
++ arch_leave_lazy_mmu_mode();
++ spin_unlock(ptl);
++
++ return suitable_to_scan(total, young);
++}
++
++#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
++static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area_struct *vma,
++ struct mm_walk *args, unsigned long *bitmap, unsigned long *start)
++{
++ int i;
++ pmd_t *pmd;
++ spinlock_t *ptl;
++ struct lru_gen_mm_walk *walk = args->private;
++ struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
++ struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
++ int old_gen, new_gen = lru_gen_from_seq(walk->max_seq);
++
++ VM_WARN_ON_ONCE(pud_leaf(*pud));
++
++ /* try to batch at most 1+MIN_LRU_BATCH+1 entries */
++ if (*start == -1) {
++ *start = next;
++ return;
++ }
++
++ i = next == -1 ? 0 : pmd_index(next) - pmd_index(*start);
++ if (i && i <= MIN_LRU_BATCH) {
++ __set_bit(i - 1, bitmap);
++ return;
++ }
++
++ pmd = pmd_offset(pud, *start);
++
++ ptl = pmd_lockptr(args->mm, pmd);
++ if (!spin_trylock(ptl))
++ goto done;
++
++ arch_enter_lazy_mmu_mode();
++
++ do {
++ unsigned long pfn;
++ struct page *page;
++ unsigned long addr = i ? (*start & PMD_MASK) + i * PMD_SIZE : *start;
++
++ pfn = get_pmd_pfn(pmd[i], vma, addr);
++ if (pfn == -1)
++ goto next;
++
++ if (!pmd_trans_huge(pmd[i])) {
++ if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG))
++ pmdp_test_and_clear_young(vma, addr, pmd + i);
++ goto next;
++ }
++
++ page = get_pfn_page(pfn, memcg, pgdat, walk->can_swap);
++ if (!page)
++ goto next;
++
++ if (!pmdp_test_and_clear_young(vma, addr, pmd + i))
++ goto next;
++
++ walk->mm_stats[MM_LEAF_YOUNG]++;
++
++ if (pmd_dirty(pmd[i]) && !PageDirty(page) &&
++ !(PageAnon(page) && PageSwapBacked(page) &&
++ !PageSwapCache(page)))
++ set_page_dirty(page);
++
++ old_gen = page_update_gen(page, new_gen);
++ if (old_gen >= 0 && old_gen != new_gen)
++ update_batch_size(walk, page, old_gen, new_gen);
++next:
++ i = i > MIN_LRU_BATCH ? 0 : find_next_bit(bitmap, MIN_LRU_BATCH, i) + 1;
++ } while (i <= MIN_LRU_BATCH);
++
++ arch_leave_lazy_mmu_mode();
++ spin_unlock(ptl);
++done:
++ *start = -1;
++ bitmap_zero(bitmap, MIN_LRU_BATCH);
++}
++#else
++static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area_struct *vma,
++ struct mm_walk *args, unsigned long *bitmap, unsigned long *start)
++{
++}
++#endif
++
++static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end,
++ struct mm_walk *args)
++{
++ int i;
++ pmd_t *pmd;
++ unsigned long next;
++ unsigned long addr;
++ struct vm_area_struct *vma;
++ unsigned long pos = -1;
++ struct lru_gen_mm_walk *walk = args->private;
++ unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {};
++
++ VM_WARN_ON_ONCE(pud_leaf(*pud));
++
++ /*
++ * Finish an entire PMD in two passes: the first only reaches to PTE
++ * tables to avoid taking the PMD lock; the second, if necessary, takes
++ * the PMD lock to clear the accessed bit in PMD entries.
++ */
++ pmd = pmd_offset(pud, start & PUD_MASK);
++restart:
++ /* walk_pte_range() may call get_next_vma() */
++ vma = args->vma;
++ for (i = pmd_index(start), addr = start; addr != end; i++, addr = next) {
++ pmd_t val = pmd_read_atomic(pmd + i);
++
++ /* for pmd_read_atomic() */
++ barrier();
++
++ next = pmd_addr_end(addr, end);
++
++ if (!pmd_present(val) || is_huge_zero_pmd(val)) {
++ walk->mm_stats[MM_LEAF_TOTAL]++;
++ continue;
++ }
++
++#ifdef CONFIG_TRANSPARENT_HUGEPAGE
++ if (pmd_trans_huge(val)) {
++ unsigned long pfn = pmd_pfn(val);
++ struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
++
++ walk->mm_stats[MM_LEAF_TOTAL]++;
++
++ if (!pmd_young(val)) {
++ walk->mm_stats[MM_LEAF_OLD]++;
++ continue;
++ }
++
++ /* try to avoid unnecessary memory loads */
++ if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat))
++ continue;
++
++ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
++ continue;
++ }
++#endif
++ walk->mm_stats[MM_NONLEAF_TOTAL]++;
++
++#ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG
++ if (!pmd_young(val))
++ continue;
++
++ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
++#endif
++ if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i))
++ continue;
++
++ walk->mm_stats[MM_NONLEAF_FOUND]++;
++
++ if (!walk_pte_range(&val, addr, next, args))
++ continue;
++
++ walk->mm_stats[MM_NONLEAF_ADDED]++;
++
++ /* carry over to the next generation */
++ update_bloom_filter(walk->lruvec, walk->max_seq + 1, pmd + i);
++ }
++
++ walk_pmd_range_locked(pud, -1, vma, args, bitmap, &pos);
++
++ if (i < PTRS_PER_PMD && get_next_vma(PUD_MASK, PMD_SIZE, args, &start, &end))
++ goto restart;
++}
++
++static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end,
++ struct mm_walk *args)
++{
++ int i;
++ pud_t *pud;
++ unsigned long addr;
++ unsigned long next;
++ struct lru_gen_mm_walk *walk = args->private;
++
++ VM_WARN_ON_ONCE(p4d_leaf(*p4d));
++
++ pud = pud_offset(p4d, start & P4D_MASK);
++restart:
++ for (i = pud_index(start), addr = start; addr != end; i++, addr = next) {
++ pud_t val = READ_ONCE(pud[i]);
++
++ next = pud_addr_end(addr, end);
++
++ if (!pud_present(val) || WARN_ON_ONCE(pud_leaf(val)))
++ continue;
++
++ walk_pmd_range(&val, addr, next, args);
++
++ /* a racy check to curtail the waiting time */
++ if (wq_has_sleeper(&walk->lruvec->mm_state.wait))
++ return 1;
++
++ if (need_resched() || walk->batched >= MAX_LRU_BATCH) {
++ end = (addr | ~PUD_MASK) + 1;
++ goto done;
++ }
++ }
++
++ if (i < PTRS_PER_PUD && get_next_vma(P4D_MASK, PUD_SIZE, args, &start, &end))
++ goto restart;
++
++ end = round_up(end, P4D_SIZE);
++done:
++ if (!end || !args->vma)
++ return 1;
++
++ walk->next_addr = max(end, args->vma->vm_start);
++
++ return -EAGAIN;
++}
++
++static void walk_mm(struct lruvec *lruvec, struct mm_struct *mm, struct lru_gen_mm_walk *walk)
++{
++ static const struct mm_walk_ops mm_walk_ops = {
++ .test_walk = should_skip_vma,
++ .p4d_entry = walk_pud_range,
++ };
++
++ int err;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++
++ walk->next_addr = FIRST_USER_ADDRESS;
++
++ do {
++ err = -EBUSY;
++
++ /* page_update_gen() requires stable page_memcg() */
++ if (!mem_cgroup_trylock_pages(memcg))
++ break;
++
++ /* the caller might be holding the lock for write */
++ if (mmap_read_trylock(mm)) {
++ err = walk_page_range(mm, walk->next_addr, ULONG_MAX, &mm_walk_ops, walk);
++
++ mmap_read_unlock(mm);
++ }
++
++ mem_cgroup_unlock_pages();
++
++ if (walk->batched) {
++ spin_lock_irq(&lruvec->lru_lock);
++ reset_batch_size(lruvec, walk);
++ spin_unlock_irq(&lruvec->lru_lock);
++ }
++
++ cond_resched();
++ } while (err == -EAGAIN);
++}
++
++static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat)
++{
++ struct lru_gen_mm_walk *walk = current->reclaim_state->mm_walk;
++
++ if (pgdat && current_is_kswapd()) {
++ VM_WARN_ON_ONCE(walk);
++
++ walk = &pgdat->mm_walk;
++ } else if (!pgdat && !walk) {
++ VM_WARN_ON_ONCE(current_is_kswapd());
++
++ walk = kzalloc(sizeof(*walk), __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
++ }
++
++ current->reclaim_state->mm_walk = walk;
++
++ return walk;
++}
++
++static void clear_mm_walk(void)
++{
++ struct lru_gen_mm_walk *walk = current->reclaim_state->mm_walk;
++
++ VM_WARN_ON_ONCE(walk && memchr_inv(walk->nr_pages, 0, sizeof(walk->nr_pages)));
++ VM_WARN_ON_ONCE(walk && memchr_inv(walk->mm_stats, 0, sizeof(walk->mm_stats)));
++
++ current->reclaim_state->mm_walk = NULL;
++
++ if (!current_is_kswapd())
++ kfree(walk);
++}
++
+ static void inc_min_seq(struct lruvec *lruvec, int type)
+ {
+ struct lru_gen_struct *lrugen = &lruvec->lrugen;
+@@ -3136,7 +4001,7 @@ next:
+ return success;
+ }
+
+-static void inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, bool can_swap)
++static void inc_max_seq(struct lruvec *lruvec, bool can_swap)
+ {
+ int prev, next;
+ int type, zone;
+@@ -3146,9 +4011,6 @@ static void inc_max_seq(struct lruvec *l
+
+ VM_WARN_ON_ONCE(!seq_is_valid(lruvec));
+
+- if (max_seq != lrugen->max_seq)
+- goto unlock;
+-
+ for (type = ANON_AND_FILE - 1; type >= 0; type--) {
+ if (get_nr_gens(lruvec, type) != MAX_NR_GENS)
+ continue;
+@@ -3186,10 +4048,76 @@ static void inc_max_seq(struct lruvec *l
+
+ /* make sure preceding modifications appear */
+ smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1);
+-unlock:
++
+ spin_unlock_irq(&lruvec->lru_lock);
+ }
+
++static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq,
++ struct scan_control *sc, bool can_swap)
++{
++ bool success;
++ struct lru_gen_mm_walk *walk;
++ struct mm_struct *mm = NULL;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ VM_WARN_ON_ONCE(max_seq > READ_ONCE(lrugen->max_seq));
++
++ /* see the comment in iterate_mm_list() */
++ if (max_seq <= READ_ONCE(lruvec->mm_state.seq)) {
++ success = false;
++ goto done;
++ }
++
++ /*
++ * If the hardware doesn't automatically set the accessed bit, fallback
++ * to lru_gen_look_around(), which only clears the accessed bit in a
++ * handful of PTEs. Spreading the work out over a period of time usually
++ * is less efficient, but it avoids bursty page faults.
++ */
++ if (!arch_has_hw_pte_young()) {
++ success = iterate_mm_list_nowalk(lruvec, max_seq);
++ goto done;
++ }
++
++ walk = set_mm_walk(NULL);
++ if (!walk) {
++ success = iterate_mm_list_nowalk(lruvec, max_seq);
++ goto done;
++ }
++
++ walk->lruvec = lruvec;
++ walk->max_seq = max_seq;
++ walk->can_swap = can_swap;
++ walk->force_scan = false;
++
++ do {
++ success = iterate_mm_list(lruvec, walk, &mm);
++ if (mm)
++ walk_mm(lruvec, mm, walk);
++
++ cond_resched();
++ } while (mm);
++done:
++ if (!success) {
++ if (sc->priority <= DEF_PRIORITY - 2)
++ wait_event_killable(lruvec->mm_state.wait,
++ max_seq < READ_ONCE(lrugen->max_seq));
++
++ return max_seq < READ_ONCE(lrugen->max_seq);
++ }
++
++ VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq));
++
++ inc_max_seq(lruvec, can_swap);
++ /* either this sees any waiters or they will see updated max_seq */
++ if (wq_has_sleeper(&lruvec->mm_state.wait))
++ wake_up_all(&lruvec->mm_state.wait);
++
++ wakeup_flusher_threads(WB_REASON_VMSCAN);
++
++ return true;
++}
++
+ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsigned long *min_seq,
+ struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
+ {
+@@ -3265,7 +4193,7 @@ static void age_lruvec(struct lruvec *lr
+
+ need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan);
+ if (need_aging)
+- inc_max_seq(lruvec, max_seq, swappiness);
++ try_to_inc_max_seq(lruvec, max_seq, sc, swappiness);
+ }
+
+ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)
+@@ -3274,6 +4202,8 @@ static void lru_gen_age_node(struct pgli
+
+ VM_WARN_ON_ONCE(!current_is_kswapd());
+
++ set_mm_walk(pgdat);
++
+ memcg = mem_cgroup_iter(NULL, NULL, NULL);
+ do {
+ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat);
+@@ -3282,11 +4212,16 @@ static void lru_gen_age_node(struct pgli
+
+ cond_resched();
+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
++
++ clear_mm_walk();
+ }
+
+ /*
+ * This function exploits spatial locality when shrink_page_list() walks the
+- * rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages.
++ * rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages. If
++ * the scan was done cacheline efficiently, it adds the PMD entry pointing to
++ * the PTE table to the Bloom filter. This forms a feedback loop between the
++ * eviction and the aging.
+ */
+ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw)
+ {
+@@ -3295,6 +4230,8 @@ void lru_gen_look_around(struct page_vma
+ unsigned long start;
+ unsigned long end;
+ unsigned long addr;
++ struct lru_gen_mm_walk *walk;
++ int young = 0;
+ unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {};
+ struct page *page = pvmw->page;
+ struct mem_cgroup *memcg = page_memcg(page);
+@@ -3309,6 +4246,9 @@ void lru_gen_look_around(struct page_vma
+ if (spin_is_contended(pvmw->ptl))
+ return;
+
++ /* avoid taking the LRU lock under the PTL when possible */
++ walk = current->reclaim_state ? current->reclaim_state->mm_walk : NULL;
++
+ start = max(pvmw->address & PMD_MASK, pvmw->vma->vm_start);
+ end = min(pvmw->address | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1;
+
+@@ -3338,13 +4278,15 @@ void lru_gen_look_around(struct page_vma
+ if (!pte_young(pte[i]))
+ continue;
+
+- page = get_pfn_page(pfn, memcg, pgdat);
++ page = get_pfn_page(pfn, memcg, pgdat, !walk || walk->can_swap);
+ if (!page)
+ continue;
+
+ if (!ptep_test_and_clear_young(pvmw->vma, addr, pte + i))
+ VM_WARN_ON_ONCE(true);
+
++ young++;
++
+ if (pte_dirty(pte[i]) && !PageDirty(page) &&
+ !(PageAnon(page) && PageSwapBacked(page) &&
+ !PageSwapCache(page)))
+@@ -3360,7 +4302,11 @@ void lru_gen_look_around(struct page_vma
+ arch_leave_lazy_mmu_mode();
+ rcu_read_unlock();
+
+- if (bitmap_weight(bitmap, MIN_LRU_BATCH) < PAGEVEC_SIZE) {
++ /* feedback from rmap walkers to page table walkers */
++ if (suitable_to_scan(i, young))
++ update_bloom_filter(lruvec, max_seq, pvmw->pmd);
++
++ if (!walk && bitmap_weight(bitmap, MIN_LRU_BATCH) < PAGEVEC_SIZE) {
+ for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
+ page = pte_page(pte[i]);
+ activate_page(page);
+@@ -3372,8 +4318,10 @@ void lru_gen_look_around(struct page_vma
+ if (!mem_cgroup_trylock_pages(memcg))
+ return;
+
+- spin_lock_irq(&lruvec->lru_lock);
+- new_gen = lru_gen_from_seq(lruvec->lrugen.max_seq);
++ if (!walk) {
++ spin_lock_irq(&lruvec->lru_lock);
++ new_gen = lru_gen_from_seq(lruvec->lrugen.max_seq);
++ }
+
+ for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
+ page = compound_head(pte_page(pte[i]));
+@@ -3384,10 +4332,14 @@ void lru_gen_look_around(struct page_vma
+ if (old_gen < 0 || old_gen == new_gen)
+ continue;
+
+- lru_gen_update_size(lruvec, page, old_gen, new_gen);
++ if (walk)
++ update_batch_size(walk, page, old_gen, new_gen);
++ else
++ lru_gen_update_size(lruvec, page, old_gen, new_gen);
+ }
+
+- spin_unlock_irq(&lruvec->lru_lock);
++ if (!walk)
++ spin_unlock_irq(&lruvec->lru_lock);
+
+ mem_cgroup_unlock_pages();
+ }
+@@ -3670,6 +4622,7 @@ static int evict_pages(struct lruvec *lr
+ struct page *page;
+ enum vm_event_item item;
+ struct reclaim_stat stat;
++ struct lru_gen_mm_walk *walk;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+
+@@ -3706,6 +4659,10 @@ static int evict_pages(struct lruvec *lr
+
+ move_pages_to_lru(lruvec, &list);
+
++ walk = current->reclaim_state->mm_walk;
++ if (walk && walk->batched)
++ reset_batch_size(lruvec, walk);
++
+ item = current_is_kswapd() ? PGSTEAL_KSWAPD : PGSTEAL_DIRECT;
+ if (!cgroup_reclaim(sc))
+ __count_vm_events(item, reclaimed);
+@@ -3722,6 +4679,11 @@ static int evict_pages(struct lruvec *lr
+ return scanned;
+ }
+
++/*
++ * For future optimizations:
++ * 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg
++ * reclaim.
++ */
+ static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
+ bool can_swap)
+ {
+@@ -3747,7 +4709,8 @@ static unsigned long get_nr_to_scan(stru
+ if (current_is_kswapd())
+ return 0;
+
+- inc_max_seq(lruvec, max_seq, can_swap);
++ if (try_to_inc_max_seq(lruvec, max_seq, sc, can_swap))
++ return nr_to_scan;
+ done:
+ return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
+ }
+@@ -3761,6 +4724,8 @@ static void lru_gen_shrink_lruvec(struct
+
+ blk_start_plug(&plug);
+
++ set_mm_walk(lruvec_pgdat(lruvec));
++
+ while (true) {
+ int delta;
+ int swappiness;
+@@ -3788,6 +4753,8 @@ static void lru_gen_shrink_lruvec(struct
+ cond_resched();
+ }
+
++ clear_mm_walk();
++
+ blk_finish_plug(&plug);
+ }
+
+@@ -3804,15 +4771,21 @@ void lru_gen_init_lruvec(struct lruvec *
+
+ for_each_gen_type_zone(gen, type, zone)
+ INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]);
++
++ lruvec->mm_state.seq = MIN_NR_GENS;
++ init_waitqueue_head(&lruvec->mm_state.wait);
+ }
+
+ #ifdef CONFIG_MEMCG
+ void lru_gen_init_memcg(struct mem_cgroup *memcg)
+ {
++ INIT_LIST_HEAD(&memcg->mm_list.fifo);
++ spin_lock_init(&memcg->mm_list.lock);
+ }
+
+ void lru_gen_exit_memcg(struct mem_cgroup *memcg)
+ {
++ int i;
+ int nid;
+
+ for_each_node(nid) {
+@@ -3820,6 +4793,11 @@ void lru_gen_exit_memcg(struct mem_cgrou
+
+ VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0,
+ sizeof(lruvec->lrugen.nr_pages)));
++
++ for (i = 0; i < NR_BLOOM_FILTERS; i++) {
++ bitmap_free(lruvec->mm_state.filters[i]);
++ lruvec->mm_state.filters[i] = NULL;
++ }
+ }
+ }
+ #endif
diff --git a/target/linux/generic/backport-6.1/020-v6.1-09-mm-multi-gen-LRU-optimize-multiple-memcgs.patch b/target/linux/generic/backport-6.1/020-v6.1-09-mm-multi-gen-LRU-optimize-multiple-memcgs.patch
new file mode 100644
index 0000000000..b5fb195151
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-09-mm-multi-gen-LRU-optimize-multiple-memcgs.patch
@@ -0,0 +1,315 @@
+From 36a18a68ea458e8f4db2ca86b00091daf32c6c74 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:06 -0600
+Subject: [PATCH 09/29] mm: multi-gen LRU: optimize multiple memcgs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When multiple memcgs are available, it is possible to use generations as a
+frame of reference to make better choices and improve overall performance
+under global memory pressure. This patch adds a basic optimization to
+select memcgs that can drop single-use unmapped clean pages first. Doing
+so reduces the chance of going into the aging path or swapping, which can
+be costly.
+
+A typical example that benefits from this optimization is a server running
+mixed types of workloads, e.g., heavy anon workload in one memcg and heavy
+buffered I/O workload in the other.
+
+Though this optimization can be applied to both kswapd and direct reclaim,
+it is only added to kswapd to keep the patchset manageable. Later
+improvements may cover the direct reclaim path.
+
+While ensuring certain fairness to all eligible memcgs, proportional scans
+of individual memcgs also require proper backoff to avoid overshooting
+their aggregate reclaim target by too much. Otherwise it can cause high
+direct reclaim latency. The conditions for backoff are:
+
+1. At low priorities, for direct reclaim, if aging fairness or direct
+ reclaim latency is at risk, i.e., aging one memcg multiple times or
+ swapping after the target is met.
+2. At high priorities, for global reclaim, if per-zone free pages are
+ above respective watermarks.
+
+Server benchmark results:
+ Mixed workloads:
+ fio (buffered I/O): +[19, 21]%
+ IOPS BW
+ patch1-8: 1880k 7343MiB/s
+ patch1-9: 2252k 8796MiB/s
+
+ memcached (anon): +[119, 123]%
+ Ops/sec KB/sec
+ patch1-8: 862768.65 33514.68
+ patch1-9: 1911022.12 74234.54
+
+ Mixed workloads:
+ fio (buffered I/O): +[75, 77]%
+ IOPS BW
+ 5.19-rc1: 1279k 4996MiB/s
+ patch1-9: 2252k 8796MiB/s
+
+ memcached (anon): +[13, 15]%
+ Ops/sec KB/sec
+ 5.19-rc1: 1673524.04 65008.87
+ patch1-9: 1911022.12 74234.54
+
+ Configurations:
+ (changes since patch 6)
+
+ cat mixed.sh
+ modprobe brd rd_nr=2 rd_size=56623104
+
+ swapoff -a
+ mkswap /dev/ram0
+ swapon /dev/ram0
+
+ mkfs.ext4 /dev/ram1
+ mount -t ext4 /dev/ram1 /mnt
+
+ memtier_benchmark -S /var/run/memcached/memcached.sock \
+ -P memcache_binary -n allkeys --key-minimum=1 \
+ --key-maximum=50000000 --key-pattern=P:P -c 1 -t 36 \
+ --ratio 1:0 --pipeline 8 -d 2000
+
+ fio -name=mglru --numjobs=36 --directory=/mnt --size=1408m \
+ --buffered=1 --ioengine=io_uring --iodepth=128 \
+ --iodepth_batch_submit=32 --iodepth_batch_complete=32 \
+ --rw=randread --random_distribution=random --norandommap \
+ --time_based --ramp_time=10m --runtime=90m --group_reporting &
+ pid=$!
+
+ sleep 200
+
+ memtier_benchmark -S /var/run/memcached/memcached.sock \
+ -P memcache_binary -n allkeys --key-minimum=1 \
+ --key-maximum=50000000 --key-pattern=R:R -c 1 -t 36 \
+ --ratio 0:1 --pipeline 8 --randomize --distinct-client-seed
+
+ kill -INT $pid
+ wait
+
+Client benchmark results:
+ no change (CONFIG_MEMCG=n)
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-10-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Barry Song <baohua@kernel.org>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 96 insertions(+), 9 deletions(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -127,6 +127,12 @@ struct scan_control {
+ /* Always discard instead of demoting to lower tier memory */
+ unsigned int no_demotion:1;
+
++#ifdef CONFIG_LRU_GEN
++ /* help kswapd make better choices among multiple memcgs */
++ unsigned int memcgs_need_aging:1;
++ unsigned long last_reclaimed;
++#endif
++
+ /* Allocation order */
+ s8 order;
+
+@@ -4202,6 +4208,19 @@ static void lru_gen_age_node(struct pgli
+
+ VM_WARN_ON_ONCE(!current_is_kswapd());
+
++ sc->last_reclaimed = sc->nr_reclaimed;
++
++ /*
++ * To reduce the chance of going into the aging path, which can be
++ * costly, optimistically skip it if the flag below was cleared in the
++ * eviction path. This improves the overall performance when multiple
++ * memcgs are available.
++ */
++ if (!sc->memcgs_need_aging) {
++ sc->memcgs_need_aging = true;
++ return;
++ }
++
+ set_mm_walk(pgdat);
+
+ memcg = mem_cgroup_iter(NULL, NULL, NULL);
+@@ -4613,7 +4632,8 @@ static int isolate_pages(struct lruvec *
+ return scanned;
+ }
+
+-static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness)
++static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness,
++ bool *need_swapping)
+ {
+ int type;
+ int scanned;
+@@ -4676,6 +4696,9 @@ static int evict_pages(struct lruvec *lr
+
+ sc->nr_reclaimed += reclaimed;
+
++ if (need_swapping && type == LRU_GEN_ANON)
++ *need_swapping = true;
++
+ return scanned;
+ }
+
+@@ -4685,9 +4708,8 @@ static int evict_pages(struct lruvec *lr
+ * reclaim.
+ */
+ static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
+- bool can_swap)
++ bool can_swap, bool *need_aging)
+ {
+- bool need_aging;
+ unsigned long nr_to_scan;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ DEFINE_MAX_SEQ(lruvec);
+@@ -4697,8 +4719,8 @@ static unsigned long get_nr_to_scan(stru
+ (mem_cgroup_below_low(memcg) && !sc->memcg_low_reclaim))
+ return 0;
+
+- need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, can_swap, &nr_to_scan);
+- if (!need_aging)
++ *need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, can_swap, &nr_to_scan);
++ if (!*need_aging)
+ return nr_to_scan;
+
+ /* skip the aging path at the default priority */
+@@ -4715,10 +4737,68 @@ done:
+ return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
+ }
+
++static bool should_abort_scan(struct lruvec *lruvec, unsigned long seq,
++ struct scan_control *sc, bool need_swapping)
++{
++ int i;
++ DEFINE_MAX_SEQ(lruvec);
++
++ if (!current_is_kswapd()) {
++ /* age each memcg once to ensure fairness */
++ if (max_seq - seq > 1)
++ return true;
++
++ /* over-swapping can increase allocation latency */
++ if (sc->nr_reclaimed >= sc->nr_to_reclaim && need_swapping)
++ return true;
++
++ /* give this thread a chance to exit and free its memory */
++ if (fatal_signal_pending(current)) {
++ sc->nr_reclaimed += MIN_LRU_BATCH;
++ return true;
++ }
++
++ if (cgroup_reclaim(sc))
++ return false;
++ } else if (sc->nr_reclaimed - sc->last_reclaimed < sc->nr_to_reclaim)
++ return false;
++
++ /* keep scanning at low priorities to ensure fairness */
++ if (sc->priority > DEF_PRIORITY - 2)
++ return false;
++
++ /*
++ * A minimum amount of work was done under global memory pressure. For
++ * kswapd, it may be overshooting. For direct reclaim, the target isn't
++ * met, and yet the allocation may still succeed, since kswapd may have
++ * caught up. In either case, it's better to stop now, and restart if
++ * necessary.
++ */
++ for (i = 0; i <= sc->reclaim_idx; i++) {
++ unsigned long wmark;
++ struct zone *zone = lruvec_pgdat(lruvec)->node_zones + i;
++
++ if (!managed_zone(zone))
++ continue;
++
++ wmark = current_is_kswapd() ? high_wmark_pages(zone) : low_wmark_pages(zone);
++ if (wmark > zone_page_state(zone, NR_FREE_PAGES))
++ return false;
++ }
++
++ sc->nr_reclaimed += MIN_LRU_BATCH;
++
++ return true;
++}
++
+ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
+ {
+ struct blk_plug plug;
++ bool need_aging = false;
++ bool need_swapping = false;
+ unsigned long scanned = 0;
++ unsigned long reclaimed = sc->nr_reclaimed;
++ DEFINE_MAX_SEQ(lruvec);
+
+ lru_add_drain();
+
+@@ -4738,21 +4818,28 @@ static void lru_gen_shrink_lruvec(struct
+ else
+ swappiness = 0;
+
+- nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
++ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness, &need_aging);
+ if (!nr_to_scan)
+- break;
++ goto done;
+
+- delta = evict_pages(lruvec, sc, swappiness);
++ delta = evict_pages(lruvec, sc, swappiness, &need_swapping);
+ if (!delta)
+- break;
++ goto done;
+
+ scanned += delta;
+ if (scanned >= nr_to_scan)
+ break;
+
++ if (should_abort_scan(lruvec, max_seq, sc, need_swapping))
++ break;
++
+ cond_resched();
+ }
+
++ /* see the comment in lru_gen_age_node() */
++ if (sc->nr_reclaimed - reclaimed >= MIN_LRU_BATCH && !need_aging)
++ sc->memcgs_need_aging = false;
++done:
+ clear_mm_walk();
+
+ blk_finish_plug(&plug);
diff --git a/target/linux/generic/backport-6.1/020-v6.1-10-mm-multi-gen-LRU-kill-switch.patch b/target/linux/generic/backport-6.1/020-v6.1-10-mm-multi-gen-LRU-kill-switch.patch
new file mode 100644
index 0000000000..cf5b8f0e9f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-10-mm-multi-gen-LRU-kill-switch.patch
@@ -0,0 +1,498 @@
+From 640db3a029dca909af47157ca18f52b29d34a1b9 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:07 -0600
+Subject: [PATCH 10/29] mm: multi-gen LRU: kill switch
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add /sys/kernel/mm/lru_gen/enabled as a kill switch. Components that
+can be disabled include:
+ 0x0001: the multi-gen LRU core
+ 0x0002: walking page table, when arch_has_hw_pte_young() returns
+ true
+ 0x0004: clearing the accessed bit in non-leaf PMD entries, when
+ CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y
+ [yYnN]: apply to all the components above
+E.g.,
+ echo y >/sys/kernel/mm/lru_gen/enabled
+ cat /sys/kernel/mm/lru_gen/enabled
+ 0x0007
+ echo 5 >/sys/kernel/mm/lru_gen/enabled
+ cat /sys/kernel/mm/lru_gen/enabled
+ 0x0005
+
+NB: the page table walks happen on the scale of seconds under heavy memory
+pressure, in which case the mmap_lock contention is a lesser concern,
+compared with the LRU lock contention and the I/O congestion. So far the
+only well-known case of the mmap_lock contention happens on Android, due
+to Scudo [1] which allocates several thousand VMAs for merely a few
+hundred MBs. The SPF and the Maple Tree also have provided their own
+assessments [2][3]. However, if walking page tables does worsen the
+mmap_lock contention, the kill switch can be used to disable it. In this
+case the multi-gen LRU will suffer a minor performance degradation, as
+shown previously.
+
+Clearing the accessed bit in non-leaf PMD entries can also be disabled,
+since this behavior was not tested on x86 varieties other than Intel and
+AMD.
+
+[1] https://source.android.com/devices/tech/debug/scudo
+[2] https://lore.kernel.org/r/20220128131006.67712-1-michel@lespinasse.org/
+[3] https://lore.kernel.org/r/20220426150616.3937571-1-Liam.Howlett@oracle.com/
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-11-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Barry Song <baohua@kernel.org>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/cgroup.h | 15 ++-
+ include/linux/mm_inline.h | 15 ++-
+ include/linux/mmzone.h | 9 ++
+ kernel/cgroup/cgroup-internal.h | 1 -
+ mm/Kconfig | 6 +
+ mm/vmscan.c | 228 +++++++++++++++++++++++++++++++-
+ 6 files changed, 265 insertions(+), 9 deletions(-)
+
+--- a/include/linux/cgroup.h
++++ b/include/linux/cgroup.h
+@@ -433,6 +433,18 @@ static inline void cgroup_put(struct cgr
+ css_put(&cgrp->self);
+ }
+
++extern struct mutex cgroup_mutex;
++
++static inline void cgroup_lock(void)
++{
++ mutex_lock(&cgroup_mutex);
++}
++
++static inline void cgroup_unlock(void)
++{
++ mutex_unlock(&cgroup_mutex);
++}
++
+ /**
+ * task_css_set_check - obtain a task's css_set with extra access conditions
+ * @task: the task to obtain css_set for
+@@ -447,7 +459,6 @@ static inline void cgroup_put(struct cgr
+ * as locks used during the cgroup_subsys::attach() methods.
+ */
+ #ifdef CONFIG_PROVE_RCU
+-extern struct mutex cgroup_mutex;
+ extern spinlock_t css_set_lock;
+ #define task_css_set_check(task, __c) \
+ rcu_dereference_check((task)->cgroups, \
+@@ -708,6 +719,8 @@ struct cgroup;
+ static inline u64 cgroup_id(const struct cgroup *cgrp) { return 1; }
+ static inline void css_get(struct cgroup_subsys_state *css) {}
+ static inline void css_put(struct cgroup_subsys_state *css) {}
++static inline void cgroup_lock(void) {}
++static inline void cgroup_unlock(void) {}
+ static inline int cgroup_attach_task_all(struct task_struct *from,
+ struct task_struct *t) { return 0; }
+ static inline int cgroupstats_build(struct cgroupstats *stats,
+--- a/include/linux/mm_inline.h
++++ b/include/linux/mm_inline.h
+@@ -91,10 +91,21 @@ static __always_inline enum lru_list pag
+
+ #ifdef CONFIG_LRU_GEN
+
++#ifdef CONFIG_LRU_GEN_ENABLED
+ static inline bool lru_gen_enabled(void)
+ {
+- return true;
++ DECLARE_STATIC_KEY_TRUE(lru_gen_caps[NR_LRU_GEN_CAPS]);
++
++ return static_branch_likely(&lru_gen_caps[LRU_GEN_CORE]);
++}
++#else
++static inline bool lru_gen_enabled(void)
++{
++ DECLARE_STATIC_KEY_FALSE(lru_gen_caps[NR_LRU_GEN_CAPS]);
++
++ return static_branch_unlikely(&lru_gen_caps[LRU_GEN_CORE]);
+ }
++#endif
+
+ static inline bool lru_gen_in_fault(void)
+ {
+@@ -207,7 +218,7 @@ static inline bool lru_gen_add_page(stru
+
+ VM_WARN_ON_ONCE_PAGE(gen != -1, page);
+
+- if (PageUnevictable(page))
++ if (PageUnevictable(page) || !lrugen->enabled)
+ return false;
+ /*
+ * There are three common cases for this page:
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -364,6 +364,13 @@ enum {
+ LRU_GEN_FILE,
+ };
+
++enum {
++ LRU_GEN_CORE,
++ LRU_GEN_MM_WALK,
++ LRU_GEN_NONLEAF_YOUNG,
++ NR_LRU_GEN_CAPS
++};
++
+ #define MIN_LRU_BATCH BITS_PER_LONG
+ #define MAX_LRU_BATCH (MIN_LRU_BATCH * 64)
+
+@@ -405,6 +412,8 @@ struct lru_gen_struct {
+ /* can be modified without holding the LRU lock */
+ atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
+ atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
++ /* whether the multi-gen LRU is enabled */
++ bool enabled;
+ };
+
+ enum {
+--- a/kernel/cgroup/cgroup-internal.h
++++ b/kernel/cgroup/cgroup-internal.h
+@@ -165,7 +165,6 @@ struct cgroup_mgctx {
+ #define DEFINE_CGROUP_MGCTX(name) \
+ struct cgroup_mgctx name = CGROUP_MGCTX_INIT(name)
+
+-extern struct mutex cgroup_mutex;
+ extern spinlock_t css_set_lock;
+ extern struct cgroup_subsys *cgroup_subsys[];
+ extern struct list_head cgroup_roots;
+--- a/mm/Kconfig
++++ b/mm/Kconfig
+@@ -906,6 +906,12 @@ config LRU_GEN
+ help
+ A high performance LRU implementation to overcommit memory.
+
++config LRU_GEN_ENABLED
++ bool "Enable by default"
++ depends on LRU_GEN
++ help
++ This option enables the multi-gen LRU by default.
++
+ config LRU_GEN_STATS
+ bool "Full stats for debugging"
+ depends on LRU_GEN
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -52,6 +52,7 @@
+ #include <linux/psi.h>
+ #include <linux/pagewalk.h>
+ #include <linux/shmem_fs.h>
++#include <linux/ctype.h>
+
+ #include <asm/tlbflush.h>
+ #include <asm/div64.h>
+@@ -2841,6 +2842,14 @@ static bool can_age_anon_pages(struct pg
+
+ #ifdef CONFIG_LRU_GEN
+
++#ifdef CONFIG_LRU_GEN_ENABLED
++DEFINE_STATIC_KEY_ARRAY_TRUE(lru_gen_caps, NR_LRU_GEN_CAPS);
++#define get_cap(cap) static_branch_likely(&lru_gen_caps[cap])
++#else
++DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_caps, NR_LRU_GEN_CAPS);
++#define get_cap(cap) static_branch_unlikely(&lru_gen_caps[cap])
++#endif
++
+ /******************************************************************************
+ * shorthand helpers
+ ******************************************************************************/
+@@ -3717,7 +3726,8 @@ static void walk_pmd_range_locked(pud_t
+ goto next;
+
+ if (!pmd_trans_huge(pmd[i])) {
+- if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG))
++ if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) &&
++ get_cap(LRU_GEN_NONLEAF_YOUNG))
+ pmdp_test_and_clear_young(vma, addr, pmd + i);
+ goto next;
+ }
+@@ -3815,10 +3825,12 @@ restart:
+ walk->mm_stats[MM_NONLEAF_TOTAL]++;
+
+ #ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG
+- if (!pmd_young(val))
+- continue;
++ if (get_cap(LRU_GEN_NONLEAF_YOUNG)) {
++ if (!pmd_young(val))
++ continue;
+
+- walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
++ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
++ }
+ #endif
+ if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i))
+ continue;
+@@ -4080,7 +4092,7 @@ static bool try_to_inc_max_seq(struct lr
+ * handful of PTEs. Spreading the work out over a period of time usually
+ * is less efficient, but it avoids bursty page faults.
+ */
+- if (!arch_has_hw_pte_young()) {
++ if (!(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) {
+ success = iterate_mm_list_nowalk(lruvec, max_seq);
+ goto done;
+ }
+@@ -4846,6 +4858,208 @@ done:
+ }
+
+ /******************************************************************************
++ * state change
++ ******************************************************************************/
++
++static bool __maybe_unused state_is_valid(struct lruvec *lruvec)
++{
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ if (lrugen->enabled) {
++ enum lru_list lru;
++
++ for_each_evictable_lru(lru) {
++ if (!list_empty(&lruvec->lists[lru]))
++ return false;
++ }
++ } else {
++ int gen, type, zone;
++
++ for_each_gen_type_zone(gen, type, zone) {
++ if (!list_empty(&lrugen->lists[gen][type][zone]))
++ return false;
++ }
++ }
++
++ return true;
++}
++
++static bool fill_evictable(struct lruvec *lruvec)
++{
++ enum lru_list lru;
++ int remaining = MAX_LRU_BATCH;
++
++ for_each_evictable_lru(lru) {
++ int type = is_file_lru(lru);
++ bool active = is_active_lru(lru);
++ struct list_head *head = &lruvec->lists[lru];
++
++ while (!list_empty(head)) {
++ bool success;
++ struct page *page = lru_to_page(head);
++
++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page);
++ VM_WARN_ON_ONCE_PAGE(PageActive(page) != active, page);
++ VM_WARN_ON_ONCE_PAGE(page_is_file_lru(page) != type, page);
++ VM_WARN_ON_ONCE_PAGE(page_lru_gen(page) != -1, page);
++
++ del_page_from_lru_list(page, lruvec);
++ success = lru_gen_add_page(lruvec, page, false);
++ VM_WARN_ON_ONCE(!success);
++
++ if (!--remaining)
++ return false;
++ }
++ }
++
++ return true;
++}
++
++static bool drain_evictable(struct lruvec *lruvec)
++{
++ int gen, type, zone;
++ int remaining = MAX_LRU_BATCH;
++
++ for_each_gen_type_zone(gen, type, zone) {
++ struct list_head *head = &lruvec->lrugen.lists[gen][type][zone];
++
++ while (!list_empty(head)) {
++ bool success;
++ struct page *page = lru_to_page(head);
++
++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page);
++ VM_WARN_ON_ONCE_PAGE(PageActive(page), page);
++ VM_WARN_ON_ONCE_PAGE(page_is_file_lru(page) != type, page);
++ VM_WARN_ON_ONCE_PAGE(page_zonenum(page) != zone, page);
++
++ success = lru_gen_del_page(lruvec, page, false);
++ VM_WARN_ON_ONCE(!success);
++ add_page_to_lru_list(page, lruvec);
++
++ if (!--remaining)
++ return false;
++ }
++ }
++
++ return true;
++}
++
++static void lru_gen_change_state(bool enabled)
++{
++ static DEFINE_MUTEX(state_mutex);
++
++ struct mem_cgroup *memcg;
++
++ cgroup_lock();
++ cpus_read_lock();
++ get_online_mems();
++ mutex_lock(&state_mutex);
++
++ if (enabled == lru_gen_enabled())
++ goto unlock;
++
++ if (enabled)
++ static_branch_enable_cpuslocked(&lru_gen_caps[LRU_GEN_CORE]);
++ else
++ static_branch_disable_cpuslocked(&lru_gen_caps[LRU_GEN_CORE]);
++
++ memcg = mem_cgroup_iter(NULL, NULL, NULL);
++ do {
++ int nid;
++
++ for_each_node(nid) {
++ struct lruvec *lruvec = get_lruvec(memcg, nid);
++
++ if (!lruvec)
++ continue;
++
++ spin_lock_irq(&lruvec->lru_lock);
++
++ VM_WARN_ON_ONCE(!seq_is_valid(lruvec));
++ VM_WARN_ON_ONCE(!state_is_valid(lruvec));
++
++ lruvec->lrugen.enabled = enabled;
++
++ while (!(enabled ? fill_evictable(lruvec) : drain_evictable(lruvec))) {
++ spin_unlock_irq(&lruvec->lru_lock);
++ cond_resched();
++ spin_lock_irq(&lruvec->lru_lock);
++ }
++
++ spin_unlock_irq(&lruvec->lru_lock);
++ }
++
++ cond_resched();
++ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
++unlock:
++ mutex_unlock(&state_mutex);
++ put_online_mems();
++ cpus_read_unlock();
++ cgroup_unlock();
++}
++
++/******************************************************************************
++ * sysfs interface
++ ******************************************************************************/
++
++static ssize_t show_enabled(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
++{
++ unsigned int caps = 0;
++
++ if (get_cap(LRU_GEN_CORE))
++ caps |= BIT(LRU_GEN_CORE);
++
++ if (arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))
++ caps |= BIT(LRU_GEN_MM_WALK);
++
++ if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) && get_cap(LRU_GEN_NONLEAF_YOUNG))
++ caps |= BIT(LRU_GEN_NONLEAF_YOUNG);
++
++ return snprintf(buf, PAGE_SIZE, "0x%04x\n", caps);
++}
++
++static ssize_t store_enabled(struct kobject *kobj, struct kobj_attribute *attr,
++ const char *buf, size_t len)
++{
++ int i;
++ unsigned int caps;
++
++ if (tolower(*buf) == 'n')
++ caps = 0;
++ else if (tolower(*buf) == 'y')
++ caps = -1;
++ else if (kstrtouint(buf, 0, &caps))
++ return -EINVAL;
++
++ for (i = 0; i < NR_LRU_GEN_CAPS; i++) {
++ bool enabled = caps & BIT(i);
++
++ if (i == LRU_GEN_CORE)
++ lru_gen_change_state(enabled);
++ else if (enabled)
++ static_branch_enable(&lru_gen_caps[i]);
++ else
++ static_branch_disable(&lru_gen_caps[i]);
++ }
++
++ return len;
++}
++
++static struct kobj_attribute lru_gen_enabled_attr = __ATTR(
++ enabled, 0644, show_enabled, store_enabled
++);
++
++static struct attribute *lru_gen_attrs[] = {
++ &lru_gen_enabled_attr.attr,
++ NULL
++};
++
++static struct attribute_group lru_gen_attr_group = {
++ .name = "lru_gen",
++ .attrs = lru_gen_attrs,
++};
++
++/******************************************************************************
+ * initialization
+ ******************************************************************************/
+
+@@ -4855,6 +5069,7 @@ void lru_gen_init_lruvec(struct lruvec *
+ struct lru_gen_struct *lrugen = &lruvec->lrugen;
+
+ lrugen->max_seq = MIN_NR_GENS + 1;
++ lrugen->enabled = lru_gen_enabled();
+
+ for_each_gen_type_zone(gen, type, zone)
+ INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]);
+@@ -4894,6 +5109,9 @@ static int __init init_lru_gen(void)
+ BUILD_BUG_ON(MIN_NR_GENS + 1 >= MAX_NR_GENS);
+ BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS);
+
++ if (sysfs_create_group(mm_kobj, &lru_gen_attr_group))
++ pr_err("lru_gen: failed to create sysfs group\n");
++
+ return 0;
+ };
+ late_initcall(init_lru_gen);
diff --git a/target/linux/generic/backport-6.1/020-v6.1-11-mm-multi-gen-LRU-thrashing-prevention.patch b/target/linux/generic/backport-6.1/020-v6.1-11-mm-multi-gen-LRU-thrashing-prevention.patch
new file mode 100644
index 0000000000..30e20aff6e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-11-mm-multi-gen-LRU-thrashing-prevention.patch
@@ -0,0 +1,226 @@
+From 73d1ff551760f0c79c47ab70faa4c2ca91413f5c Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:08 -0600
+Subject: [PATCH 11/29] mm: multi-gen LRU: thrashing prevention
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add /sys/kernel/mm/lru_gen/min_ttl_ms for thrashing prevention, as
+requested by many desktop users [1].
+
+When set to value N, it prevents the working set of N milliseconds from
+getting evicted. The OOM killer is triggered if this working set cannot
+be kept in memory. Based on the average human detectable lag (~100ms),
+N=1000 usually eliminates intolerable lags due to thrashing. Larger
+values like N=3000 make lags less noticeable at the risk of premature OOM
+kills.
+
+Compared with the size-based approach [2], this time-based approach
+has the following advantages:
+
+1. It is easier to configure because it is agnostic to applications
+ and memory sizes.
+2. It is more reliable because it is directly wired to the OOM killer.
+
+[1] https://lore.kernel.org/r/Ydza%2FzXKY9ATRoh6@google.com/
+[2] https://lore.kernel.org/r/20101028191523.GA14972@google.com/
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-12-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Barry Song <baohua@kernel.org>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Qi Zheng <zhengqi.arch@bytedance.com>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/mmzone.h | 2 ++
+ mm/vmscan.c | 74 ++++++++++++++++++++++++++++++++++++++++--
+ 2 files changed, 73 insertions(+), 3 deletions(-)
+
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -399,6 +399,8 @@ struct lru_gen_struct {
+ unsigned long max_seq;
+ /* the eviction increments the oldest generation numbers */
+ unsigned long min_seq[ANON_AND_FILE];
++ /* the birth time of each generation in jiffies */
++ unsigned long timestamps[MAX_NR_GENS];
+ /* the multi-gen LRU lists, lazily sorted on eviction */
+ struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
+ /* the multi-gen LRU sizes, eventually consistent */
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -4064,6 +4064,7 @@ static void inc_max_seq(struct lruvec *l
+ for (type = 0; type < ANON_AND_FILE; type++)
+ reset_ctrl_pos(lruvec, type, false);
+
++ WRITE_ONCE(lrugen->timestamps[next], jiffies);
+ /* make sure preceding modifications appear */
+ smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1);
+
+@@ -4193,7 +4194,7 @@ static bool should_run_aging(struct lruv
+ return false;
+ }
+
+-static void age_lruvec(struct lruvec *lruvec, struct scan_control *sc)
++static bool age_lruvec(struct lruvec *lruvec, struct scan_control *sc, unsigned long min_ttl)
+ {
+ bool need_aging;
+ unsigned long nr_to_scan;
+@@ -4207,16 +4208,36 @@ static void age_lruvec(struct lruvec *lr
+ mem_cgroup_calculate_protection(NULL, memcg);
+
+ if (mem_cgroup_below_min(memcg))
+- return;
++ return false;
+
+ need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan);
++
++ if (min_ttl) {
++ int gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
++ unsigned long birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
++
++ if (time_is_after_jiffies(birth + min_ttl))
++ return false;
++
++ /* the size is likely too small to be helpful */
++ if (!nr_to_scan && sc->priority != DEF_PRIORITY)
++ return false;
++ }
++
+ if (need_aging)
+ try_to_inc_max_seq(lruvec, max_seq, sc, swappiness);
++
++ return true;
+ }
+
++/* to protect the working set of the last N jiffies */
++static unsigned long lru_gen_min_ttl __read_mostly;
++
+ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)
+ {
+ struct mem_cgroup *memcg;
++ bool success = false;
++ unsigned long min_ttl = READ_ONCE(lru_gen_min_ttl);
+
+ VM_WARN_ON_ONCE(!current_is_kswapd());
+
+@@ -4239,12 +4260,32 @@ static void lru_gen_age_node(struct pgli
+ do {
+ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat);
+
+- age_lruvec(lruvec, sc);
++ if (age_lruvec(lruvec, sc, min_ttl))
++ success = true;
+
+ cond_resched();
+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
+
+ clear_mm_walk();
++
++ /* check the order to exclude compaction-induced reclaim */
++ if (success || !min_ttl || sc->order)
++ return;
++
++ /*
++ * The main goal is to OOM kill if every generation from all memcgs is
++ * younger than min_ttl. However, another possibility is all memcgs are
++ * either below min or empty.
++ */
++ if (mutex_trylock(&oom_lock)) {
++ struct oom_control oc = {
++ .gfp_mask = sc->gfp_mask,
++ };
++
++ out_of_memory(&oc);
++
++ mutex_unlock(&oom_lock);
++ }
+ }
+
+ /*
+@@ -5002,6 +5043,28 @@ unlock:
+ * sysfs interface
+ ******************************************************************************/
+
++static ssize_t show_min_ttl(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
++{
++ return sprintf(buf, "%u\n", jiffies_to_msecs(READ_ONCE(lru_gen_min_ttl)));
++}
++
++static ssize_t store_min_ttl(struct kobject *kobj, struct kobj_attribute *attr,
++ const char *buf, size_t len)
++{
++ unsigned int msecs;
++
++ if (kstrtouint(buf, 0, &msecs))
++ return -EINVAL;
++
++ WRITE_ONCE(lru_gen_min_ttl, msecs_to_jiffies(msecs));
++
++ return len;
++}
++
++static struct kobj_attribute lru_gen_min_ttl_attr = __ATTR(
++ min_ttl_ms, 0644, show_min_ttl, store_min_ttl
++);
++
+ static ssize_t show_enabled(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+ {
+ unsigned int caps = 0;
+@@ -5050,6 +5113,7 @@ static struct kobj_attribute lru_gen_ena
+ );
+
+ static struct attribute *lru_gen_attrs[] = {
++ &lru_gen_min_ttl_attr.attr,
+ &lru_gen_enabled_attr.attr,
+ NULL
+ };
+@@ -5065,12 +5129,16 @@ static struct attribute_group lru_gen_at
+
+ void lru_gen_init_lruvec(struct lruvec *lruvec)
+ {
++ int i;
+ int gen, type, zone;
+ struct lru_gen_struct *lrugen = &lruvec->lrugen;
+
+ lrugen->max_seq = MIN_NR_GENS + 1;
+ lrugen->enabled = lru_gen_enabled();
+
++ for (i = 0; i <= MIN_NR_GENS + 1; i++)
++ lrugen->timestamps[i] = jiffies;
++
+ for_each_gen_type_zone(gen, type, zone)
+ INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]);
+
diff --git a/target/linux/generic/backport-6.1/020-v6.1-12-mm-multi-gen-LRU-debugfs-interface.patch b/target/linux/generic/backport-6.1/020-v6.1-12-mm-multi-gen-LRU-debugfs-interface.patch
new file mode 100644
index 0000000000..482e714bb6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-12-mm-multi-gen-LRU-debugfs-interface.patch
@@ -0,0 +1,579 @@
+From 530716d008ca26315f246cd70dc1cefc636beaa4 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 18 Sep 2022 02:00:09 -0600
+Subject: [PATCH 12/29] mm: multi-gen LRU: debugfs interface
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add /sys/kernel/debug/lru_gen for working set estimation and proactive
+reclaim. These techniques are commonly used to optimize job scheduling
+(bin packing) in data centers [1][2].
+
+Compared with the page table-based approach and the PFN-based
+approach, this lruvec-based approach has the following advantages:
+1. It offers better choices because it is aware of memcgs, NUMA nodes,
+ shared mappings and unmapped page cache.
+2. It is more scalable because it is O(nr_hot_pages), whereas the
+ PFN-based approach is O(nr_total_pages).
+
+Add /sys/kernel/debug/lru_gen_full for debugging.
+
+[1] https://dl.acm.org/doi/10.1145/3297858.3304053
+[2] https://dl.acm.org/doi/10.1145/3503222.3507731
+
+Link: https://lkml.kernel.org/r/20220918080010.2920238-13-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Reviewed-by: Qi Zheng <zhengqi.arch@bytedance.com>
+Acked-by: Brian Geffon <bgeffon@google.com>
+Acked-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
+Acked-by: Oleksandr Natalenko <oleksandr@natalenko.name>
+Acked-by: Steven Barrett <steven@liquorix.net>
+Acked-by: Suleiman Souhlal <suleiman@google.com>
+Tested-by: Daniel Byrne <djbyrne@mtu.edu>
+Tested-by: Donald Carr <d@chaos-reins.com>
+Tested-by: Holger Hoffstätte <holger@applied-asynchrony.com>
+Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
+Tested-by: Shuang Zhai <szhai2@cs.rochester.edu>
+Tested-by: Sofia Trinh <sofia.trinh@edi.works>
+Tested-by: Vaibhav Jain <vaibhav@linux.ibm.com>
+Cc: Andi Kleen <ak@linux.intel.com>
+Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
+Cc: Barry Song <baohua@kernel.org>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Linus Torvalds <torvalds@linux-foundation.org>
+Cc: Matthew Wilcox <willy@infradead.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Mike Rapoport <rppt@linux.ibm.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Tejun Heo <tj@kernel.org>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/nodemask.h | 1 +
+ mm/vmscan.c | 411 ++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 402 insertions(+), 10 deletions(-)
+
+--- a/include/linux/nodemask.h
++++ b/include/linux/nodemask.h
+@@ -485,6 +485,7 @@ static inline int num_node_state(enum no
+ #define first_online_node 0
+ #define first_memory_node 0
+ #define next_online_node(nid) (MAX_NUMNODES)
++#define next_memory_node(nid) (MAX_NUMNODES)
+ #define nr_node_ids 1U
+ #define nr_online_nodes 1U
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -53,6 +53,7 @@
+ #include <linux/pagewalk.h>
+ #include <linux/shmem_fs.h>
+ #include <linux/ctype.h>
++#include <linux/debugfs.h>
+
+ #include <asm/tlbflush.h>
+ #include <asm/div64.h>
+@@ -3968,12 +3969,40 @@ static void clear_mm_walk(void)
+ kfree(walk);
+ }
+
+-static void inc_min_seq(struct lruvec *lruvec, int type)
++static bool inc_min_seq(struct lruvec *lruvec, int type, bool can_swap)
+ {
++ int zone;
++ int remaining = MAX_LRU_BATCH;
+ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
++
++ if (type == LRU_GEN_ANON && !can_swap)
++ goto done;
++
++ /* prevent cold/hot inversion if force_scan is true */
++ for (zone = 0; zone < MAX_NR_ZONES; zone++) {
++ struct list_head *head = &lrugen->lists[old_gen][type][zone];
++
++ while (!list_empty(head)) {
++ struct page *page = lru_to_page(head);
++
++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page);
++ VM_WARN_ON_ONCE_PAGE(PageActive(page), page);
++ VM_WARN_ON_ONCE_PAGE(page_is_file_lru(page) != type, page);
++ VM_WARN_ON_ONCE_PAGE(page_zonenum(page) != zone, page);
+
++ new_gen = page_inc_gen(lruvec, page, false);
++ list_move_tail(&page->lru, &lrugen->lists[new_gen][type][zone]);
++
++ if (!--remaining)
++ return false;
++ }
++ }
++done:
+ reset_ctrl_pos(lruvec, type, true);
+ WRITE_ONCE(lrugen->min_seq[type], lrugen->min_seq[type] + 1);
++
++ return true;
+ }
+
+ static bool try_to_inc_min_seq(struct lruvec *lruvec, bool can_swap)
+@@ -4019,7 +4048,7 @@ next:
+ return success;
+ }
+
+-static void inc_max_seq(struct lruvec *lruvec, bool can_swap)
++static void inc_max_seq(struct lruvec *lruvec, bool can_swap, bool force_scan)
+ {
+ int prev, next;
+ int type, zone;
+@@ -4033,9 +4062,13 @@ static void inc_max_seq(struct lruvec *l
+ if (get_nr_gens(lruvec, type) != MAX_NR_GENS)
+ continue;
+
+- VM_WARN_ON_ONCE(type == LRU_GEN_FILE || can_swap);
++ VM_WARN_ON_ONCE(!force_scan && (type == LRU_GEN_FILE || can_swap));
+
+- inc_min_seq(lruvec, type);
++ while (!inc_min_seq(lruvec, type, can_swap)) {
++ spin_unlock_irq(&lruvec->lru_lock);
++ cond_resched();
++ spin_lock_irq(&lruvec->lru_lock);
++ }
+ }
+
+ /*
+@@ -4072,7 +4105,7 @@ static void inc_max_seq(struct lruvec *l
+ }
+
+ static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq,
+- struct scan_control *sc, bool can_swap)
++ struct scan_control *sc, bool can_swap, bool force_scan)
+ {
+ bool success;
+ struct lru_gen_mm_walk *walk;
+@@ -4093,7 +4126,7 @@ static bool try_to_inc_max_seq(struct lr
+ * handful of PTEs. Spreading the work out over a period of time usually
+ * is less efficient, but it avoids bursty page faults.
+ */
+- if (!(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) {
++ if (!force_scan && !(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) {
+ success = iterate_mm_list_nowalk(lruvec, max_seq);
+ goto done;
+ }
+@@ -4107,7 +4140,7 @@ static bool try_to_inc_max_seq(struct lr
+ walk->lruvec = lruvec;
+ walk->max_seq = max_seq;
+ walk->can_swap = can_swap;
+- walk->force_scan = false;
++ walk->force_scan = force_scan;
+
+ do {
+ success = iterate_mm_list(lruvec, walk, &mm);
+@@ -4127,7 +4160,7 @@ done:
+
+ VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq));
+
+- inc_max_seq(lruvec, can_swap);
++ inc_max_seq(lruvec, can_swap, force_scan);
+ /* either this sees any waiters or they will see updated max_seq */
+ if (wq_has_sleeper(&lruvec->mm_state.wait))
+ wake_up_all(&lruvec->mm_state.wait);
+@@ -4225,7 +4258,7 @@ static bool age_lruvec(struct lruvec *lr
+ }
+
+ if (need_aging)
+- try_to_inc_max_seq(lruvec, max_seq, sc, swappiness);
++ try_to_inc_max_seq(lruvec, max_seq, sc, swappiness, false);
+
+ return true;
+ }
+@@ -4784,7 +4817,7 @@ static unsigned long get_nr_to_scan(stru
+ if (current_is_kswapd())
+ return 0;
+
+- if (try_to_inc_max_seq(lruvec, max_seq, sc, can_swap))
++ if (try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false))
+ return nr_to_scan;
+ done:
+ return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
+@@ -5124,6 +5157,361 @@ static struct attribute_group lru_gen_at
+ };
+
+ /******************************************************************************
++ * debugfs interface
++ ******************************************************************************/
++
++static void *lru_gen_seq_start(struct seq_file *m, loff_t *pos)
++{
++ struct mem_cgroup *memcg;
++ loff_t nr_to_skip = *pos;
++
++ m->private = kvmalloc(PATH_MAX, GFP_KERNEL);
++ if (!m->private)
++ return ERR_PTR(-ENOMEM);
++
++ memcg = mem_cgroup_iter(NULL, NULL, NULL);
++ do {
++ int nid;
++
++ for_each_node_state(nid, N_MEMORY) {
++ if (!nr_to_skip--)
++ return get_lruvec(memcg, nid);
++ }
++ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
++
++ return NULL;
++}
++
++static void lru_gen_seq_stop(struct seq_file *m, void *v)
++{
++ if (!IS_ERR_OR_NULL(v))
++ mem_cgroup_iter_break(NULL, lruvec_memcg(v));
++
++ kvfree(m->private);
++ m->private = NULL;
++}
++
++static void *lru_gen_seq_next(struct seq_file *m, void *v, loff_t *pos)
++{
++ int nid = lruvec_pgdat(v)->node_id;
++ struct mem_cgroup *memcg = lruvec_memcg(v);
++
++ ++*pos;
++
++ nid = next_memory_node(nid);
++ if (nid == MAX_NUMNODES) {
++ memcg = mem_cgroup_iter(NULL, memcg, NULL);
++ if (!memcg)
++ return NULL;
++
++ nid = first_memory_node;
++ }
++
++ return get_lruvec(memcg, nid);
++}
++
++static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec,
++ unsigned long max_seq, unsigned long *min_seq,
++ unsigned long seq)
++{
++ int i;
++ int type, tier;
++ int hist = lru_hist_from_seq(seq);
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++
++ for (tier = 0; tier < MAX_NR_TIERS; tier++) {
++ seq_printf(m, " %10d", tier);
++ for (type = 0; type < ANON_AND_FILE; type++) {
++ const char *s = " ";
++ unsigned long n[3] = {};
++
++ if (seq == max_seq) {
++ s = "RT ";
++ n[0] = READ_ONCE(lrugen->avg_refaulted[type][tier]);
++ n[1] = READ_ONCE(lrugen->avg_total[type][tier]);
++ } else if (seq == min_seq[type] || NR_HIST_GENS > 1) {
++ s = "rep";
++ n[0] = atomic_long_read(&lrugen->refaulted[hist][type][tier]);
++ n[1] = atomic_long_read(&lrugen->evicted[hist][type][tier]);
++ if (tier)
++ n[2] = READ_ONCE(lrugen->protected[hist][type][tier - 1]);
++ }
++
++ for (i = 0; i < 3; i++)
++ seq_printf(m, " %10lu%c", n[i], s[i]);
++ }
++ seq_putc(m, '\n');
++ }
++
++ seq_puts(m, " ");
++ for (i = 0; i < NR_MM_STATS; i++) {
++ const char *s = " ";
++ unsigned long n = 0;
++
++ if (seq == max_seq && NR_HIST_GENS == 1) {
++ s = "LOYNFA";
++ n = READ_ONCE(lruvec->mm_state.stats[hist][i]);
++ } else if (seq != max_seq && NR_HIST_GENS > 1) {
++ s = "loynfa";
++ n = READ_ONCE(lruvec->mm_state.stats[hist][i]);
++ }
++
++ seq_printf(m, " %10lu%c", n, s[i]);
++ }
++ seq_putc(m, '\n');
++}
++
++static int lru_gen_seq_show(struct seq_file *m, void *v)
++{
++ unsigned long seq;
++ bool full = !debugfs_real_fops(m->file)->write;
++ struct lruvec *lruvec = v;
++ struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ int nid = lruvec_pgdat(lruvec)->node_id;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ DEFINE_MAX_SEQ(lruvec);
++ DEFINE_MIN_SEQ(lruvec);
++
++ if (nid == first_memory_node) {
++ const char *path = memcg ? m->private : "";
++
++#ifdef CONFIG_MEMCG
++ if (memcg)
++ cgroup_path(memcg->css.cgroup, m->private, PATH_MAX);
++#endif
++ seq_printf(m, "memcg %5hu %s\n", mem_cgroup_id(memcg), path);
++ }
++
++ seq_printf(m, " node %5d\n", nid);
++
++ if (!full)
++ seq = min_seq[LRU_GEN_ANON];
++ else if (max_seq >= MAX_NR_GENS)
++ seq = max_seq - MAX_NR_GENS + 1;
++ else
++ seq = 0;
++
++ for (; seq <= max_seq; seq++) {
++ int type, zone;
++ int gen = lru_gen_from_seq(seq);
++ unsigned long birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
++
++ seq_printf(m, " %10lu %10u", seq, jiffies_to_msecs(jiffies - birth));
++
++ for (type = 0; type < ANON_AND_FILE; type++) {
++ unsigned long size = 0;
++ char mark = full && seq < min_seq[type] ? 'x' : ' ';
++
++ for (zone = 0; zone < MAX_NR_ZONES; zone++)
++ size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
++
++ seq_printf(m, " %10lu%c", size, mark);
++ }
++
++ seq_putc(m, '\n');
++
++ if (full)
++ lru_gen_seq_show_full(m, lruvec, max_seq, min_seq, seq);
++ }
++
++ return 0;
++}
++
++static const struct seq_operations lru_gen_seq_ops = {
++ .start = lru_gen_seq_start,
++ .stop = lru_gen_seq_stop,
++ .next = lru_gen_seq_next,
++ .show = lru_gen_seq_show,
++};
++
++static int run_aging(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc,
++ bool can_swap, bool force_scan)
++{
++ DEFINE_MAX_SEQ(lruvec);
++ DEFINE_MIN_SEQ(lruvec);
++
++ if (seq < max_seq)
++ return 0;
++
++ if (seq > max_seq)
++ return -EINVAL;
++
++ if (!force_scan && min_seq[!can_swap] + MAX_NR_GENS - 1 <= max_seq)
++ return -ERANGE;
++
++ try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, force_scan);
++
++ return 0;
++}
++
++static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc,
++ int swappiness, unsigned long nr_to_reclaim)
++{
++ DEFINE_MAX_SEQ(lruvec);
++
++ if (seq + MIN_NR_GENS > max_seq)
++ return -EINVAL;
++
++ sc->nr_reclaimed = 0;
++
++ while (!signal_pending(current)) {
++ DEFINE_MIN_SEQ(lruvec);
++
++ if (seq < min_seq[!swappiness])
++ return 0;
++
++ if (sc->nr_reclaimed >= nr_to_reclaim)
++ return 0;
++
++ if (!evict_pages(lruvec, sc, swappiness, NULL))
++ return 0;
++
++ cond_resched();
++ }
++
++ return -EINTR;
++}
++
++static int run_cmd(char cmd, int memcg_id, int nid, unsigned long seq,
++ struct scan_control *sc, int swappiness, unsigned long opt)
++{
++ struct lruvec *lruvec;
++ int err = -EINVAL;
++ struct mem_cgroup *memcg = NULL;
++
++ if (nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY))
++ return -EINVAL;
++
++ if (!mem_cgroup_disabled()) {
++ rcu_read_lock();
++ memcg = mem_cgroup_from_id(memcg_id);
++#ifdef CONFIG_MEMCG
++ if (memcg && !css_tryget(&memcg->css))
++ memcg = NULL;
++#endif
++ rcu_read_unlock();
++
++ if (!memcg)
++ return -EINVAL;
++ }
++
++ if (memcg_id != mem_cgroup_id(memcg))
++ goto done;
++
++ lruvec = get_lruvec(memcg, nid);
++
++ if (swappiness < 0)
++ swappiness = get_swappiness(lruvec, sc);
++ else if (swappiness > 200)
++ goto done;
++
++ switch (cmd) {
++ case '+':
++ err = run_aging(lruvec, seq, sc, swappiness, opt);
++ break;
++ case '-':
++ err = run_eviction(lruvec, seq, sc, swappiness, opt);
++ break;
++ }
++done:
++ mem_cgroup_put(memcg);
++
++ return err;
++}
++
++static ssize_t lru_gen_seq_write(struct file *file, const char __user *src,
++ size_t len, loff_t *pos)
++{
++ void *buf;
++ char *cur, *next;
++ unsigned int flags;
++ struct blk_plug plug;
++ int err = -EINVAL;
++ struct scan_control sc = {
++ .may_writepage = true,
++ .may_unmap = true,
++ .may_swap = true,
++ .reclaim_idx = MAX_NR_ZONES - 1,
++ .gfp_mask = GFP_KERNEL,
++ };
++
++ buf = kvmalloc(len + 1, GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++
++ if (copy_from_user(buf, src, len)) {
++ kvfree(buf);
++ return -EFAULT;
++ }
++
++ set_task_reclaim_state(current, &sc.reclaim_state);
++ flags = memalloc_noreclaim_save();
++ blk_start_plug(&plug);
++ if (!set_mm_walk(NULL)) {
++ err = -ENOMEM;
++ goto done;
++ }
++
++ next = buf;
++ next[len] = '\0';
++
++ while ((cur = strsep(&next, ",;\n"))) {
++ int n;
++ int end;
++ char cmd;
++ unsigned int memcg_id;
++ unsigned int nid;
++ unsigned long seq;
++ unsigned int swappiness = -1;
++ unsigned long opt = -1;
++
++ cur = skip_spaces(cur);
++ if (!*cur)
++ continue;
++
++ n = sscanf(cur, "%c %u %u %lu %n %u %n %lu %n", &cmd, &memcg_id, &nid,
++ &seq, &end, &swappiness, &end, &opt, &end);
++ if (n < 4 || cur[end]) {
++ err = -EINVAL;
++ break;
++ }
++
++ err = run_cmd(cmd, memcg_id, nid, seq, &sc, swappiness, opt);
++ if (err)
++ break;
++ }
++done:
++ clear_mm_walk();
++ blk_finish_plug(&plug);
++ memalloc_noreclaim_restore(flags);
++ set_task_reclaim_state(current, NULL);
++
++ kvfree(buf);
++
++ return err ? : len;
++}
++
++static int lru_gen_seq_open(struct inode *inode, struct file *file)
++{
++ return seq_open(file, &lru_gen_seq_ops);
++}
++
++static const struct file_operations lru_gen_rw_fops = {
++ .open = lru_gen_seq_open,
++ .read = seq_read,
++ .write = lru_gen_seq_write,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++
++static const struct file_operations lru_gen_ro_fops = {
++ .open = lru_gen_seq_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = seq_release,
++};
++
++/******************************************************************************
+ * initialization
+ ******************************************************************************/
+
+@@ -5180,6 +5568,9 @@ static int __init init_lru_gen(void)
+ if (sysfs_create_group(mm_kobj, &lru_gen_attr_group))
+ pr_err("lru_gen: failed to create sysfs group\n");
+
++ debugfs_create_file("lru_gen", 0644, NULL, NULL, &lru_gen_rw_fops);
++ debugfs_create_file("lru_gen_full", 0444, NULL, NULL, &lru_gen_ro_fops);
++
+ return 0;
+ };
+ late_initcall(init_lru_gen);
diff --git a/target/linux/generic/backport-6.1/020-v6.1-13-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch b/target/linux/generic/backport-6.1/020-v6.1-13-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch
new file mode 100644
index 0000000000..fd4aa72747
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-13-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch
@@ -0,0 +1,32 @@
+From 92d430e8955c976eacb7cc91d7ff849c0dd009af Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Wed, 28 Sep 2022 13:36:58 -0600
+Subject: [PATCH 13/29] mm/mglru: don't sync disk for each aging cycle
+
+wakeup_flusher_threads() was added under the assumption that if a system
+runs out of clean cold pages, it might want to write back dirty pages more
+aggressively so that they can become clean and be dropped.
+
+However, doing so can breach the rate limit a system wants to impose on
+writeback, resulting in early SSD wearout.
+
+Link: https://lkml.kernel.org/r/YzSiWq9UEER5LKup@google.com
+Fixes: bd74fdaea146 ("mm: multi-gen LRU: support page table walks")
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Reported-by: Axel Rasmussen <axelrasmussen@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -4165,8 +4165,6 @@ done:
+ if (wq_has_sleeper(&lruvec->mm_state.wait))
+ wake_up_all(&lruvec->mm_state.wait);
+
+- wakeup_flusher_threads(WB_REASON_VMSCAN);
+-
+ return true;
+ }
+
diff --git a/target/linux/generic/backport-6.1/020-v6.1-14-mm-multi-gen-LRU-retry-pages-written-back-while-isol.patch b/target/linux/generic/backport-6.1/020-v6.1-14-mm-multi-gen-LRU-retry-pages-written-back-while-isol.patch
new file mode 100644
index 0000000000..31b35cbc4b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-14-mm-multi-gen-LRU-retry-pages-written-back-while-isol.patch
@@ -0,0 +1,124 @@
+From 6f315879ad750391a0b1fab8c9170bc054a5f5d7 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Tue, 15 Nov 2022 18:38:07 -0700
+Subject: [PATCH 14/29] mm: multi-gen LRU: retry pages written back while
+ isolated
+
+The page reclaim isolates a batch of pages from the tail of one of the
+LRU lists and works on those pages one by one. For a suitable
+swap-backed page, if the swap device is async, it queues that page for
+writeback. After the page reclaim finishes an entire batch, it puts back
+the pages it queued for writeback to the head of the original LRU list.
+
+In the meantime, the page writeback flushes the queued pages also by
+batches. Its batching logic is independent from that of the page reclaim.
+For each of the pages it writes back, the page writeback calls
+rotate_reclaimable_page() which tries to rotate a page to the tail.
+
+rotate_reclaimable_page() only works for a page after the page reclaim
+has put it back. If an async swap device is fast enough, the page
+writeback can finish with that page while the page reclaim is still
+working on the rest of the batch containing it. In this case, that page
+will remain at the head and the page reclaim will not retry it before
+reaching there.
+
+This patch adds a retry to evict_pages(). After evict_pages() has
+finished an entire batch and before it puts back pages it cannot free
+immediately, it retries those that may have missed the rotation.
+
+Before this patch, ~60% of pages swapped to an Intel Optane missed
+rotate_reclaimable_page(). After this patch, ~99% of missed pages were
+reclaimed upon retry.
+
+This problem affects relatively slow async swap devices like Samsung 980
+Pro much less and does not affect sync swap devices like zram or zswap at
+all.
+
+Link: https://lkml.kernel.org/r/20221116013808.3995280-1-yuzhao@google.com
+Fixes: ac35a4902374 ("mm: multi-gen LRU: minimal implementation")
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: "Yin, Fengwei" <fengwei.yin@intel.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 48 +++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 37 insertions(+), 11 deletions(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -4723,10 +4723,13 @@ static int evict_pages(struct lruvec *lr
+ int scanned;
+ int reclaimed;
+ LIST_HEAD(list);
++ LIST_HEAD(clean);
+ struct page *page;
++ struct page *next;
+ enum vm_event_item item;
+ struct reclaim_stat stat;
+ struct lru_gen_mm_walk *walk;
++ bool skip_retry = false;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+
+@@ -4743,20 +4746,37 @@ static int evict_pages(struct lruvec *lr
+
+ if (list_empty(&list))
+ return scanned;
+-
++retry:
+ reclaimed = shrink_page_list(&list, pgdat, sc, &stat, false);
++ sc->nr_reclaimed += reclaimed;
+
+- list_for_each_entry(page, &list, lru) {
+- /* restore LRU_REFS_FLAGS cleared by isolate_page() */
+- if (PageWorkingset(page))
+- SetPageReferenced(page);
++ list_for_each_entry_safe_reverse(page, next, &list, lru) {
++ if (!page_evictable(page)) {
++ list_del(&page->lru);
++ putback_lru_page(page);
++ continue;
++ }
+
+- /* don't add rejected pages to the oldest generation */
+ if (PageReclaim(page) &&
+- (PageDirty(page) || PageWriteback(page)))
+- ClearPageActive(page);
+- else
+- SetPageActive(page);
++ (PageDirty(page) || PageWriteback(page))) {
++ /* restore LRU_REFS_FLAGS cleared by isolate_page() */
++ if (PageWorkingset(page))
++ SetPageReferenced(page);
++ continue;
++ }
++
++ if (skip_retry || PageActive(page) || PageReferenced(page) ||
++ page_mapped(page) || PageLocked(page) ||
++ PageDirty(page) || PageWriteback(page)) {
++ /* don't add rejected pages to the oldest generation */
++ set_mask_bits(&page->flags, LRU_REFS_MASK | LRU_REFS_FLAGS,
++ BIT(PG_active));
++ continue;
++ }
++
++ /* retry pages that may have missed rotate_reclaimable_page() */
++ list_move(&page->lru, &clean);
++ sc->nr_scanned -= thp_nr_pages(page);
+ }
+
+ spin_lock_irq(&lruvec->lru_lock);
+@@ -4778,7 +4798,13 @@ static int evict_pages(struct lruvec *lr
+ mem_cgroup_uncharge_list(&list);
+ free_unref_page_list(&list);
+
+- sc->nr_reclaimed += reclaimed;
++ INIT_LIST_HEAD(&list);
++ list_splice_init(&clean, &list);
++
++ if (!list_empty(&list)) {
++ skip_retry = true;
++ goto retry;
++ }
+
+ if (need_swapping && type == LRU_GEN_ANON)
+ *need_swapping = true;
diff --git a/target/linux/generic/backport-6.1/020-v6.1-15-mm-multi-gen-LRU-move-lru_gen_add_mm-out-of-IRQ-off-.patch b/target/linux/generic/backport-6.1/020-v6.1-15-mm-multi-gen-LRU-move-lru_gen_add_mm-out-of-IRQ-off-.patch
new file mode 100644
index 0000000000..5b1d378504
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-15-mm-multi-gen-LRU-move-lru_gen_add_mm-out-of-IRQ-off-.patch
@@ -0,0 +1,49 @@
+From 255bb0ac393f1c2818cd75af45a9226300ab3daf Mon Sep 17 00:00:00 2001
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Date: Wed, 26 Oct 2022 15:48:30 +0200
+Subject: [PATCH 15/29] mm: multi-gen LRU: move lru_gen_add_mm() out of IRQ-off
+ region
+
+lru_gen_add_mm() has been added within an IRQ-off region in the commit
+mentioned below. The other invocations of lru_gen_add_mm() are not within
+an IRQ-off region.
+
+The invocation within IRQ-off region is problematic on PREEMPT_RT because
+the function is using a spin_lock_t which must not be used within
+IRQ-disabled regions.
+
+The other invocations of lru_gen_add_mm() occur while
+task_struct::alloc_lock is acquired. Move lru_gen_add_mm() after
+interrupts are enabled and before task_unlock().
+
+Link: https://lkml.kernel.org/r/20221026134830.711887-1-bigeasy@linutronix.de
+Fixes: bd74fdaea1460 ("mm: multi-gen LRU: support page table walks")
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Acked-by: Yu Zhao <yuzhao@google.com>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Cc: "Eric W . Biederman" <ebiederm@xmission.com>
+Cc: Kees Cook <keescook@chromium.org>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ fs/exec.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1013,7 +1013,6 @@ static int exec_mmap(struct mm_struct *m
+ active_mm = tsk->active_mm;
+ tsk->active_mm = mm;
+ tsk->mm = mm;
+- lru_gen_add_mm(mm);
+ /*
+ * This prevents preemption while active_mm is being loaded and
+ * it and mm are being updated, which could cause problems for
+@@ -1028,6 +1027,7 @@ static int exec_mmap(struct mm_struct *m
+ local_irq_enable();
+ tsk->mm->vmacache_seqnum = 0;
+ vmacache_flush(tsk);
++ lru_gen_add_mm(mm);
+ task_unlock(tsk);
+ lru_gen_use_mm(mm);
+ if (old_mm) {
diff --git a/target/linux/generic/backport-6.1/020-v6.1-17-mm-add-dummy-pmd_young-for-architectures-not-having-.patch b/target/linux/generic/backport-6.1/020-v6.1-17-mm-add-dummy-pmd_young-for-architectures-not-having-.patch
new file mode 100644
index 0000000000..1c10c168a5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-17-mm-add-dummy-pmd_young-for-architectures-not-having-.patch
@@ -0,0 +1,96 @@
+From c5ec455ebd2b488d91de9d8915a0c8036a2a04dd Mon Sep 17 00:00:00 2001
+From: Juergen Gross <jgross@suse.com>
+Date: Wed, 30 Nov 2022 14:49:41 -0800
+Subject: [PATCH 17/29] mm: add dummy pmd_young() for architectures not having
+ it
+
+In order to avoid #ifdeffery add a dummy pmd_young() implementation as a
+fallback. This is required for the later patch "mm: introduce
+arch_has_hw_nonleaf_pmd_young()".
+
+Link: https://lkml.kernel.org/r/fd3ac3cd-7349-6bbd-890a-71a9454ca0b3@suse.com
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Acked-by: Yu Zhao <yuzhao@google.com>
+Cc: Borislav Petkov <bp@alien8.de>
+Cc: Dave Hansen <dave.hansen@linux.intel.com>
+Cc: Geert Uytterhoeven <geert@linux-m68k.org>
+Cc: "H. Peter Anvin" <hpa@zytor.com>
+Cc: Ingo Molnar <mingo@redhat.com>
+Cc: Sander Eikelenboom <linux@eikelenboom.it>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ arch/mips/include/asm/pgtable.h | 1 +
+ arch/riscv/include/asm/pgtable.h | 1 +
+ arch/s390/include/asm/pgtable.h | 1 +
+ arch/sparc/include/asm/pgtable_64.h | 1 +
+ arch/x86/include/asm/pgtable.h | 1 +
+ include/linux/pgtable.h | 7 +++++++
+ 6 files changed, 12 insertions(+)
+
+--- a/arch/mips/include/asm/pgtable.h
++++ b/arch/mips/include/asm/pgtable.h
+@@ -632,6 +632,7 @@ static inline pmd_t pmd_mkdirty(pmd_t pm
+ return pmd;
+ }
+
++#define pmd_young pmd_young
+ static inline int pmd_young(pmd_t pmd)
+ {
+ return !!(pmd_val(pmd) & _PAGE_ACCESSED);
+--- a/arch/riscv/include/asm/pgtable.h
++++ b/arch/riscv/include/asm/pgtable.h
+@@ -535,6 +535,7 @@ static inline int pmd_dirty(pmd_t pmd)
+ return pte_dirty(pmd_pte(pmd));
+ }
+
++#define pmd_young pmd_young
+ static inline int pmd_young(pmd_t pmd)
+ {
+ return pte_young(pmd_pte(pmd));
+--- a/arch/s390/include/asm/pgtable.h
++++ b/arch/s390/include/asm/pgtable.h
+@@ -748,6 +748,7 @@ static inline int pmd_dirty(pmd_t pmd)
+ return (pmd_val(pmd) & _SEGMENT_ENTRY_DIRTY) != 0;
+ }
+
++#define pmd_young pmd_young
+ static inline int pmd_young(pmd_t pmd)
+ {
+ return (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG) != 0;
+--- a/arch/sparc/include/asm/pgtable_64.h
++++ b/arch/sparc/include/asm/pgtable_64.h
+@@ -712,6 +712,7 @@ static inline unsigned long pmd_dirty(pm
+ return pte_dirty(pte);
+ }
+
++#define pmd_young pmd_young
+ static inline unsigned long pmd_young(pmd_t pmd)
+ {
+ pte_t pte = __pte(pmd_val(pmd));
+--- a/arch/x86/include/asm/pgtable.h
++++ b/arch/x86/include/asm/pgtable.h
+@@ -136,6 +136,7 @@ static inline int pmd_dirty(pmd_t pmd)
+ return pmd_flags(pmd) & _PAGE_DIRTY;
+ }
+
++#define pmd_young pmd_young
+ static inline int pmd_young(pmd_t pmd)
+ {
+ return pmd_flags(pmd) & _PAGE_ACCESSED;
+--- a/include/linux/pgtable.h
++++ b/include/linux/pgtable.h
+@@ -164,6 +164,13 @@ static inline pte_t *virt_to_kpte(unsign
+ return pmd_none(*pmd) ? NULL : pte_offset_kernel(pmd, vaddr);
+ }
+
++#ifndef pmd_young
++static inline int pmd_young(pmd_t pmd)
++{
++ return 0;
++}
++#endif
++
+ #ifndef __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
+ extern int ptep_set_access_flags(struct vm_area_struct *vma,
+ unsigned long address, pte_t *ptep,
diff --git a/target/linux/generic/backport-6.1/020-v6.1-18-mm-introduce-arch_has_hw_nonleaf_pmd_young.patch b/target/linux/generic/backport-6.1/020-v6.1-18-mm-introduce-arch_has_hw_nonleaf_pmd_young.patch
new file mode 100644
index 0000000000..9a1f9bead6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.1-18-mm-introduce-arch_has_hw_nonleaf_pmd_young.patch
@@ -0,0 +1,113 @@
+From 46cbda7b65998a5af4493f745d94417af697bd68 Mon Sep 17 00:00:00 2001
+From: Juergen Gross <jgross@suse.com>
+Date: Wed, 23 Nov 2022 07:45:10 +0100
+Subject: [PATCH 18/29] mm: introduce arch_has_hw_nonleaf_pmd_young()
+
+When running as a Xen PV guests commit eed9a328aa1a ("mm: x86: add
+CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG") can cause a protection violation in
+pmdp_test_and_clear_young():
+
+ BUG: unable to handle page fault for address: ffff8880083374d0
+ #PF: supervisor write access in kernel mode
+ #PF: error_code(0x0003) - permissions violation
+ PGD 3026067 P4D 3026067 PUD 3027067 PMD 7fee5067 PTE 8010000008337065
+ Oops: 0003 [#1] PREEMPT SMP NOPTI
+ CPU: 7 PID: 158 Comm: kswapd0 Not tainted 6.1.0-rc5-20221118-doflr+ #1
+ RIP: e030:pmdp_test_and_clear_young+0x25/0x40
+
+This happens because the Xen hypervisor can't emulate direct writes to
+page table entries other than PTEs.
+
+This can easily be fixed by introducing arch_has_hw_nonleaf_pmd_young()
+similar to arch_has_hw_pte_young() and test that instead of
+CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.
+
+Link: https://lkml.kernel.org/r/20221123064510.16225-1-jgross@suse.com
+Fixes: eed9a328aa1a ("mm: x86: add CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG")
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Reported-by: Sander Eikelenboom <linux@eikelenboom.it>
+Acked-by: Yu Zhao <yuzhao@google.com>
+Tested-by: Sander Eikelenboom <linux@eikelenboom.it>
+Acked-by: David Hildenbrand <david@redhat.com> [core changes]
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ arch/x86/include/asm/pgtable.h | 8 ++++++++
+ include/linux/pgtable.h | 11 +++++++++++
+ mm/vmscan.c | 10 +++++-----
+ 3 files changed, 24 insertions(+), 5 deletions(-)
+
+--- a/arch/x86/include/asm/pgtable.h
++++ b/arch/x86/include/asm/pgtable.h
+@@ -1405,6 +1405,14 @@ static inline bool arch_has_hw_pte_young
+ return true;
+ }
+
++#ifdef CONFIG_XEN_PV
++#define arch_has_hw_nonleaf_pmd_young arch_has_hw_nonleaf_pmd_young
++static inline bool arch_has_hw_nonleaf_pmd_young(void)
++{
++ return !cpu_feature_enabled(X86_FEATURE_XENPV);
++}
++#endif
++
+ #endif /* __ASSEMBLY__ */
+
+ #endif /* _ASM_X86_PGTABLE_H */
+--- a/include/linux/pgtable.h
++++ b/include/linux/pgtable.h
+@@ -266,6 +266,17 @@ static inline int pmdp_clear_flush_young
+ #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+ #endif
+
++#ifndef arch_has_hw_nonleaf_pmd_young
++/*
++ * Return whether the accessed bit in non-leaf PMD entries is supported on the
++ * local CPU.
++ */
++static inline bool arch_has_hw_nonleaf_pmd_young(void)
++{
++ return IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG);
++}
++#endif
++
+ #ifndef arch_has_hw_pte_young
+ /*
+ * Return whether the accessed bit is supported on the local CPU.
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -3727,7 +3727,7 @@ static void walk_pmd_range_locked(pud_t
+ goto next;
+
+ if (!pmd_trans_huge(pmd[i])) {
+- if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) &&
++ if (arch_has_hw_nonleaf_pmd_young() &&
+ get_cap(LRU_GEN_NONLEAF_YOUNG))
+ pmdp_test_and_clear_young(vma, addr, pmd + i);
+ goto next;
+@@ -3825,14 +3825,14 @@ restart:
+ #endif
+ walk->mm_stats[MM_NONLEAF_TOTAL]++;
+
+-#ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG
+- if (get_cap(LRU_GEN_NONLEAF_YOUNG)) {
++ if (arch_has_hw_nonleaf_pmd_young() &&
++ get_cap(LRU_GEN_NONLEAF_YOUNG)) {
+ if (!pmd_young(val))
+ continue;
+
+ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
+ }
+-#endif
++
+ if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i))
+ continue;
+
+@@ -5132,7 +5132,7 @@ static ssize_t show_enabled(struct kobje
+ if (arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))
+ caps |= BIT(LRU_GEN_MM_WALK);
+
+- if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) && get_cap(LRU_GEN_NONLEAF_YOUNG))
++ if (arch_has_hw_nonleaf_pmd_young() && get_cap(LRU_GEN_NONLEAF_YOUNG))
+ caps |= BIT(LRU_GEN_NONLEAF_YOUNG);
+
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n", caps);
diff --git a/target/linux/generic/backport-6.1/020-v6.2-16-mm-multi-gen-LRU-fix-crash-during-cgroup-migration.patch b/target/linux/generic/backport-6.1/020-v6.2-16-mm-multi-gen-LRU-fix-crash-during-cgroup-migration.patch
new file mode 100644
index 0000000000..e37386abdf
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.2-16-mm-multi-gen-LRU-fix-crash-during-cgroup-migration.patch
@@ -0,0 +1,56 @@
+From c7dfefd4bdfba3d5171038d1cc2d4160288e6ee4 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Sun, 15 Jan 2023 20:44:05 -0700
+Subject: [PATCH 16/29] mm: multi-gen LRU: fix crash during cgroup migration
+
+lru_gen_migrate_mm() assumes lru_gen_add_mm() runs prior to itself. This
+isn't true for the following scenario:
+
+ CPU 1 CPU 2
+
+ clone()
+ cgroup_can_fork()
+ cgroup_procs_write()
+ cgroup_post_fork()
+ task_lock()
+ lru_gen_migrate_mm()
+ task_unlock()
+ task_lock()
+ lru_gen_add_mm()
+ task_unlock()
+
+And when the above happens, kernel crashes because of linked list
+corruption (mm_struct->lru_gen.list).
+
+Link: https://lore.kernel.org/r/20230115134651.30028-1-msizanoen@qtmlabs.xyz/
+Link: https://lkml.kernel.org/r/20230116034405.2960276-1-yuzhao@google.com
+Fixes: bd74fdaea146 ("mm: multi-gen LRU: support page table walks")
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Reported-by: msizanoen <msizanoen@qtmlabs.xyz>
+Tested-by: msizanoen <msizanoen@qtmlabs.xyz>
+Cc: <stable@vger.kernel.org> [6.1+]
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -3024,13 +3024,16 @@ void lru_gen_migrate_mm(struct mm_struct
+ if (mem_cgroup_disabled())
+ return;
+
++ /* migration can happen before addition */
++ if (!mm->lru_gen.memcg)
++ return;
++
+ rcu_read_lock();
+ memcg = mem_cgroup_from_task(task);
+ rcu_read_unlock();
+ if (memcg == mm->lru_gen.memcg)
+ return;
+
+- VM_WARN_ON_ONCE(!mm->lru_gen.memcg);
+ VM_WARN_ON_ONCE(list_empty(&mm->lru_gen.list));
+
+ lru_gen_del_mm(mm);
diff --git a/target/linux/generic/backport-6.1/020-v6.3-19-mm-add-vma_has_recency.patch b/target/linux/generic/backport-6.1/020-v6.3-19-mm-add-vma_has_recency.patch
new file mode 100644
index 0000000000..d740c5bd1d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-19-mm-add-vma_has_recency.patch
@@ -0,0 +1,196 @@
+From 6c7f552a48b49a8612786a28a2239fbc24fac289 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Fri, 30 Dec 2022 14:52:51 -0700
+Subject: [PATCH 19/29] mm: add vma_has_recency()
+
+Add vma_has_recency() to indicate whether a VMA may exhibit temporal
+locality that the LRU algorithm relies on.
+
+This function returns false for VMAs marked by VM_SEQ_READ or
+VM_RAND_READ. While the former flag indicates linear access, i.e., a
+special case of spatial locality, both flags indicate a lack of temporal
+locality, i.e., the reuse of an area within a relatively small duration.
+
+"Recency" is chosen over "locality" to avoid confusion between temporal
+and spatial localities.
+
+Before this patch, the active/inactive LRU only ignored the accessed bit
+from VMAs marked by VM_SEQ_READ. After this patch, the active/inactive
+LRU and MGLRU share the same logic: they both ignore the accessed bit if
+vma_has_recency() returns false.
+
+For the active/inactive LRU, the following fio test showed a [6, 8]%
+increase in IOPS when randomly accessing mapped files under memory
+pressure.
+
+ kb=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
+ kb=$((kb - 8*1024*1024))
+
+ modprobe brd rd_nr=1 rd_size=$kb
+ dd if=/dev/zero of=/dev/ram0 bs=1M
+
+ mkfs.ext4 /dev/ram0
+ mount /dev/ram0 /mnt/
+ swapoff -a
+
+ fio --name=test --directory=/mnt/ --ioengine=mmap --numjobs=8 \
+ --size=8G --rw=randrw --time_based --runtime=10m \
+ --group_reporting
+
+The discussion that led to this patch is here [1]. Additional test
+results are available in that thread.
+
+[1] https://lore.kernel.org/r/Y31s%2FK8T85jh05wH@google.com/
+
+Link: https://lkml.kernel.org/r/20221230215252.2628425-1-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Alexander Viro <viro@zeniv.linux.org.uk>
+Cc: Andrea Righi <andrea.righi@canonical.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/mm_inline.h | 9 +++++++++
+ mm/memory.c | 8 ++++----
+ mm/rmap.c | 42 +++++++++++++++++----------------------
+ mm/vmscan.c | 5 ++++-
+ 4 files changed, 35 insertions(+), 29 deletions(-)
+
+--- a/include/linux/mm_inline.h
++++ b/include/linux/mm_inline.h
+@@ -333,4 +333,13 @@ static __always_inline void del_page_fro
+ update_lru_size(lruvec, page_lru(page), page_zonenum(page),
+ -thp_nr_pages(page));
+ }
++
++static inline bool vma_has_recency(struct vm_area_struct *vma)
++{
++ if (vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))
++ return false;
++
++ return true;
++}
++
+ #endif
+--- a/mm/memory.c
++++ b/mm/memory.c
+@@ -41,6 +41,7 @@
+
+ #include <linux/kernel_stat.h>
+ #include <linux/mm.h>
++#include <linux/mm_inline.h>
+ #include <linux/sched/mm.h>
+ #include <linux/sched/coredump.h>
+ #include <linux/sched/numa_balancing.h>
+@@ -1353,8 +1354,7 @@ again:
+ force_flush = 1;
+ set_page_dirty(page);
+ }
+- if (pte_young(ptent) &&
+- likely(!(vma->vm_flags & VM_SEQ_READ)))
++ if (pte_young(ptent) && likely(vma_has_recency(vma)))
+ mark_page_accessed(page);
+ }
+ rss[mm_counter(page)]--;
+@@ -4795,8 +4795,8 @@ static inline void mm_account_fault(stru
+ #ifdef CONFIG_LRU_GEN
+ static void lru_gen_enter_fault(struct vm_area_struct *vma)
+ {
+- /* the LRU algorithm doesn't apply to sequential or random reads */
+- current->in_lru_fault = !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ));
++ /* the LRU algorithm only applies to accesses with recency */
++ current->in_lru_fault = vma_has_recency(vma);
+ }
+
+ static void lru_gen_exit_fault(void)
+--- a/mm/rmap.c
++++ b/mm/rmap.c
+@@ -794,25 +794,14 @@ static bool page_referenced_one(struct p
+ }
+
+ if (pvmw.pte) {
+- if (lru_gen_enabled() && pte_young(*pvmw.pte) &&
+- !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))) {
++ if (lru_gen_enabled() && pte_young(*pvmw.pte)) {
+ lru_gen_look_around(&pvmw);
+ referenced++;
+ }
+
+ if (ptep_clear_flush_young_notify(vma, address,
+- pvmw.pte)) {
+- /*
+- * Don't treat a reference through
+- * a sequentially read mapping as such.
+- * If the page has been used in another mapping,
+- * we will catch it; if this other mapping is
+- * already gone, the unmap path will have set
+- * PG_referenced or activated the page.
+- */
+- if (likely(!(vma->vm_flags & VM_SEQ_READ)))
+- referenced++;
+- }
++ pvmw.pte))
++ referenced++;
+ } else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
+ if (pmdp_clear_flush_young_notify(vma, address,
+ pvmw.pmd))
+@@ -846,7 +835,20 @@ static bool invalid_page_referenced_vma(
+ struct page_referenced_arg *pra = arg;
+ struct mem_cgroup *memcg = pra->memcg;
+
+- if (!mm_match_cgroup(vma->vm_mm, memcg))
++ /*
++ * Ignore references from this mapping if it has no recency. If the
++ * page has been used in another mapping, we will catch it; if this
++ * other mapping is already gone, the unmap path will have set the
++ * referenced flag or activated the page in zap_pte_range().
++ */
++ if (!vma_has_recency(vma))
++ return true;
++
++ /*
++ * If we are reclaiming on behalf of a cgroup, skip counting on behalf
++ * of references from different cgroups.
++ */
++ if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
+ return true;
+
+ return false;
+@@ -876,6 +878,7 @@ int page_referenced(struct page *page,
+ .rmap_one = page_referenced_one,
+ .arg = (void *)&pra,
+ .anon_lock = page_lock_anon_vma_read,
++ .invalid_vma = invalid_page_referenced_vma,
+ };
+
+ *vm_flags = 0;
+@@ -891,15 +894,6 @@ int page_referenced(struct page *page,
+ return 1;
+ }
+
+- /*
+- * If we are reclaiming on behalf of a cgroup, skip
+- * counting on behalf of references from different
+- * cgroups
+- */
+- if (memcg) {
+- rwc.invalid_vma = invalid_page_referenced_vma;
+- }
+-
+ rmap_walk(page, &rwc);
+ *vm_flags = pra.vm_flags;
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -3486,7 +3486,10 @@ static int should_skip_vma(unsigned long
+ if (is_vm_hugetlb_page(vma))
+ return true;
+
+- if (vma->vm_flags & (VM_LOCKED | VM_SPECIAL | VM_SEQ_READ | VM_RAND_READ))
++ if (!vma_has_recency(vma))
++ return true;
++
++ if (vma->vm_flags & (VM_LOCKED | VM_SPECIAL))
+ return true;
+
+ if (vma == get_gate_vma(vma->vm_mm))
diff --git a/target/linux/generic/backport-6.1/020-v6.3-20-mm-support-POSIX_FADV_NOREUSE.patch b/target/linux/generic/backport-6.1/020-v6.3-20-mm-support-POSIX_FADV_NOREUSE.patch
new file mode 100644
index 0000000000..3bb075bf36
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-20-mm-support-POSIX_FADV_NOREUSE.patch
@@ -0,0 +1,125 @@
+From 686c3d4f71de9e0e7a27f03a5617a712385f90cd Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Fri, 30 Dec 2022 14:52:52 -0700
+Subject: [PATCH 20/29] mm: support POSIX_FADV_NOREUSE
+
+This patch adds POSIX_FADV_NOREUSE to vma_has_recency() so that the LRU
+algorithm can ignore access to mapped files marked by this flag.
+
+The advantages of POSIX_FADV_NOREUSE are:
+1. Unlike MADV_SEQUENTIAL and MADV_RANDOM, it does not alter the
+ default readahead behavior.
+2. Unlike MADV_SEQUENTIAL and MADV_RANDOM, it does not split VMAs and
+ therefore does not take mmap_lock.
+3. Unlike MADV_COLD, setting it has a negligible cost, regardless of
+ how many pages it affects.
+
+Its limitations are:
+1. Like POSIX_FADV_RANDOM and POSIX_FADV_SEQUENTIAL, it currently does
+ not support range. IOW, its scope is the entire file.
+2. It currently does not ignore access through file descriptors.
+ Specifically, for the active/inactive LRU, given a file page shared
+ by two users and one of them having set POSIX_FADV_NOREUSE on the
+ file, this page will be activated upon the second user accessing
+ it. This corner case can be covered by checking POSIX_FADV_NOREUSE
+ before calling mark_page_accessed() on the read path. But it is
+ considered not worth the effort.
+
+There have been a few attempts to support POSIX_FADV_NOREUSE, e.g., [1].
+This time the goal is to fill a niche: a few desktop applications, e.g.,
+large file transferring and video encoding/decoding, want fast file
+streaming with mmap() rather than direct IO. Among those applications, an
+SVT-AV1 regression was reported when running with MGLRU [2]. The
+following test can reproduce that regression.
+
+ kb=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
+ kb=$((kb - 8*1024*1024))
+
+ modprobe brd rd_nr=1 rd_size=$kb
+ dd if=/dev/zero of=/dev/ram0 bs=1M
+
+ mkfs.ext4 /dev/ram0
+ mount /dev/ram0 /mnt/
+ swapoff -a
+
+ fallocate -l 8G /mnt/swapfile
+ mkswap /mnt/swapfile
+ swapon /mnt/swapfile
+
+ wget http://ultravideo.cs.tut.fi/video/Bosphorus_3840x2160_120fps_420_8bit_YUV_Y4M.7z
+ 7z e -o/mnt/ Bosphorus_3840x2160_120fps_420_8bit_YUV_Y4M.7z
+ SvtAv1EncApp --preset 12 -w 3840 -h 2160 \
+ -i /mnt/Bosphorus_3840x2160.y4m
+
+For MGLRU, the following change showed a [9-11]% increase in FPS,
+which makes it on par with the active/inactive LRU.
+
+ patch Source/App/EncApp/EbAppMain.c <<EOF
+ 31a32
+ > #include <fcntl.h>
+ 35d35
+ < #include <fcntl.h> /* _O_BINARY */
+ 117a118
+ > posix_fadvise(config->mmap.fd, 0, 0, POSIX_FADV_NOREUSE);
+ EOF
+
+[1] https://lore.kernel.org/r/1308923350-7932-1-git-send-email-andrea@betterlinux.com/
+[2] https://openbenchmarking.org/result/2209259-PTS-MGLRU8GB57
+
+Link: https://lkml.kernel.org/r/20221230215252.2628425-2-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Alexander Viro <viro@zeniv.linux.org.uk>
+Cc: Andrea Righi <andrea.righi@canonical.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/fs.h | 2 ++
+ include/linux/mm_inline.h | 3 +++
+ mm/fadvise.c | 5 ++++-
+ 3 files changed, 9 insertions(+), 1 deletion(-)
+
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -167,6 +167,8 @@ typedef int (dio_iodone_t)(struct kiocb
+ /* File is stream-like */
+ #define FMODE_STREAM ((__force fmode_t)0x200000)
+
++#define FMODE_NOREUSE ((__force fmode_t)0x400000)
++
+ /* File was opened by fanotify and shouldn't generate fanotify events */
+ #define FMODE_NONOTIFY ((__force fmode_t)0x4000000)
+
+--- a/include/linux/mm_inline.h
++++ b/include/linux/mm_inline.h
+@@ -339,6 +339,9 @@ static inline bool vma_has_recency(struc
+ if (vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))
+ return false;
+
++ if (vma->vm_file && (vma->vm_file->f_mode & FMODE_NOREUSE))
++ return false;
++
+ return true;
+ }
+
+--- a/mm/fadvise.c
++++ b/mm/fadvise.c
+@@ -80,7 +80,7 @@ int generic_fadvise(struct file *file, l
+ case POSIX_FADV_NORMAL:
+ file->f_ra.ra_pages = bdi->ra_pages;
+ spin_lock(&file->f_lock);
+- file->f_mode &= ~FMODE_RANDOM;
++ file->f_mode &= ~(FMODE_RANDOM | FMODE_NOREUSE);
+ spin_unlock(&file->f_lock);
+ break;
+ case POSIX_FADV_RANDOM:
+@@ -107,6 +107,9 @@ int generic_fadvise(struct file *file, l
+ force_page_cache_readahead(mapping, file, start_index, nrpages);
+ break;
+ case POSIX_FADV_NOREUSE:
++ spin_lock(&file->f_lock);
++ file->f_mode |= FMODE_NOREUSE;
++ spin_unlock(&file->f_lock);
+ break;
+ case POSIX_FADV_DONTNEED:
+ if (!inode_write_congested(mapping->host))
diff --git a/target/linux/generic/backport-6.1/020-v6.3-21-mm-multi-gen-LRU-rename-lru_gen_struct-to-lru_gen_pa.patch b/target/linux/generic/backport-6.1/020-v6.3-21-mm-multi-gen-LRU-rename-lru_gen_struct-to-lru_gen_pa.patch
new file mode 100644
index 0000000000..4e09173681
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-21-mm-multi-gen-LRU-rename-lru_gen_struct-to-lru_gen_pa.patch
@@ -0,0 +1,348 @@
+From 348fdbada9fb3f0bf1a53651be46319105af187f Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Wed, 21 Dec 2022 21:18:59 -0700
+Subject: [PATCH 21/29] mm: multi-gen LRU: rename lru_gen_struct to
+ lru_gen_page
+
+Patch series "mm: multi-gen LRU: memcg LRU", v3.
+
+Overview
+========
+
+An memcg LRU is a per-node LRU of memcgs. It is also an LRU of LRUs,
+since each node and memcg combination has an LRU of pages (see
+mem_cgroup_lruvec()).
+
+Its goal is to improve the scalability of global reclaim, which is
+critical to system-wide memory overcommit in data centers. Note that
+memcg reclaim is currently out of scope.
+
+Its memory bloat is a pointer to each lruvec and negligible to each
+pglist_data. In terms of traversing memcgs during global reclaim, it
+improves the best-case complexity from O(n) to O(1) and does not affect
+the worst-case complexity O(n). Therefore, on average, it has a sublinear
+complexity in contrast to the current linear complexity.
+
+The basic structure of an memcg LRU can be understood by an analogy to
+the active/inactive LRU (of pages):
+1. It has the young and the old (generations), i.e., the counterparts
+ to the active and the inactive;
+2. The increment of max_seq triggers promotion, i.e., the counterpart
+ to activation;
+3. Other events trigger similar operations, e.g., offlining an memcg
+ triggers demotion, i.e., the counterpart to deactivation.
+
+In terms of global reclaim, it has two distinct features:
+1. Sharding, which allows each thread to start at a random memcg (in
+ the old generation) and improves parallelism;
+2. Eventual fairness, which allows direct reclaim to bail out at will
+ and reduces latency without affecting fairness over some time.
+
+The commit message in patch 6 details the workflow:
+https://lore.kernel.org/r/20221222041905.2431096-7-yuzhao@google.com/
+
+The following is a simple test to quickly verify its effectiveness.
+
+ Test design:
+ 1. Create multiple memcgs.
+ 2. Each memcg contains a job (fio).
+ 3. All jobs access the same amount of memory randomly.
+ 4. The system does not experience global memory pressure.
+ 5. Periodically write to the root memory.reclaim.
+
+ Desired outcome:
+ 1. All memcgs have similar pgsteal counts, i.e., stddev(pgsteal)
+ over mean(pgsteal) is close to 0%.
+ 2. The total pgsteal is close to the total requested through
+ memory.reclaim, i.e., sum(pgsteal) over sum(requested) is close
+ to 100%.
+
+ Actual outcome [1]:
+ MGLRU off MGLRU on
+ stddev(pgsteal) / mean(pgsteal) 75% 20%
+ sum(pgsteal) / sum(requested) 425% 95%
+
+ ####################################################################
+ MEMCGS=128
+
+ for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
+ mkdir /sys/fs/cgroup/memcg$memcg
+ done
+
+ start() {
+ echo $BASHPID > /sys/fs/cgroup/memcg$memcg/cgroup.procs
+
+ fio -name=memcg$memcg --numjobs=1 --ioengine=mmap \
+ --filename=/dev/zero --size=1920M --rw=randrw \
+ --rate=64m,64m --random_distribution=random \
+ --fadvise_hint=0 --time_based --runtime=10h \
+ --group_reporting --minimal
+ }
+
+ for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
+ start &
+ done
+
+ sleep 600
+
+ for ((i = 0; i < 600; i++)); do
+ echo 256m >/sys/fs/cgroup/memory.reclaim
+ sleep 6
+ done
+
+ for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
+ grep "pgsteal " /sys/fs/cgroup/memcg$memcg/memory.stat
+ done
+ ####################################################################
+
+[1]: This was obtained from running the above script (touches less
+ than 256GB memory) on an EPYC 7B13 with 512GB DRAM for over an
+ hour.
+
+This patch (of 8):
+
+The new name lru_gen_page will be more distinct from the coming
+lru_gen_memcg.
+
+Link: https://lkml.kernel.org/r/20221222041905.2431096-1-yuzhao@google.com
+Link: https://lkml.kernel.org/r/20221222041905.2431096-2-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/mm_inline.h | 4 ++--
+ include/linux/mmzone.h | 6 +++---
+ mm/vmscan.c | 34 +++++++++++++++++-----------------
+ mm/workingset.c | 4 ++--
+ 4 files changed, 24 insertions(+), 24 deletions(-)
+
+--- a/include/linux/mm_inline.h
++++ b/include/linux/mm_inline.h
+@@ -168,7 +168,7 @@ static inline void lru_gen_update_size(s
+ int zone = page_zonenum(page);
+ int delta = thp_nr_pages(page);
+ enum lru_list lru = type * LRU_INACTIVE_FILE;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+
+ VM_WARN_ON_ONCE(old_gen != -1 && old_gen >= MAX_NR_GENS);
+ VM_WARN_ON_ONCE(new_gen != -1 && new_gen >= MAX_NR_GENS);
+@@ -214,7 +214,7 @@ static inline bool lru_gen_add_page(stru
+ int gen = page_lru_gen(page);
+ int type = page_is_file_lru(page);
+ int zone = page_zonenum(page);
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+
+ VM_WARN_ON_ONCE_PAGE(gen != -1, page);
+
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -394,7 +394,7 @@ enum {
+ * The number of pages in each generation is eventually consistent and therefore
+ * can be transiently negative when reset_batch_size() is pending.
+ */
+-struct lru_gen_struct {
++struct lru_gen_page {
+ /* the aging increments the youngest generation number */
+ unsigned long max_seq;
+ /* the eviction increments the oldest generation numbers */
+@@ -451,7 +451,7 @@ struct lru_gen_mm_state {
+ struct lru_gen_mm_walk {
+ /* the lruvec under reclaim */
+ struct lruvec *lruvec;
+- /* unstable max_seq from lru_gen_struct */
++ /* unstable max_seq from lru_gen_page */
+ unsigned long max_seq;
+ /* the next address within an mm to scan */
+ unsigned long next_addr;
+@@ -514,7 +514,7 @@ struct lruvec {
+ unsigned long flags;
+ #ifdef CONFIG_LRU_GEN
+ /* evictable pages divided into generations */
+- struct lru_gen_struct lrugen;
++ struct lru_gen_page lrugen;
+ /* to concurrently iterate lru_gen_mm_list */
+ struct lru_gen_mm_state mm_state;
+ #endif
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -2910,7 +2910,7 @@ static int get_nr_gens(struct lruvec *lr
+
+ static bool __maybe_unused seq_is_valid(struct lruvec *lruvec)
+ {
+- /* see the comment on lru_gen_struct */
++ /* see the comment on lru_gen_page */
+ return get_nr_gens(lruvec, LRU_GEN_FILE) >= MIN_NR_GENS &&
+ get_nr_gens(lruvec, LRU_GEN_FILE) <= get_nr_gens(lruvec, LRU_GEN_ANON) &&
+ get_nr_gens(lruvec, LRU_GEN_ANON) <= MAX_NR_GENS;
+@@ -3316,7 +3316,7 @@ struct ctrl_pos {
+ static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
+ struct ctrl_pos *pos)
+ {
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ int hist = lru_hist_from_seq(lrugen->min_seq[type]);
+
+ pos->refaulted = lrugen->avg_refaulted[type][tier] +
+@@ -3331,7 +3331,7 @@ static void read_ctrl_pos(struct lruvec
+ static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover)
+ {
+ int hist, tier;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ bool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS > 1;
+ unsigned long seq = carryover ? lrugen->min_seq[type] : lrugen->max_seq + 1;
+
+@@ -3408,7 +3408,7 @@ static int page_update_gen(struct page *
+ static int page_inc_gen(struct lruvec *lruvec, struct page *page, bool reclaiming)
+ {
+ int type = page_is_file_lru(page);
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
+ unsigned long new_flags, old_flags = READ_ONCE(page->flags);
+
+@@ -3453,7 +3453,7 @@ static void update_batch_size(struct lru
+ static void reset_batch_size(struct lruvec *lruvec, struct lru_gen_mm_walk *walk)
+ {
+ int gen, type, zone;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+
+ walk->batched = 0;
+
+@@ -3979,7 +3979,7 @@ static bool inc_min_seq(struct lruvec *l
+ {
+ int zone;
+ int remaining = MAX_LRU_BATCH;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
+
+ if (type == LRU_GEN_ANON && !can_swap)
+@@ -4015,7 +4015,7 @@ static bool try_to_inc_min_seq(struct lr
+ {
+ int gen, type, zone;
+ bool success = false;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ DEFINE_MIN_SEQ(lruvec);
+
+ VM_WARN_ON_ONCE(!seq_is_valid(lruvec));
+@@ -4036,7 +4036,7 @@ next:
+ ;
+ }
+
+- /* see the comment on lru_gen_struct */
++ /* see the comment on lru_gen_page */
+ if (can_swap) {
+ min_seq[LRU_GEN_ANON] = min(min_seq[LRU_GEN_ANON], min_seq[LRU_GEN_FILE]);
+ min_seq[LRU_GEN_FILE] = max(min_seq[LRU_GEN_ANON], lrugen->min_seq[LRU_GEN_FILE]);
+@@ -4058,7 +4058,7 @@ static void inc_max_seq(struct lruvec *l
+ {
+ int prev, next;
+ int type, zone;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+
+ spin_lock_irq(&lruvec->lru_lock);
+
+@@ -4116,7 +4116,7 @@ static bool try_to_inc_max_seq(struct lr
+ bool success;
+ struct lru_gen_mm_walk *walk;
+ struct mm_struct *mm = NULL;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+
+ VM_WARN_ON_ONCE(max_seq > READ_ONCE(lrugen->max_seq));
+
+@@ -4181,7 +4181,7 @@ static bool should_run_aging(struct lruv
+ unsigned long old = 0;
+ unsigned long young = 0;
+ unsigned long total = 0;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+
+ for (type = !can_swap; type < ANON_AND_FILE; type++) {
+@@ -4466,7 +4466,7 @@ static bool sort_page(struct lruvec *lru
+ int delta = thp_nr_pages(page);
+ int refs = page_lru_refs(page);
+ int tier = lru_tier_from_refs(refs);
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+
+ VM_WARN_ON_ONCE_PAGE(gen >= MAX_NR_GENS, page);
+
+@@ -4566,7 +4566,7 @@ static int scan_pages(struct lruvec *lru
+ int scanned = 0;
+ int isolated = 0;
+ int remaining = MAX_LRU_BATCH;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+
+ VM_WARN_ON_ONCE(!list_empty(list));
+@@ -4967,7 +4967,7 @@ done:
+
+ static bool __maybe_unused state_is_valid(struct lruvec *lruvec)
+ {
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+
+ if (lrugen->enabled) {
+ enum lru_list lru;
+@@ -5247,7 +5247,7 @@ static void lru_gen_seq_show_full(struct
+ int i;
+ int type, tier;
+ int hist = lru_hist_from_seq(seq);
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+
+ for (tier = 0; tier < MAX_NR_TIERS; tier++) {
+ seq_printf(m, " %10d", tier);
+@@ -5296,7 +5296,7 @@ static int lru_gen_seq_show(struct seq_f
+ unsigned long seq;
+ bool full = !debugfs_real_fops(m->file)->write;
+ struct lruvec *lruvec = v;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ int nid = lruvec_pgdat(lruvec)->node_id;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ DEFINE_MAX_SEQ(lruvec);
+@@ -5549,7 +5549,7 @@ void lru_gen_init_lruvec(struct lruvec *
+ {
+ int i;
+ int gen, type, zone;
+- struct lru_gen_struct *lrugen = &lruvec->lrugen;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+
+ lrugen->max_seq = MIN_NR_GENS + 1;
+ lrugen->enabled = lru_gen_enabled();
+--- a/mm/workingset.c
++++ b/mm/workingset.c
+@@ -223,7 +223,7 @@ static void *lru_gen_eviction(struct pag
+ unsigned long token;
+ unsigned long min_seq;
+ struct lruvec *lruvec;
+- struct lru_gen_struct *lrugen;
++ struct lru_gen_page *lrugen;
+ int type = page_is_file_lru(page);
+ int delta = thp_nr_pages(page);
+ int refs = page_lru_refs(page);
+@@ -252,7 +252,7 @@ static void lru_gen_refault(struct page
+ unsigned long token;
+ unsigned long min_seq;
+ struct lruvec *lruvec;
+- struct lru_gen_struct *lrugen;
++ struct lru_gen_page *lrugen;
+ struct mem_cgroup *memcg;
+ struct pglist_data *pgdat;
+ int type = page_is_file_lru(page);
diff --git a/target/linux/generic/backport-6.1/020-v6.3-22-mm-multi-gen-LRU-rename-lrugen-lists-to-lrugen-pages.patch b/target/linux/generic/backport-6.1/020-v6.3-22-mm-multi-gen-LRU-rename-lrugen-lists-to-lrugen-pages.patch
new file mode 100644
index 0000000000..b548c1c8b3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-22-mm-multi-gen-LRU-rename-lrugen-lists-to-lrugen-pages.patch
@@ -0,0 +1,162 @@
+From afd37e73db04c7e6b47411120ac5f6a7eca51fec Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Wed, 21 Dec 2022 21:19:00 -0700
+Subject: [PATCH 22/29] mm: multi-gen LRU: rename lrugen->lists[] to
+ lrugen->pages[]
+
+lru_gen_page will be chained into per-node lists by the coming
+lrugen->list.
+
+Link: https://lkml.kernel.org/r/20221222041905.2431096-3-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/mm_inline.h | 4 ++--
+ include/linux/mmzone.h | 8 ++++----
+ mm/vmscan.c | 20 ++++++++++----------
+ 3 files changed, 16 insertions(+), 16 deletions(-)
+
+--- a/include/linux/mm_inline.h
++++ b/include/linux/mm_inline.h
+@@ -246,9 +246,9 @@ static inline bool lru_gen_add_page(stru
+ lru_gen_update_size(lruvec, page, -1, gen);
+ /* for rotate_reclaimable_page() */
+ if (reclaiming)
+- list_add_tail(&page->lru, &lrugen->lists[gen][type][zone]);
++ list_add_tail(&page->lru, &lrugen->pages[gen][type][zone]);
+ else
+- list_add(&page->lru, &lrugen->lists[gen][type][zone]);
++ list_add(&page->lru, &lrugen->pages[gen][type][zone]);
+
+ return true;
+ }
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -302,7 +302,7 @@ enum lruvec_flags {
+ * They form a sliding window of a variable size [MIN_NR_GENS, MAX_NR_GENS]. An
+ * offset within MAX_NR_GENS, i.e., gen, indexes the LRU list of the
+ * corresponding generation. The gen counter in page->flags stores gen+1 while
+- * a page is on one of lrugen->lists[]. Otherwise it stores 0.
++ * a page is on one of lrugen->pages[]. Otherwise it stores 0.
+ *
+ * A page is added to the youngest generation on faulting. The aging needs to
+ * check the accessed bit at least twice before handing this page over to the
+@@ -314,8 +314,8 @@ enum lruvec_flags {
+ * rest of generations, if they exist, are considered inactive. See
+ * lru_gen_is_active().
+ *
+- * PG_active is always cleared while a page is on one of lrugen->lists[] so that
+- * the aging needs not to worry about it. And it's set again when a page
++ * PG_active is always cleared while a page is on one of lrugen->pages[] so
++ * that the aging needs not to worry about it. And it's set again when a page
+ * considered active is isolated for non-reclaiming purposes, e.g., migration.
+ * See lru_gen_add_page() and lru_gen_del_page().
+ *
+@@ -402,7 +402,7 @@ struct lru_gen_page {
+ /* the birth time of each generation in jiffies */
+ unsigned long timestamps[MAX_NR_GENS];
+ /* the multi-gen LRU lists, lazily sorted on eviction */
+- struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
++ struct list_head pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
+ /* the multi-gen LRU sizes, eventually consistent */
+ long nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES];
+ /* the exponential moving average of refaulted */
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -3987,7 +3987,7 @@ static bool inc_min_seq(struct lruvec *l
+
+ /* prevent cold/hot inversion if force_scan is true */
+ for (zone = 0; zone < MAX_NR_ZONES; zone++) {
+- struct list_head *head = &lrugen->lists[old_gen][type][zone];
++ struct list_head *head = &lrugen->pages[old_gen][type][zone];
+
+ while (!list_empty(head)) {
+ struct page *page = lru_to_page(head);
+@@ -3998,7 +3998,7 @@ static bool inc_min_seq(struct lruvec *l
+ VM_WARN_ON_ONCE_PAGE(page_zonenum(page) != zone, page);
+
+ new_gen = page_inc_gen(lruvec, page, false);
+- list_move_tail(&page->lru, &lrugen->lists[new_gen][type][zone]);
++ list_move_tail(&page->lru, &lrugen->pages[new_gen][type][zone]);
+
+ if (!--remaining)
+ return false;
+@@ -4026,7 +4026,7 @@ static bool try_to_inc_min_seq(struct lr
+ gen = lru_gen_from_seq(min_seq[type]);
+
+ for (zone = 0; zone < MAX_NR_ZONES; zone++) {
+- if (!list_empty(&lrugen->lists[gen][type][zone]))
++ if (!list_empty(&lrugen->pages[gen][type][zone]))
+ goto next;
+ }
+
+@@ -4491,7 +4491,7 @@ static bool sort_page(struct lruvec *lru
+
+ /* promoted */
+ if (gen != lru_gen_from_seq(lrugen->min_seq[type])) {
+- list_move(&page->lru, &lrugen->lists[gen][type][zone]);
++ list_move(&page->lru, &lrugen->pages[gen][type][zone]);
+ return true;
+ }
+
+@@ -4500,7 +4500,7 @@ static bool sort_page(struct lruvec *lru
+ int hist = lru_hist_from_seq(lrugen->min_seq[type]);
+
+ gen = page_inc_gen(lruvec, page, false);
+- list_move_tail(&page->lru, &lrugen->lists[gen][type][zone]);
++ list_move_tail(&page->lru, &lrugen->pages[gen][type][zone]);
+
+ WRITE_ONCE(lrugen->protected[hist][type][tier - 1],
+ lrugen->protected[hist][type][tier - 1] + delta);
+@@ -4512,7 +4512,7 @@ static bool sort_page(struct lruvec *lru
+ if (PageLocked(page) || PageWriteback(page) ||
+ (type == LRU_GEN_FILE && PageDirty(page))) {
+ gen = page_inc_gen(lruvec, page, true);
+- list_move(&page->lru, &lrugen->lists[gen][type][zone]);
++ list_move(&page->lru, &lrugen->pages[gen][type][zone]);
+ return true;
+ }
+
+@@ -4579,7 +4579,7 @@ static int scan_pages(struct lruvec *lru
+ for (zone = sc->reclaim_idx; zone >= 0; zone--) {
+ LIST_HEAD(moved);
+ int skipped = 0;
+- struct list_head *head = &lrugen->lists[gen][type][zone];
++ struct list_head *head = &lrugen->pages[gen][type][zone];
+
+ while (!list_empty(head)) {
+ struct page *page = lru_to_page(head);
+@@ -4980,7 +4980,7 @@ static bool __maybe_unused state_is_vali
+ int gen, type, zone;
+
+ for_each_gen_type_zone(gen, type, zone) {
+- if (!list_empty(&lrugen->lists[gen][type][zone]))
++ if (!list_empty(&lrugen->pages[gen][type][zone]))
+ return false;
+ }
+ }
+@@ -5025,7 +5025,7 @@ static bool drain_evictable(struct lruve
+ int remaining = MAX_LRU_BATCH;
+
+ for_each_gen_type_zone(gen, type, zone) {
+- struct list_head *head = &lruvec->lrugen.lists[gen][type][zone];
++ struct list_head *head = &lruvec->lrugen.pages[gen][type][zone];
+
+ while (!list_empty(head)) {
+ bool success;
+@@ -5558,7 +5558,7 @@ void lru_gen_init_lruvec(struct lruvec *
+ lrugen->timestamps[i] = jiffies;
+
+ for_each_gen_type_zone(gen, type, zone)
+- INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]);
++ INIT_LIST_HEAD(&lrugen->pages[gen][type][zone]);
+
+ lruvec->mm_state.seq = MIN_NR_GENS;
+ init_waitqueue_head(&lruvec->mm_state.wait);
diff --git a/target/linux/generic/backport-6.1/020-v6.3-23-mm-multi-gen-LRU-remove-eviction-fairness-safeguard.patch b/target/linux/generic/backport-6.1/020-v6.3-23-mm-multi-gen-LRU-remove-eviction-fairness-safeguard.patch
new file mode 100644
index 0000000000..2bb6e12a5a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-23-mm-multi-gen-LRU-remove-eviction-fairness-safeguard.patch
@@ -0,0 +1,188 @@
+From ce45f1c4b32cf69b166f56ef5bc6c761e06ed4e5 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Wed, 21 Dec 2022 21:19:01 -0700
+Subject: [PATCH 23/29] mm: multi-gen LRU: remove eviction fairness safeguard
+
+Recall that the eviction consumes the oldest generation: first it
+bucket-sorts pages whose gen counters were updated by the aging and
+reclaims the rest; then it increments lrugen->min_seq.
+
+The current eviction fairness safeguard for global reclaim has a
+dilemma: when there are multiple eligible memcgs, should it continue
+or stop upon meeting the reclaim goal? If it continues, it overshoots
+and increases direct reclaim latency; if it stops, it loses fairness
+between memcgs it has taken memory away from and those it has yet to.
+
+With memcg LRU, the eviction, while ensuring eventual fairness, will
+stop upon meeting its goal. Therefore the current eviction fairness
+safeguard for global reclaim will not be needed.
+
+Note that memcg LRU only applies to global reclaim. For memcg reclaim,
+the eviction will continue, even if it is overshooting. This becomes
+unconditional due to code simplification.
+
+Link: https://lkml.kernel.org/r/20221222041905.2431096-4-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 82 +++++++++++++++--------------------------------------
+ 1 file changed, 23 insertions(+), 59 deletions(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -443,6 +443,11 @@ static bool cgroup_reclaim(struct scan_c
+ return sc->target_mem_cgroup;
+ }
+
++static bool global_reclaim(struct scan_control *sc)
++{
++ return !sc->target_mem_cgroup || mem_cgroup_is_root(sc->target_mem_cgroup);
++}
++
+ /**
+ * writeback_throttling_sane - is the usual dirty throttling mechanism available?
+ * @sc: scan_control in question
+@@ -493,6 +498,11 @@ static bool cgroup_reclaim(struct scan_c
+ return false;
+ }
+
++static bool global_reclaim(struct scan_control *sc)
++{
++ return true;
++}
++
+ static bool writeback_throttling_sane(struct scan_control *sc)
+ {
+ return true;
+@@ -4722,8 +4732,7 @@ static int isolate_pages(struct lruvec *
+ return scanned;
+ }
+
+-static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness,
+- bool *need_swapping)
++static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness)
+ {
+ int type;
+ int scanned;
+@@ -4812,9 +4821,6 @@ retry:
+ goto retry;
+ }
+
+- if (need_swapping && type == LRU_GEN_ANON)
+- *need_swapping = true;
+-
+ return scanned;
+ }
+
+@@ -4853,68 +4859,26 @@ done:
+ return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
+ }
+
+-static bool should_abort_scan(struct lruvec *lruvec, unsigned long seq,
+- struct scan_control *sc, bool need_swapping)
++static unsigned long get_nr_to_reclaim(struct scan_control *sc)
+ {
+- int i;
+- DEFINE_MAX_SEQ(lruvec);
+-
+- if (!current_is_kswapd()) {
+- /* age each memcg once to ensure fairness */
+- if (max_seq - seq > 1)
+- return true;
+-
+- /* over-swapping can increase allocation latency */
+- if (sc->nr_reclaimed >= sc->nr_to_reclaim && need_swapping)
+- return true;
+-
+- /* give this thread a chance to exit and free its memory */
+- if (fatal_signal_pending(current)) {
+- sc->nr_reclaimed += MIN_LRU_BATCH;
+- return true;
+- }
+-
+- if (cgroup_reclaim(sc))
+- return false;
+- } else if (sc->nr_reclaimed - sc->last_reclaimed < sc->nr_to_reclaim)
+- return false;
+-
+- /* keep scanning at low priorities to ensure fairness */
+- if (sc->priority > DEF_PRIORITY - 2)
+- return false;
+-
+- /*
+- * A minimum amount of work was done under global memory pressure. For
+- * kswapd, it may be overshooting. For direct reclaim, the target isn't
+- * met, and yet the allocation may still succeed, since kswapd may have
+- * caught up. In either case, it's better to stop now, and restart if
+- * necessary.
+- */
+- for (i = 0; i <= sc->reclaim_idx; i++) {
+- unsigned long wmark;
+- struct zone *zone = lruvec_pgdat(lruvec)->node_zones + i;
+-
+- if (!managed_zone(zone))
+- continue;
+-
+- wmark = current_is_kswapd() ? high_wmark_pages(zone) : low_wmark_pages(zone);
+- if (wmark > zone_page_state(zone, NR_FREE_PAGES))
+- return false;
+- }
++ /* don't abort memcg reclaim to ensure fairness */
++ if (!global_reclaim(sc))
++ return -1;
+
+- sc->nr_reclaimed += MIN_LRU_BATCH;
++ /* discount the previous progress for kswapd */
++ if (current_is_kswapd())
++ return sc->nr_to_reclaim + sc->last_reclaimed;
+
+- return true;
++ return max(sc->nr_to_reclaim, compact_gap(sc->order));
+ }
+
+ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
+ {
+ struct blk_plug plug;
+ bool need_aging = false;
+- bool need_swapping = false;
+ unsigned long scanned = 0;
+ unsigned long reclaimed = sc->nr_reclaimed;
+- DEFINE_MAX_SEQ(lruvec);
++ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
+
+ lru_add_drain();
+
+@@ -4938,7 +4902,7 @@ static void lru_gen_shrink_lruvec(struct
+ if (!nr_to_scan)
+ goto done;
+
+- delta = evict_pages(lruvec, sc, swappiness, &need_swapping);
++ delta = evict_pages(lruvec, sc, swappiness);
+ if (!delta)
+ goto done;
+
+@@ -4946,7 +4910,7 @@ static void lru_gen_shrink_lruvec(struct
+ if (scanned >= nr_to_scan)
+ break;
+
+- if (should_abort_scan(lruvec, max_seq, sc, need_swapping))
++ if (sc->nr_reclaimed >= nr_to_reclaim)
+ break;
+
+ cond_resched();
+@@ -5393,7 +5357,7 @@ static int run_eviction(struct lruvec *l
+ if (sc->nr_reclaimed >= nr_to_reclaim)
+ return 0;
+
+- if (!evict_pages(lruvec, sc, swappiness, NULL))
++ if (!evict_pages(lruvec, sc, swappiness))
+ return 0;
+
+ cond_resched();
diff --git a/target/linux/generic/backport-6.1/020-v6.3-24-mm-multi-gen-LRU-remove-aging-fairness-safeguard.patch b/target/linux/generic/backport-6.1/020-v6.3-24-mm-multi-gen-LRU-remove-aging-fairness-safeguard.patch
new file mode 100644
index 0000000000..316217ed02
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-24-mm-multi-gen-LRU-remove-aging-fairness-safeguard.patch
@@ -0,0 +1,287 @@
+From e20b7386fccc18c791796eb1dc1a91eee3ccf801 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Wed, 21 Dec 2022 21:19:02 -0700
+Subject: [PATCH 24/29] mm: multi-gen LRU: remove aging fairness safeguard
+
+Recall that the aging produces the youngest generation: first it scans
+for accessed pages and updates their gen counters; then it increments
+lrugen->max_seq.
+
+The current aging fairness safeguard for kswapd uses two passes to
+ensure the fairness to multiple eligible memcgs. On the first pass,
+which is shared with the eviction, it checks whether all eligible
+memcgs are low on cold pages. If so, it requires a second pass, on
+which it ages all those memcgs at the same time.
+
+With memcg LRU, the aging, while ensuring eventual fairness, will run
+when necessary. Therefore the current aging fairness safeguard for
+kswapd will not be needed.
+
+Note that memcg LRU only applies to global reclaim. For memcg reclaim,
+the aging can be unfair to different memcgs, i.e., their
+lrugen->max_seq can be incremented at different paces.
+
+Link: https://lkml.kernel.org/r/20221222041905.2431096-5-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 126 ++++++++++++++++++++++++----------------------------
+ 1 file changed, 59 insertions(+), 67 deletions(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -131,7 +131,6 @@ struct scan_control {
+
+ #ifdef CONFIG_LRU_GEN
+ /* help kswapd make better choices among multiple memcgs */
+- unsigned int memcgs_need_aging:1;
+ unsigned long last_reclaimed;
+ #endif
+
+@@ -4184,7 +4183,7 @@ done:
+ return true;
+ }
+
+-static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsigned long *min_seq,
++static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
+ struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
+ {
+ int gen, type, zone;
+@@ -4193,6 +4192,13 @@ static bool should_run_aging(struct lruv
+ unsigned long total = 0;
+ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ DEFINE_MIN_SEQ(lruvec);
++
++ /* whether this lruvec is completely out of cold pages */
++ if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
++ *nr_to_scan = 0;
++ return true;
++ }
+
+ for (type = !can_swap; type < ANON_AND_FILE; type++) {
+ unsigned long seq;
+@@ -4221,8 +4227,6 @@ static bool should_run_aging(struct lruv
+ * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
+ * ideal number of generations is MIN_NR_GENS+1.
+ */
+- if (min_seq[!can_swap] + MIN_NR_GENS > max_seq)
+- return true;
+ if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
+ return false;
+
+@@ -4241,40 +4245,54 @@ static bool should_run_aging(struct lruv
+ return false;
+ }
+
+-static bool age_lruvec(struct lruvec *lruvec, struct scan_control *sc, unsigned long min_ttl)
++static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
+ {
+- bool need_aging;
+- unsigned long nr_to_scan;
+- int swappiness = get_swappiness(lruvec, sc);
++ int gen, type, zone;
++ unsigned long total = 0;
++ bool can_swap = get_swappiness(lruvec, sc);
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ DEFINE_MAX_SEQ(lruvec);
+ DEFINE_MIN_SEQ(lruvec);
+
+- VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
++ for (type = !can_swap; type < ANON_AND_FILE; type++) {
++ unsigned long seq;
+
+- mem_cgroup_calculate_protection(NULL, memcg);
++ for (seq = min_seq[type]; seq <= max_seq; seq++) {
++ gen = lru_gen_from_seq(seq);
+
+- if (mem_cgroup_below_min(memcg))
+- return false;
++ for (zone = 0; zone < MAX_NR_ZONES; zone++)
++ total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
++ }
++ }
+
+- need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan);
++ /* whether the size is big enough to be helpful */
++ return mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
++}
+
+- if (min_ttl) {
+- int gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
+- unsigned long birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
++static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc,
++ unsigned long min_ttl)
++{
++ int gen;
++ unsigned long birth;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ DEFINE_MIN_SEQ(lruvec);
+
+- if (time_is_after_jiffies(birth + min_ttl))
+- return false;
++ VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
+
+- /* the size is likely too small to be helpful */
+- if (!nr_to_scan && sc->priority != DEF_PRIORITY)
+- return false;
+- }
++ /* see the comment on lru_gen_page */
++ gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
++ birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
+
+- if (need_aging)
+- try_to_inc_max_seq(lruvec, max_seq, sc, swappiness, false);
++ if (time_is_after_jiffies(birth + min_ttl))
++ return false;
+
+- return true;
++ if (!lruvec_is_sizable(lruvec, sc))
++ return false;
++
++ mem_cgroup_calculate_protection(NULL, memcg);
++
++ return !mem_cgroup_below_min(memcg);
+ }
+
+ /* to protect the working set of the last N jiffies */
+@@ -4283,46 +4301,32 @@ static unsigned long lru_gen_min_ttl __r
+ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)
+ {
+ struct mem_cgroup *memcg;
+- bool success = false;
+ unsigned long min_ttl = READ_ONCE(lru_gen_min_ttl);
+
+ VM_WARN_ON_ONCE(!current_is_kswapd());
+
+ sc->last_reclaimed = sc->nr_reclaimed;
+
+- /*
+- * To reduce the chance of going into the aging path, which can be
+- * costly, optimistically skip it if the flag below was cleared in the
+- * eviction path. This improves the overall performance when multiple
+- * memcgs are available.
+- */
+- if (!sc->memcgs_need_aging) {
+- sc->memcgs_need_aging = true;
++ /* check the order to exclude compaction-induced reclaim */
++ if (!min_ttl || sc->order || sc->priority == DEF_PRIORITY)
+ return;
+- }
+-
+- set_mm_walk(pgdat);
+
+ memcg = mem_cgroup_iter(NULL, NULL, NULL);
+ do {
+ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat);
+
+- if (age_lruvec(lruvec, sc, min_ttl))
+- success = true;
++ if (lruvec_is_reclaimable(lruvec, sc, min_ttl)) {
++ mem_cgroup_iter_break(NULL, memcg);
++ return;
++ }
+
+ cond_resched();
+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
+
+- clear_mm_walk();
+-
+- /* check the order to exclude compaction-induced reclaim */
+- if (success || !min_ttl || sc->order)
+- return;
+-
+ /*
+ * The main goal is to OOM kill if every generation from all memcgs is
+ * younger than min_ttl. However, another possibility is all memcgs are
+- * either below min or empty.
++ * either too small or below min.
+ */
+ if (mutex_trylock(&oom_lock)) {
+ struct oom_control oc = {
+@@ -4830,33 +4834,27 @@ retry:
+ * reclaim.
+ */
+ static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
+- bool can_swap, bool *need_aging)
++ bool can_swap)
+ {
+ unsigned long nr_to_scan;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ DEFINE_MAX_SEQ(lruvec);
+- DEFINE_MIN_SEQ(lruvec);
+
+ if (mem_cgroup_below_min(memcg) ||
+ (mem_cgroup_below_low(memcg) && !sc->memcg_low_reclaim))
+ return 0;
+
+- *need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, can_swap, &nr_to_scan);
+- if (!*need_aging)
++ if (!should_run_aging(lruvec, max_seq, sc, can_swap, &nr_to_scan))
+ return nr_to_scan;
+
+ /* skip the aging path at the default priority */
+ if (sc->priority == DEF_PRIORITY)
+- goto done;
++ return nr_to_scan;
+
+- /* leave the work to lru_gen_age_node() */
+- if (current_is_kswapd())
+- return 0;
++ try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false);
+
+- if (try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false))
+- return nr_to_scan;
+-done:
+- return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
++ /* skip this lruvec as it's low on cold pages */
++ return 0;
+ }
+
+ static unsigned long get_nr_to_reclaim(struct scan_control *sc)
+@@ -4875,9 +4873,7 @@ static unsigned long get_nr_to_reclaim(s
+ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
+ {
+ struct blk_plug plug;
+- bool need_aging = false;
+ unsigned long scanned = 0;
+- unsigned long reclaimed = sc->nr_reclaimed;
+ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
+
+ lru_add_drain();
+@@ -4898,13 +4894,13 @@ static void lru_gen_shrink_lruvec(struct
+ else
+ swappiness = 0;
+
+- nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness, &need_aging);
++ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
+ if (!nr_to_scan)
+- goto done;
++ break;
+
+ delta = evict_pages(lruvec, sc, swappiness);
+ if (!delta)
+- goto done;
++ break;
+
+ scanned += delta;
+ if (scanned >= nr_to_scan)
+@@ -4916,10 +4912,6 @@ static void lru_gen_shrink_lruvec(struct
+ cond_resched();
+ }
+
+- /* see the comment in lru_gen_age_node() */
+- if (sc->nr_reclaimed - reclaimed >= MIN_LRU_BATCH && !need_aging)
+- sc->memcgs_need_aging = false;
+-done:
+ clear_mm_walk();
+
+ blk_finish_plug(&plug);
diff --git a/target/linux/generic/backport-6.1/020-v6.3-25-mm-multi-gen-LRU-shuffle-should_run_aging.patch b/target/linux/generic/backport-6.1/020-v6.3-25-mm-multi-gen-LRU-shuffle-should_run_aging.patch
new file mode 100644
index 0000000000..391ee6e67c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-25-mm-multi-gen-LRU-shuffle-should_run_aging.patch
@@ -0,0 +1,161 @@
+From 107d54931df3c28d81648122e219bf0034ef4e99 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Wed, 21 Dec 2022 21:19:03 -0700
+Subject: [PATCH 25/29] mm: multi-gen LRU: shuffle should_run_aging()
+
+Move should_run_aging() next to its only caller left.
+
+Link: https://lkml.kernel.org/r/20221222041905.2431096-6-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 124 ++++++++++++++++++++++++++--------------------------
+ 1 file changed, 62 insertions(+), 62 deletions(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -4183,68 +4183,6 @@ done:
+ return true;
+ }
+
+-static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
+- struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
+-{
+- int gen, type, zone;
+- unsigned long old = 0;
+- unsigned long young = 0;
+- unsigned long total = 0;
+- struct lru_gen_page *lrugen = &lruvec->lrugen;
+- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+- DEFINE_MIN_SEQ(lruvec);
+-
+- /* whether this lruvec is completely out of cold pages */
+- if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
+- *nr_to_scan = 0;
+- return true;
+- }
+-
+- for (type = !can_swap; type < ANON_AND_FILE; type++) {
+- unsigned long seq;
+-
+- for (seq = min_seq[type]; seq <= max_seq; seq++) {
+- unsigned long size = 0;
+-
+- gen = lru_gen_from_seq(seq);
+-
+- for (zone = 0; zone < MAX_NR_ZONES; zone++)
+- size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
+-
+- total += size;
+- if (seq == max_seq)
+- young += size;
+- else if (seq + MIN_NR_GENS == max_seq)
+- old += size;
+- }
+- }
+-
+- /* try to scrape all its memory if this memcg was deleted */
+- *nr_to_scan = mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
+-
+- /*
+- * The aging tries to be lazy to reduce the overhead, while the eviction
+- * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
+- * ideal number of generations is MIN_NR_GENS+1.
+- */
+- if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
+- return false;
+-
+- /*
+- * It's also ideal to spread pages out evenly, i.e., 1/(MIN_NR_GENS+1)
+- * of the total number of pages for each generation. A reasonable range
+- * for this average portion is [1/MIN_NR_GENS, 1/(MIN_NR_GENS+2)]. The
+- * aging cares about the upper bound of hot pages, while the eviction
+- * cares about the lower bound of cold pages.
+- */
+- if (young * MIN_NR_GENS > total)
+- return true;
+- if (old * (MIN_NR_GENS + 2) < total)
+- return true;
+-
+- return false;
+-}
+-
+ static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
+ {
+ int gen, type, zone;
+@@ -4828,6 +4766,68 @@ retry:
+ return scanned;
+ }
+
++static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
++ struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
++{
++ int gen, type, zone;
++ unsigned long old = 0;
++ unsigned long young = 0;
++ unsigned long total = 0;
++ struct lru_gen_page *lrugen = &lruvec->lrugen;
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ DEFINE_MIN_SEQ(lruvec);
++
++ /* whether this lruvec is completely out of cold pages */
++ if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
++ *nr_to_scan = 0;
++ return true;
++ }
++
++ for (type = !can_swap; type < ANON_AND_FILE; type++) {
++ unsigned long seq;
++
++ for (seq = min_seq[type]; seq <= max_seq; seq++) {
++ unsigned long size = 0;
++
++ gen = lru_gen_from_seq(seq);
++
++ for (zone = 0; zone < MAX_NR_ZONES; zone++)
++ size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
++
++ total += size;
++ if (seq == max_seq)
++ young += size;
++ else if (seq + MIN_NR_GENS == max_seq)
++ old += size;
++ }
++ }
++
++ /* try to scrape all its memory if this memcg was deleted */
++ *nr_to_scan = mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
++
++ /*
++ * The aging tries to be lazy to reduce the overhead, while the eviction
++ * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
++ * ideal number of generations is MIN_NR_GENS+1.
++ */
++ if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
++ return false;
++
++ /*
++ * It's also ideal to spread pages out evenly, i.e., 1/(MIN_NR_GENS+1)
++ * of the total number of pages for each generation. A reasonable range
++ * for this average portion is [1/MIN_NR_GENS, 1/(MIN_NR_GENS+2)]. The
++ * aging cares about the upper bound of hot pages, while the eviction
++ * cares about the lower bound of cold pages.
++ */
++ if (young * MIN_NR_GENS > total)
++ return true;
++ if (old * (MIN_NR_GENS + 2) < total)
++ return true;
++
++ return false;
++}
++
+ /*
+ * For future optimizations:
+ * 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg
diff --git a/target/linux/generic/backport-6.1/020-v6.3-26-mm-multi-gen-LRU-per-node-lru_gen_page-lists.patch b/target/linux/generic/backport-6.1/020-v6.3-26-mm-multi-gen-LRU-per-node-lru_gen_page-lists.patch
new file mode 100644
index 0000000000..f54e828de1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-26-mm-multi-gen-LRU-per-node-lru_gen_page-lists.patch
@@ -0,0 +1,868 @@
+From fa6363828d314e837c5f79e97ea5e8c0d2f7f062 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Wed, 21 Dec 2022 21:19:04 -0700
+Subject: [PATCH 26/29] mm: multi-gen LRU: per-node lru_gen_page lists
+
+For each node, memcgs are divided into two generations: the old and
+the young. For each generation, memcgs are randomly sharded into
+multiple bins to improve scalability. For each bin, an RCU hlist_nulls
+is virtually divided into three segments: the head, the tail and the
+default.
+
+An onlining memcg is added to the tail of a random bin in the old
+generation. The eviction starts at the head of a random bin in the old
+generation. The per-node memcg generation counter, whose reminder (mod
+2) indexes the old generation, is incremented when all its bins become
+empty.
+
+There are four operations:
+1. MEMCG_LRU_HEAD, which moves an memcg to the head of a random bin in
+ its current generation (old or young) and updates its "seg" to
+ "head";
+2. MEMCG_LRU_TAIL, which moves an memcg to the tail of a random bin in
+ its current generation (old or young) and updates its "seg" to
+ "tail";
+3. MEMCG_LRU_OLD, which moves an memcg to the head of a random bin in
+ the old generation, updates its "gen" to "old" and resets its "seg"
+ to "default";
+4. MEMCG_LRU_YOUNG, which moves an memcg to the tail of a random bin
+ in the young generation, updates its "gen" to "young" and resets
+ its "seg" to "default".
+
+The events that trigger the above operations are:
+1. Exceeding the soft limit, which triggers MEMCG_LRU_HEAD;
+2. The first attempt to reclaim an memcg below low, which triggers
+ MEMCG_LRU_TAIL;
+3. The first attempt to reclaim an memcg below reclaimable size
+ threshold, which triggers MEMCG_LRU_TAIL;
+4. The second attempt to reclaim an memcg below reclaimable size
+ threshold, which triggers MEMCG_LRU_YOUNG;
+5. Attempting to reclaim an memcg below min, which triggers
+ MEMCG_LRU_YOUNG;
+6. Finishing the aging on the eviction path, which triggers
+ MEMCG_LRU_YOUNG;
+7. Offlining an memcg, which triggers MEMCG_LRU_OLD.
+
+Note that memcg LRU only applies to global reclaim, and the
+round-robin incrementing of their max_seq counters ensures the
+eventual fairness to all eligible memcgs. For memcg reclaim, it still
+relies on mem_cgroup_iter().
+
+Link: https://lkml.kernel.org/r/20221222041905.2431096-7-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ include/linux/memcontrol.h | 10 +
+ include/linux/mm_inline.h | 17 ++
+ include/linux/mmzone.h | 117 +++++++++++-
+ mm/memcontrol.c | 16 ++
+ mm/page_alloc.c | 1 +
+ mm/vmscan.c | 373 +++++++++++++++++++++++++++++++++----
+ 6 files changed, 499 insertions(+), 35 deletions(-)
+
+--- a/include/linux/memcontrol.h
++++ b/include/linux/memcontrol.h
+@@ -818,6 +818,11 @@ static inline void obj_cgroup_put(struct
+ percpu_ref_put(&objcg->refcnt);
+ }
+
++static inline bool mem_cgroup_tryget(struct mem_cgroup *memcg)
++{
++ return !memcg || css_tryget(&memcg->css);
++}
++
+ static inline void mem_cgroup_put(struct mem_cgroup *memcg)
+ {
+ if (memcg)
+@@ -1283,6 +1288,11 @@ struct mem_cgroup *mem_cgroup_from_css(s
+ return NULL;
+ }
+
++static inline bool mem_cgroup_tryget(struct mem_cgroup *memcg)
++{
++ return true;
++}
++
+ static inline void mem_cgroup_put(struct mem_cgroup *memcg)
+ {
+ }
+--- a/include/linux/mm_inline.h
++++ b/include/linux/mm_inline.h
+@@ -112,6 +112,18 @@ static inline bool lru_gen_in_fault(void
+ return current->in_lru_fault;
+ }
+
++#ifdef CONFIG_MEMCG
++static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
++{
++ return READ_ONCE(lruvec->lrugen.seg);
++}
++#else
++static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
++{
++ return 0;
++}
++#endif
++
+ static inline int lru_gen_from_seq(unsigned long seq)
+ {
+ return seq % MAX_NR_GENS;
+@@ -287,6 +299,11 @@ static inline bool lru_gen_in_fault(void
+ return false;
+ }
+
++static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
++{
++ return 0;
++}
++
+ static inline bool lru_gen_add_page(struct lruvec *lruvec, struct page *page, bool reclaiming)
+ {
+ return false;
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -7,6 +7,7 @@
+
+ #include <linux/spinlock.h>
+ #include <linux/list.h>
++#include <linux/list_nulls.h>
+ #include <linux/wait.h>
+ #include <linux/bitops.h>
+ #include <linux/cache.h>
+@@ -357,6 +358,15 @@ struct page_vma_mapped_walk;
+ #define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
+ #define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
+
++/* see the comment on MEMCG_NR_GENS */
++enum {
++ MEMCG_LRU_NOP,
++ MEMCG_LRU_HEAD,
++ MEMCG_LRU_TAIL,
++ MEMCG_LRU_OLD,
++ MEMCG_LRU_YOUNG,
++};
++
+ #ifdef CONFIG_LRU_GEN
+
+ enum {
+@@ -416,6 +426,14 @@ struct lru_gen_page {
+ atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
+ /* whether the multi-gen LRU is enabled */
+ bool enabled;
++#ifdef CONFIG_MEMCG
++ /* the memcg generation this lru_gen_page belongs to */
++ u8 gen;
++ /* the list segment this lru_gen_page belongs to */
++ u8 seg;
++ /* per-node lru_gen_page list for global reclaim */
++ struct hlist_nulls_node list;
++#endif
+ };
+
+ enum {
+@@ -469,12 +487,87 @@ void lru_gen_init_lruvec(struct lruvec *
+ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw);
+
+ #ifdef CONFIG_MEMCG
++
++/*
++ * For each node, memcgs are divided into two generations: the old and the
++ * young. For each generation, memcgs are randomly sharded into multiple bins
++ * to improve scalability. For each bin, the hlist_nulls is virtually divided
++ * into three segments: the head, the tail and the default.
++ *
++ * An onlining memcg is added to the tail of a random bin in the old generation.
++ * The eviction starts at the head of a random bin in the old generation. The
++ * per-node memcg generation counter, whose reminder (mod MEMCG_NR_GENS) indexes
++ * the old generation, is incremented when all its bins become empty.
++ *
++ * There are four operations:
++ * 1. MEMCG_LRU_HEAD, which moves an memcg to the head of a random bin in its
++ * current generation (old or young) and updates its "seg" to "head";
++ * 2. MEMCG_LRU_TAIL, which moves an memcg to the tail of a random bin in its
++ * current generation (old or young) and updates its "seg" to "tail";
++ * 3. MEMCG_LRU_OLD, which moves an memcg to the head of a random bin in the old
++ * generation, updates its "gen" to "old" and resets its "seg" to "default";
++ * 4. MEMCG_LRU_YOUNG, which moves an memcg to the tail of a random bin in the
++ * young generation, updates its "gen" to "young" and resets its "seg" to
++ * "default".
++ *
++ * The events that trigger the above operations are:
++ * 1. Exceeding the soft limit, which triggers MEMCG_LRU_HEAD;
++ * 2. The first attempt to reclaim an memcg below low, which triggers
++ * MEMCG_LRU_TAIL;
++ * 3. The first attempt to reclaim an memcg below reclaimable size threshold,
++ * which triggers MEMCG_LRU_TAIL;
++ * 4. The second attempt to reclaim an memcg below reclaimable size threshold,
++ * which triggers MEMCG_LRU_YOUNG;
++ * 5. Attempting to reclaim an memcg below min, which triggers MEMCG_LRU_YOUNG;
++ * 6. Finishing the aging on the eviction path, which triggers MEMCG_LRU_YOUNG;
++ * 7. Offlining an memcg, which triggers MEMCG_LRU_OLD.
++ *
++ * Note that memcg LRU only applies to global reclaim, and the round-robin
++ * incrementing of their max_seq counters ensures the eventual fairness to all
++ * eligible memcgs. For memcg reclaim, it still relies on mem_cgroup_iter().
++ */
++#define MEMCG_NR_GENS 2
++#define MEMCG_NR_BINS 8
++
++struct lru_gen_memcg {
++ /* the per-node memcg generation counter */
++ unsigned long seq;
++ /* each memcg has one lru_gen_page per node */
++ unsigned long nr_memcgs[MEMCG_NR_GENS];
++ /* per-node lru_gen_page list for global reclaim */
++ struct hlist_nulls_head fifo[MEMCG_NR_GENS][MEMCG_NR_BINS];
++ /* protects the above */
++ spinlock_t lock;
++};
++
++void lru_gen_init_pgdat(struct pglist_data *pgdat);
++
+ void lru_gen_init_memcg(struct mem_cgroup *memcg);
+ void lru_gen_exit_memcg(struct mem_cgroup *memcg);
+-#endif
++void lru_gen_online_memcg(struct mem_cgroup *memcg);
++void lru_gen_offline_memcg(struct mem_cgroup *memcg);
++void lru_gen_release_memcg(struct mem_cgroup *memcg);
++void lru_gen_rotate_memcg(struct lruvec *lruvec, int op);
++
++#else /* !CONFIG_MEMCG */
++
++#define MEMCG_NR_GENS 1
++
++struct lru_gen_memcg {
++};
++
++static inline void lru_gen_init_pgdat(struct pglist_data *pgdat)
++{
++}
++
++#endif /* CONFIG_MEMCG */
+
+ #else /* !CONFIG_LRU_GEN */
+
++static inline void lru_gen_init_pgdat(struct pglist_data *pgdat)
++{
++}
++
+ static inline void lru_gen_init_lruvec(struct lruvec *lruvec)
+ {
+ }
+@@ -484,6 +577,7 @@ static inline void lru_gen_look_around(s
+ }
+
+ #ifdef CONFIG_MEMCG
++
+ static inline void lru_gen_init_memcg(struct mem_cgroup *memcg)
+ {
+ }
+@@ -491,7 +585,24 @@ static inline void lru_gen_init_memcg(st
+ static inline void lru_gen_exit_memcg(struct mem_cgroup *memcg)
+ {
+ }
+-#endif
++
++static inline void lru_gen_online_memcg(struct mem_cgroup *memcg)
++{
++}
++
++static inline void lru_gen_offline_memcg(struct mem_cgroup *memcg)
++{
++}
++
++static inline void lru_gen_release_memcg(struct mem_cgroup *memcg)
++{
++}
++
++static inline void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
++{
++}
++
++#endif /* CONFIG_MEMCG */
+
+ #endif /* CONFIG_LRU_GEN */
+
+@@ -1105,6 +1216,8 @@ typedef struct pglist_data {
+ #ifdef CONFIG_LRU_GEN
+ /* kswap mm walk data */
+ struct lru_gen_mm_walk mm_walk;
++ /* lru_gen_page list */
++ struct lru_gen_memcg memcg_lru;
+ #endif
+
+ ZONE_PADDING(_pad2_)
+--- a/mm/memcontrol.c
++++ b/mm/memcontrol.c
+@@ -549,6 +549,16 @@ static void mem_cgroup_update_tree(struc
+ struct mem_cgroup_per_node *mz;
+ struct mem_cgroup_tree_per_node *mctz;
+
++ if (lru_gen_enabled()) {
++ struct lruvec *lruvec = &mem_cgroup_page_nodeinfo(memcg, page)->lruvec;
++
++ /* see the comment on MEMCG_NR_GENS */
++ if (soft_limit_excess(memcg) && lru_gen_memcg_seg(lruvec) != MEMCG_LRU_HEAD)
++ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);
++
++ return;
++ }
++
+ mctz = soft_limit_tree_from_page(page);
+ if (!mctz)
+ return;
+@@ -3433,6 +3443,9 @@ unsigned long mem_cgroup_soft_limit_recl
+ unsigned long excess;
+ unsigned long nr_scanned;
+
++ if (lru_gen_enabled())
++ return 0;
++
+ if (order > 0)
+ return 0;
+
+@@ -5321,6 +5334,7 @@ static int mem_cgroup_css_online(struct
+ if (unlikely(mem_cgroup_is_root(memcg)))
+ queue_delayed_work(system_unbound_wq, &stats_flush_dwork,
+ 2UL*HZ);
++ lru_gen_online_memcg(memcg);
+ return 0;
+ }
+
+@@ -5347,6 +5361,7 @@ static void mem_cgroup_css_offline(struc
+ memcg_offline_kmem(memcg);
+ reparent_shrinker_deferred(memcg);
+ wb_memcg_offline(memcg);
++ lru_gen_offline_memcg(memcg);
+
+ drain_all_stock(memcg);
+
+@@ -5358,6 +5373,7 @@ static void mem_cgroup_css_released(stru
+ struct mem_cgroup *memcg = mem_cgroup_from_css(css);
+
+ invalidate_reclaim_iterators(memcg);
++ lru_gen_release_memcg(memcg);
+ }
+
+ static void mem_cgroup_css_free(struct cgroup_subsys_state *css)
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -7661,6 +7661,7 @@ static void __init free_area_init_node(i
+ pgdat_set_deferred_range(pgdat);
+
+ free_area_init_core(pgdat);
++ lru_gen_init_pgdat(pgdat);
+ }
+
+ void __init free_area_init_memoryless_node(int nid)
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -54,6 +54,8 @@
+ #include <linux/shmem_fs.h>
+ #include <linux/ctype.h>
+ #include <linux/debugfs.h>
++#include <linux/rculist_nulls.h>
++#include <linux/random.h>
+
+ #include <asm/tlbflush.h>
+ #include <asm/div64.h>
+@@ -129,11 +131,6 @@ struct scan_control {
+ /* Always discard instead of demoting to lower tier memory */
+ unsigned int no_demotion:1;
+
+-#ifdef CONFIG_LRU_GEN
+- /* help kswapd make better choices among multiple memcgs */
+- unsigned long last_reclaimed;
+-#endif
+-
+ /* Allocation order */
+ s8 order;
+
+@@ -2880,6 +2877,9 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_ca
+ for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \
+ for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++)
+
++#define get_memcg_gen(seq) ((seq) % MEMCG_NR_GENS)
++#define get_memcg_bin(bin) ((bin) % MEMCG_NR_BINS)
++
+ static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid)
+ {
+ struct pglist_data *pgdat = NODE_DATA(nid);
+@@ -4169,8 +4169,7 @@ done:
+ if (sc->priority <= DEF_PRIORITY - 2)
+ wait_event_killable(lruvec->mm_state.wait,
+ max_seq < READ_ONCE(lrugen->max_seq));
+-
+- return max_seq < READ_ONCE(lrugen->max_seq);
++ return false;
+ }
+
+ VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq));
+@@ -4243,8 +4242,6 @@ static void lru_gen_age_node(struct pgli
+
+ VM_WARN_ON_ONCE(!current_is_kswapd());
+
+- sc->last_reclaimed = sc->nr_reclaimed;
+-
+ /* check the order to exclude compaction-induced reclaim */
+ if (!min_ttl || sc->order || sc->priority == DEF_PRIORITY)
+ return;
+@@ -4833,8 +4830,7 @@ static bool should_run_aging(struct lruv
+ * 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg
+ * reclaim.
+ */
+-static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
+- bool can_swap)
++static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, bool can_swap)
+ {
+ unsigned long nr_to_scan;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+@@ -4851,10 +4847,8 @@ static unsigned long get_nr_to_scan(stru
+ if (sc->priority == DEF_PRIORITY)
+ return nr_to_scan;
+
+- try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false);
+-
+ /* skip this lruvec as it's low on cold pages */
+- return 0;
++ return try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false) ? -1 : 0;
+ }
+
+ static unsigned long get_nr_to_reclaim(struct scan_control *sc)
+@@ -4863,29 +4857,18 @@ static unsigned long get_nr_to_reclaim(s
+ if (!global_reclaim(sc))
+ return -1;
+
+- /* discount the previous progress for kswapd */
+- if (current_is_kswapd())
+- return sc->nr_to_reclaim + sc->last_reclaimed;
+-
+ return max(sc->nr_to_reclaim, compact_gap(sc->order));
+ }
+
+-static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
++static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
+ {
+- struct blk_plug plug;
++ long nr_to_scan;
+ unsigned long scanned = 0;
+ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
+
+- lru_add_drain();
+-
+- blk_start_plug(&plug);
+-
+- set_mm_walk(lruvec_pgdat(lruvec));
+-
+ while (true) {
+ int delta;
+ int swappiness;
+- unsigned long nr_to_scan;
+
+ if (sc->may_swap)
+ swappiness = get_swappiness(lruvec, sc);
+@@ -4895,7 +4878,7 @@ static void lru_gen_shrink_lruvec(struct
+ swappiness = 0;
+
+ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
+- if (!nr_to_scan)
++ if (nr_to_scan <= 0)
+ break;
+
+ delta = evict_pages(lruvec, sc, swappiness);
+@@ -4912,10 +4895,250 @@ static void lru_gen_shrink_lruvec(struct
+ cond_resched();
+ }
+
++ /* whether try_to_inc_max_seq() was successful */
++ return nr_to_scan < 0;
++}
++
++static int shrink_one(struct lruvec *lruvec, struct scan_control *sc)
++{
++ bool success;
++ unsigned long scanned = sc->nr_scanned;
++ unsigned long reclaimed = sc->nr_reclaimed;
++ int seg = lru_gen_memcg_seg(lruvec);
++ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
++ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
++
++ /* see the comment on MEMCG_NR_GENS */
++ if (!lruvec_is_sizable(lruvec, sc))
++ return seg != MEMCG_LRU_TAIL ? MEMCG_LRU_TAIL : MEMCG_LRU_YOUNG;
++
++ mem_cgroup_calculate_protection(NULL, memcg);
++
++ if (mem_cgroup_below_min(memcg))
++ return MEMCG_LRU_YOUNG;
++
++ if (mem_cgroup_below_low(memcg)) {
++ /* see the comment on MEMCG_NR_GENS */
++ if (seg != MEMCG_LRU_TAIL)
++ return MEMCG_LRU_TAIL;
++
++ memcg_memory_event(memcg, MEMCG_LOW);
++ }
++
++ success = try_to_shrink_lruvec(lruvec, sc);
++
++ shrink_slab(sc->gfp_mask, pgdat->node_id, memcg, sc->priority);
++
++ vmpressure(sc->gfp_mask, memcg, false, sc->nr_scanned - scanned,
++ sc->nr_reclaimed - reclaimed);
++
++ sc->nr_reclaimed += current->reclaim_state->reclaimed_slab;
++ current->reclaim_state->reclaimed_slab = 0;
++
++ return success ? MEMCG_LRU_YOUNG : 0;
++}
++
++#ifdef CONFIG_MEMCG
++
++static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
++{
++ int gen;
++ int bin;
++ int first_bin;
++ struct lruvec *lruvec;
++ struct lru_gen_page *lrugen;
++ const struct hlist_nulls_node *pos;
++ int op = 0;
++ struct mem_cgroup *memcg = NULL;
++ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
++
++ bin = first_bin = prandom_u32_max(MEMCG_NR_BINS);
++restart:
++ gen = get_memcg_gen(READ_ONCE(pgdat->memcg_lru.seq));
++
++ rcu_read_lock();
++
++ hlist_nulls_for_each_entry_rcu(lrugen, pos, &pgdat->memcg_lru.fifo[gen][bin], list) {
++ if (op)
++ lru_gen_rotate_memcg(lruvec, op);
++
++ mem_cgroup_put(memcg);
++
++ lruvec = container_of(lrugen, struct lruvec, lrugen);
++ memcg = lruvec_memcg(lruvec);
++
++ if (!mem_cgroup_tryget(memcg)) {
++ op = 0;
++ memcg = NULL;
++ continue;
++ }
++
++ rcu_read_unlock();
++
++ op = shrink_one(lruvec, sc);
++
++ if (sc->nr_reclaimed >= nr_to_reclaim)
++ goto success;
++
++ rcu_read_lock();
++ }
++
++ rcu_read_unlock();
++
++ /* restart if raced with lru_gen_rotate_memcg() */
++ if (gen != get_nulls_value(pos))
++ goto restart;
++
++ /* try the rest of the bins of the current generation */
++ bin = get_memcg_bin(bin + 1);
++ if (bin != first_bin)
++ goto restart;
++success:
++ if (op)
++ lru_gen_rotate_memcg(lruvec, op);
++
++ mem_cgroup_put(memcg);
++}
++
++static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
++{
++ struct blk_plug plug;
++
++ VM_WARN_ON_ONCE(global_reclaim(sc));
++
++ lru_add_drain();
++
++ blk_start_plug(&plug);
++
++ set_mm_walk(lruvec_pgdat(lruvec));
++
++ if (try_to_shrink_lruvec(lruvec, sc))
++ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG);
++
++ clear_mm_walk();
++
++ blk_finish_plug(&plug);
++}
++
++#else /* !CONFIG_MEMCG */
++
++static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
++{
++ BUILD_BUG();
++}
++
++static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
++{
++ BUILD_BUG();
++}
++
++#endif
++
++static void set_initial_priority(struct pglist_data *pgdat, struct scan_control *sc)
++{
++ int priority;
++ unsigned long reclaimable;
++ struct lruvec *lruvec = mem_cgroup_lruvec(NULL, pgdat);
++
++ if (sc->priority != DEF_PRIORITY || sc->nr_to_reclaim < MIN_LRU_BATCH)
++ return;
++ /*
++ * Determine the initial priority based on ((total / MEMCG_NR_GENS) >>
++ * priority) * reclaimed_to_scanned_ratio = nr_to_reclaim, where the
++ * estimated reclaimed_to_scanned_ratio = inactive / total.
++ */
++ reclaimable = node_page_state(pgdat, NR_INACTIVE_FILE);
++ if (get_swappiness(lruvec, sc))
++ reclaimable += node_page_state(pgdat, NR_INACTIVE_ANON);
++
++ reclaimable /= MEMCG_NR_GENS;
++
++ /* round down reclaimable and round up sc->nr_to_reclaim */
++ priority = fls_long(reclaimable) - 1 - fls_long(sc->nr_to_reclaim - 1);
++
++ sc->priority = clamp(priority, 0, DEF_PRIORITY);
++}
++
++static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)
++{
++ struct blk_plug plug;
++ unsigned long reclaimed = sc->nr_reclaimed;
++
++ VM_WARN_ON_ONCE(!global_reclaim(sc));
++
++ lru_add_drain();
++
++ blk_start_plug(&plug);
++
++ set_mm_walk(pgdat);
++
++ set_initial_priority(pgdat, sc);
++
++ if (current_is_kswapd())
++ sc->nr_reclaimed = 0;
++
++ if (mem_cgroup_disabled())
++ shrink_one(&pgdat->__lruvec, sc);
++ else
++ shrink_many(pgdat, sc);
++
++ if (current_is_kswapd())
++ sc->nr_reclaimed += reclaimed;
++
+ clear_mm_walk();
+
+ blk_finish_plug(&plug);
++
++ /* kswapd should never fail */
++ pgdat->kswapd_failures = 0;
++}
++
++#ifdef CONFIG_MEMCG
++void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
++{
++ int seg;
++ int old, new;
++ int bin = prandom_u32_max(MEMCG_NR_BINS);
++ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
++
++ spin_lock(&pgdat->memcg_lru.lock);
++
++ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
++
++ seg = 0;
++ new = old = lruvec->lrugen.gen;
++
++ /* see the comment on MEMCG_NR_GENS */
++ if (op == MEMCG_LRU_HEAD)
++ seg = MEMCG_LRU_HEAD;
++ else if (op == MEMCG_LRU_TAIL)
++ seg = MEMCG_LRU_TAIL;
++ else if (op == MEMCG_LRU_OLD)
++ new = get_memcg_gen(pgdat->memcg_lru.seq);
++ else if (op == MEMCG_LRU_YOUNG)
++ new = get_memcg_gen(pgdat->memcg_lru.seq + 1);
++ else
++ VM_WARN_ON_ONCE(true);
++
++ hlist_nulls_del_rcu(&lruvec->lrugen.list);
++
++ if (op == MEMCG_LRU_HEAD || op == MEMCG_LRU_OLD)
++ hlist_nulls_add_head_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
++ else
++ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
++
++ pgdat->memcg_lru.nr_memcgs[old]--;
++ pgdat->memcg_lru.nr_memcgs[new]++;
++
++ lruvec->lrugen.gen = new;
++ WRITE_ONCE(lruvec->lrugen.seg, seg);
++
++ if (!pgdat->memcg_lru.nr_memcgs[old] && old == get_memcg_gen(pgdat->memcg_lru.seq))
++ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
++
++ spin_unlock(&pgdat->memcg_lru.lock);
+ }
++#endif
+
+ /******************************************************************************
+ * state change
+@@ -5370,11 +5593,11 @@ static int run_cmd(char cmd, int memcg_i
+
+ if (!mem_cgroup_disabled()) {
+ rcu_read_lock();
++
+ memcg = mem_cgroup_from_id(memcg_id);
+-#ifdef CONFIG_MEMCG
+- if (memcg && !css_tryget(&memcg->css))
++ if (!mem_cgroup_tryget(memcg))
+ memcg = NULL;
+-#endif
++
+ rcu_read_unlock();
+
+ if (!memcg)
+@@ -5521,6 +5744,19 @@ void lru_gen_init_lruvec(struct lruvec *
+ }
+
+ #ifdef CONFIG_MEMCG
++
++void lru_gen_init_pgdat(struct pglist_data *pgdat)
++{
++ int i, j;
++
++ spin_lock_init(&pgdat->memcg_lru.lock);
++
++ for (i = 0; i < MEMCG_NR_GENS; i++) {
++ for (j = 0; j < MEMCG_NR_BINS; j++)
++ INIT_HLIST_NULLS_HEAD(&pgdat->memcg_lru.fifo[i][j], i);
++ }
++}
++
+ void lru_gen_init_memcg(struct mem_cgroup *memcg)
+ {
+ INIT_LIST_HEAD(&memcg->mm_list.fifo);
+@@ -5544,7 +5780,69 @@ void lru_gen_exit_memcg(struct mem_cgrou
+ }
+ }
+ }
+-#endif
++
++void lru_gen_online_memcg(struct mem_cgroup *memcg)
++{
++ int gen;
++ int nid;
++ int bin = prandom_u32_max(MEMCG_NR_BINS);
++
++ for_each_node(nid) {
++ struct pglist_data *pgdat = NODE_DATA(nid);
++ struct lruvec *lruvec = get_lruvec(memcg, nid);
++
++ spin_lock(&pgdat->memcg_lru.lock);
++
++ VM_WARN_ON_ONCE(!hlist_nulls_unhashed(&lruvec->lrugen.list));
++
++ gen = get_memcg_gen(pgdat->memcg_lru.seq);
++
++ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[gen][bin]);
++ pgdat->memcg_lru.nr_memcgs[gen]++;
++
++ lruvec->lrugen.gen = gen;
++
++ spin_unlock(&pgdat->memcg_lru.lock);
++ }
++}
++
++void lru_gen_offline_memcg(struct mem_cgroup *memcg)
++{
++ int nid;
++
++ for_each_node(nid) {
++ struct lruvec *lruvec = get_lruvec(memcg, nid);
++
++ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);
++ }
++}
++
++void lru_gen_release_memcg(struct mem_cgroup *memcg)
++{
++ int gen;
++ int nid;
++
++ for_each_node(nid) {
++ struct pglist_data *pgdat = NODE_DATA(nid);
++ struct lruvec *lruvec = get_lruvec(memcg, nid);
++
++ spin_lock(&pgdat->memcg_lru.lock);
++
++ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
++
++ gen = lruvec->lrugen.gen;
++
++ hlist_nulls_del_rcu(&lruvec->lrugen.list);
++ pgdat->memcg_lru.nr_memcgs[gen]--;
++
++ if (!pgdat->memcg_lru.nr_memcgs[gen] && gen == get_memcg_gen(pgdat->memcg_lru.seq))
++ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
++
++ spin_unlock(&pgdat->memcg_lru.lock);
++ }
++}
++
++#endif /* CONFIG_MEMCG */
+
+ static int __init init_lru_gen(void)
+ {
+@@ -5571,6 +5869,10 @@ static void lru_gen_shrink_lruvec(struct
+ {
+ }
+
++static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)
++{
++}
++
+ #endif /* CONFIG_LRU_GEN */
+
+ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
+@@ -5584,7 +5886,7 @@ static void shrink_lruvec(struct lruvec
+ bool proportional_reclaim;
+ struct blk_plug plug;
+
+- if (lru_gen_enabled()) {
++ if (lru_gen_enabled() && !global_reclaim(sc)) {
+ lru_gen_shrink_lruvec(lruvec, sc);
+ return;
+ }
+@@ -5826,6 +6128,11 @@ static void shrink_node(pg_data_t *pgdat
+ struct lruvec *target_lruvec;
+ bool reclaimable = false;
+
++ if (lru_gen_enabled() && global_reclaim(sc)) {
++ lru_gen_shrink_node(pgdat, sc);
++ return;
++ }
++
+ target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat);
+
+ again:
diff --git a/target/linux/generic/backport-6.1/020-v6.3-27-mm-multi-gen-LRU-clarify-scan_control-flags.patch b/target/linux/generic/backport-6.1/020-v6.3-27-mm-multi-gen-LRU-clarify-scan_control-flags.patch
new file mode 100644
index 0000000000..882f29e989
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-27-mm-multi-gen-LRU-clarify-scan_control-flags.patch
@@ -0,0 +1,196 @@
+From 93147736b5b3a21bea24313bfc7a696829932009 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Wed, 21 Dec 2022 21:19:05 -0700
+Subject: [PATCH 27/29] mm: multi-gen LRU: clarify scan_control flags
+
+Among the flags in scan_control:
+1. sc->may_swap, which indicates swap constraint due to memsw.max, is
+ supported as usual.
+2. sc->proactive, which indicates reclaim by memory.reclaim, may not
+ opportunistically skip the aging path, since it is considered less
+ latency sensitive.
+3. !(sc->gfp_mask & __GFP_IO), which indicates IO constraint, lowers
+ swappiness to prioritize file LRU, since clean file pages are more
+ likely to exist.
+4. sc->may_writepage and sc->may_unmap, which indicates opportunistic
+ reclaim, are rejected, since unmapped clean pages are already
+ prioritized. Scanning for more of them is likely futile and can
+ cause high reclaim latency when there is a large number of memcgs.
+
+The rest are handled by the existing code.
+
+Link: https://lkml.kernel.org/r/20221222041905.2431096-8-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 55 +++++++++++++++++++++++++++--------------------------
+ 1 file changed, 28 insertions(+), 27 deletions(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -2905,6 +2905,9 @@ static int get_swappiness(struct lruvec
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+
++ if (!sc->may_swap)
++ return 0;
++
+ if (!can_demote(pgdat->node_id, sc) &&
+ mem_cgroup_get_nr_swap_pages(memcg) < MIN_LRU_BATCH)
+ return 0;
+@@ -3952,7 +3955,7 @@ static void walk_mm(struct lruvec *lruve
+ } while (err == -EAGAIN);
+ }
+
+-static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat)
++static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat, bool force_alloc)
+ {
+ struct lru_gen_mm_walk *walk = current->reclaim_state->mm_walk;
+
+@@ -3960,7 +3963,7 @@ static struct lru_gen_mm_walk *set_mm_wa
+ VM_WARN_ON_ONCE(walk);
+
+ walk = &pgdat->mm_walk;
+- } else if (!pgdat && !walk) {
++ } else if (!walk && force_alloc) {
+ VM_WARN_ON_ONCE(current_is_kswapd());
+
+ walk = kzalloc(sizeof(*walk), __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
+@@ -4146,7 +4149,7 @@ static bool try_to_inc_max_seq(struct lr
+ goto done;
+ }
+
+- walk = set_mm_walk(NULL);
++ walk = set_mm_walk(NULL, true);
+ if (!walk) {
+ success = iterate_mm_list_nowalk(lruvec, max_seq);
+ goto done;
+@@ -4215,8 +4218,6 @@ static bool lruvec_is_reclaimable(struct
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ DEFINE_MIN_SEQ(lruvec);
+
+- VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
+-
+ /* see the comment on lru_gen_page */
+ gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
+ birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
+@@ -4472,12 +4473,8 @@ static bool isolate_page(struct lruvec *
+ {
+ bool success;
+
+- /* unmapping inhibited */
+- if (!sc->may_unmap && page_mapped(page))
+- return false;
+-
+ /* swapping inhibited */
+- if (!(sc->may_writepage && (sc->gfp_mask & __GFP_IO)) &&
++ if (!(sc->gfp_mask & __GFP_IO) &&
+ (PageDirty(page) ||
+ (PageAnon(page) && !PageSwapCache(page))))
+ return false;
+@@ -4574,9 +4571,8 @@ static int scan_pages(struct lruvec *lru
+ __count_vm_events(PGSCAN_ANON + type, isolated);
+
+ /*
+- * There might not be eligible pages due to reclaim_idx, may_unmap and
+- * may_writepage. Check the remaining to prevent livelock if it's not
+- * making progress.
++ * There might not be eligible pages due to reclaim_idx. Check the
++ * remaining to prevent livelock if it's not making progress.
+ */
+ return isolated || !remaining ? scanned : 0;
+ }
+@@ -4836,8 +4832,7 @@ static long get_nr_to_scan(struct lruvec
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ DEFINE_MAX_SEQ(lruvec);
+
+- if (mem_cgroup_below_min(memcg) ||
+- (mem_cgroup_below_low(memcg) && !sc->memcg_low_reclaim))
++ if (mem_cgroup_below_min(memcg))
+ return 0;
+
+ if (!should_run_aging(lruvec, max_seq, sc, can_swap, &nr_to_scan))
+@@ -4865,17 +4860,14 @@ static bool try_to_shrink_lruvec(struct
+ long nr_to_scan;
+ unsigned long scanned = 0;
+ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
++ int swappiness = get_swappiness(lruvec, sc);
++
++ /* clean file pages are more likely to exist */
++ if (swappiness && !(sc->gfp_mask & __GFP_IO))
++ swappiness = 1;
+
+ while (true) {
+ int delta;
+- int swappiness;
+-
+- if (sc->may_swap)
+- swappiness = get_swappiness(lruvec, sc);
+- else if (!cgroup_reclaim(sc) && get_swappiness(lruvec, sc))
+- swappiness = 1;
+- else
+- swappiness = 0;
+
+ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
+ if (nr_to_scan <= 0)
+@@ -5005,12 +4997,13 @@ static void lru_gen_shrink_lruvec(struct
+ struct blk_plug plug;
+
+ VM_WARN_ON_ONCE(global_reclaim(sc));
++ VM_WARN_ON_ONCE(!sc->may_writepage || !sc->may_unmap);
+
+ lru_add_drain();
+
+ blk_start_plug(&plug);
+
+- set_mm_walk(lruvec_pgdat(lruvec));
++ set_mm_walk(NULL, false);
+
+ if (try_to_shrink_lruvec(lruvec, sc))
+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG);
+@@ -5066,11 +5059,19 @@ static void lru_gen_shrink_node(struct p
+
+ VM_WARN_ON_ONCE(!global_reclaim(sc));
+
++ /*
++ * Unmapped clean pages are already prioritized. Scanning for more of
++ * them is likely futile and can cause high reclaim latency when there
++ * is a large number of memcgs.
++ */
++ if (!sc->may_writepage || !sc->may_unmap)
++ goto done;
++
+ lru_add_drain();
+
+ blk_start_plug(&plug);
+
+- set_mm_walk(pgdat);
++ set_mm_walk(pgdat, false);
+
+ set_initial_priority(pgdat, sc);
+
+@@ -5088,7 +5089,7 @@ static void lru_gen_shrink_node(struct p
+ clear_mm_walk();
+
+ blk_finish_plug(&plug);
+-
++done:
+ /* kswapd should never fail */
+ pgdat->kswapd_failures = 0;
+ }
+@@ -5656,7 +5657,7 @@ static ssize_t lru_gen_seq_write(struct
+ set_task_reclaim_state(current, &sc.reclaim_state);
+ flags = memalloc_noreclaim_save();
+ blk_start_plug(&plug);
+- if (!set_mm_walk(NULL)) {
++ if (!set_mm_walk(NULL, true)) {
+ err = -ENOMEM;
+ goto done;
+ }
diff --git a/target/linux/generic/backport-6.1/020-v6.3-28-mm-multi-gen-LRU-simplify-arch_has_hw_pte_young-chec.patch b/target/linux/generic/backport-6.1/020-v6.3-28-mm-multi-gen-LRU-simplify-arch_has_hw_pte_young-chec.patch
new file mode 100644
index 0000000000..38d0e5496c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-28-mm-multi-gen-LRU-simplify-arch_has_hw_pte_young-chec.patch
@@ -0,0 +1,34 @@
+From cf3297e4c7a928da8b2b2f0baff2f9c69ea57952 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Wed, 21 Dec 2022 21:19:06 -0700
+Subject: [PATCH 28/29] mm: multi-gen LRU: simplify arch_has_hw_pte_young()
+ check
+
+Scanning page tables when hardware does not set the accessed bit has
+no real use cases.
+
+Link: https://lkml.kernel.org/r/20221222041905.2431096-9-yuzhao@google.com
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Cc: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Jonathan Corbet <corbet@lwn.net>
+Cc: Michael Larabel <Michael@MichaelLarabel.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -4144,7 +4144,7 @@ static bool try_to_inc_max_seq(struct lr
+ * handful of PTEs. Spreading the work out over a period of time usually
+ * is less efficient, but it avoids bursty page faults.
+ */
+- if (!force_scan && !(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) {
++ if (!arch_has_hw_pte_young() || !get_cap(LRU_GEN_MM_WALK)) {
+ success = iterate_mm_list_nowalk(lruvec, max_seq);
+ goto done;
+ }
diff --git a/target/linux/generic/backport-6.1/020-v6.3-29-mm-multi-gen-LRU-avoid-futile-retries.patch b/target/linux/generic/backport-6.1/020-v6.3-29-mm-multi-gen-LRU-avoid-futile-retries.patch
new file mode 100644
index 0000000000..2d19d0d7da
--- /dev/null
+++ b/target/linux/generic/backport-6.1/020-v6.3-29-mm-multi-gen-LRU-avoid-futile-retries.patch
@@ -0,0 +1,88 @@
+From cc67f962cc53f6e1dfa92eb85b7b26fe83a3c66f Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yuzhao@google.com>
+Date: Mon, 13 Feb 2023 00:53:22 -0700
+Subject: [PATCH 29/29] mm: multi-gen LRU: avoid futile retries
+
+Recall that the per-node memcg LRU has two generations and they alternate
+when the last memcg (of a given node) is moved from one to the other.
+Each generation is also sharded into multiple bins to improve scalability.
+A reclaimer starts with a random bin (in the old generation) and, if it
+fails, it will retry, i.e., to try the rest of the bins.
+
+If a reclaimer fails with the last memcg, it should move this memcg to the
+young generation first, which causes the generations to alternate, and
+then retry. Otherwise, the retries will be futile because all other bins
+are empty.
+
+Link: https://lkml.kernel.org/r/20230213075322.1416966-1-yuzhao@google.com
+Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
+Signed-off-by: Yu Zhao <yuzhao@google.com>
+Reported-by: T.J. Mercier <tjmercier@google.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ mm/vmscan.c | 25 +++++++++++++++----------
+ 1 file changed, 15 insertions(+), 10 deletions(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -4934,18 +4934,20 @@ static int shrink_one(struct lruvec *lru
+
+ static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
+ {
++ int op;
+ int gen;
+ int bin;
+ int first_bin;
+ struct lruvec *lruvec;
+ struct lru_gen_page *lrugen;
++ struct mem_cgroup *memcg;
+ const struct hlist_nulls_node *pos;
+- int op = 0;
+- struct mem_cgroup *memcg = NULL;
+ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
+
+ bin = first_bin = prandom_u32_max(MEMCG_NR_BINS);
+ restart:
++ op = 0;
++ memcg = NULL;
+ gen = get_memcg_gen(READ_ONCE(pgdat->memcg_lru.seq));
+
+ rcu_read_lock();
+@@ -4969,14 +4971,22 @@ restart:
+
+ op = shrink_one(lruvec, sc);
+
+- if (sc->nr_reclaimed >= nr_to_reclaim)
+- goto success;
+-
+ rcu_read_lock();
++
++ if (sc->nr_reclaimed >= nr_to_reclaim)
++ break;
+ }
+
+ rcu_read_unlock();
+
++ if (op)
++ lru_gen_rotate_memcg(lruvec, op);
++
++ mem_cgroup_put(memcg);
++
++ if (sc->nr_reclaimed >= nr_to_reclaim)
++ return;
++
+ /* restart if raced with lru_gen_rotate_memcg() */
+ if (gen != get_nulls_value(pos))
+ goto restart;
+@@ -4985,11 +4995,6 @@ restart:
+ bin = get_memcg_bin(bin + 1);
+ if (bin != first_bin)
+ goto restart;
+-success:
+- if (op)
+- lru_gen_rotate_memcg(lruvec, op);
+-
+- mem_cgroup_put(memcg);
+ }
+
+ static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
diff --git a/target/linux/generic/backport-6.1/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch b/target/linux/generic/backport-6.1/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch
new file mode 100644
index 0000000000..82feb7421d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch
@@ -0,0 +1,65 @@
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:02 +0200
+Subject: [PATCH] MIPS: uasm: Enable muhu opcode for MIPS R6
+
+Enable the 'muhu' instruction, complementing the existing 'mulu', needed
+to implement a MIPS32 BPF JIT.
+
+Also fix a typo in the existing definition of 'dmulu'.
+
+Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com>
+
+This patch is a dependency for my 32-bit MIPS eBPF JIT.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+
+--- a/arch/mips/include/asm/uasm.h
++++ b/arch/mips/include/asm/uasm.h
+@@ -145,6 +145,7 @@ Ip_u1(_mtlo);
+ Ip_u3u1u2(_mul);
+ Ip_u1u2(_multu);
+ Ip_u3u1u2(_mulu);
++Ip_u3u1u2(_muhu);
+ Ip_u3u1u2(_nor);
+ Ip_u3u1u2(_or);
+ Ip_u2u1u3(_ori);
+--- a/arch/mips/mm/uasm-mips.c
++++ b/arch/mips/mm/uasm-mips.c
+@@ -90,7 +90,7 @@ static const struct insn insn_table[insn
+ RS | RT | RD},
+ [insn_dmtc0] = {M(cop0_op, dmtc_op, 0, 0, 0, 0), RT | RD | SET},
+ [insn_dmultu] = {M(spec_op, 0, 0, 0, 0, dmultu_op), RS | RT},
+- [insn_dmulu] = {M(spec_op, 0, 0, 0, dmult_dmul_op, dmultu_op),
++ [insn_dmulu] = {M(spec_op, 0, 0, 0, dmultu_dmulu_op, dmultu_op),
+ RS | RT | RD},
+ [insn_drotr] = {M(spec_op, 1, 0, 0, 0, dsrl_op), RT | RD | RE},
+ [insn_drotr32] = {M(spec_op, 1, 0, 0, 0, dsrl32_op), RT | RD | RE},
+@@ -150,6 +150,8 @@ static const struct insn insn_table[insn
+ [insn_mtlo] = {M(spec_op, 0, 0, 0, 0, mtlo_op), RS},
+ [insn_mulu] = {M(spec_op, 0, 0, 0, multu_mulu_op, multu_op),
+ RS | RT | RD},
++ [insn_muhu] = {M(spec_op, 0, 0, 0, multu_muhu_op, multu_op),
++ RS | RT | RD},
+ #ifndef CONFIG_CPU_MIPSR6
+ [insn_mul] = {M(spec2_op, 0, 0, 0, 0, mul_op), RS | RT | RD},
+ #else
+--- a/arch/mips/mm/uasm.c
++++ b/arch/mips/mm/uasm.c
+@@ -59,7 +59,7 @@ enum opcode {
+ insn_lddir, insn_ldpte, insn_ldx, insn_lh, insn_lhu, insn_ll, insn_lld,
+ insn_lui, insn_lw, insn_lwu, insn_lwx, insn_mfc0, insn_mfhc0, insn_mfhi,
+ insn_mflo, insn_modu, insn_movn, insn_movz, insn_mtc0, insn_mthc0,
+- insn_mthi, insn_mtlo, insn_mul, insn_multu, insn_mulu, insn_nor,
++ insn_mthi, insn_mtlo, insn_mul, insn_multu, insn_mulu, insn_muhu, insn_nor,
+ insn_or, insn_ori, insn_pref, insn_rfe, insn_rotr, insn_sb, insn_sc,
+ insn_scd, insn_seleqz, insn_selnez, insn_sd, insn_sh, insn_sll,
+ insn_sllv, insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra,
+@@ -344,6 +344,7 @@ I_u1(_mtlo)
+ I_u3u1u2(_mul)
+ I_u1u2(_multu)
+ I_u3u1u2(_mulu)
++I_u3u1u2(_muhu)
+ I_u3u1u2(_nor)
+ I_u3u1u2(_or)
+ I_u2u1u3(_ori)
diff --git a/target/linux/generic/backport-6.1/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch b/target/linux/generic/backport-6.1/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch
new file mode 100644
index 0000000000..3a4d573f80
--- /dev/null
+++ b/target/linux/generic/backport-6.1/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch
@@ -0,0 +1,31 @@
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:03 +0200
+Subject: [PATCH] mips: uasm: Add workaround for Loongson-2F nop CPU errata
+
+This patch implements a workaround for the Loongson-2F nop in generated,
+code, if the existing option CONFIG_CPU_NOP_WORKAROUND is set. Before,
+the binutils option -mfix-loongson2f-nop was enabled, but no workaround
+was done when emitting MIPS code. Now, the nop pseudo instruction is
+emitted as "or ax,ax,zero" instead of the default "sll zero,zero,0". This
+is consistent with the workaround implemented by binutils.
+
+Link: https://sourceware.org/legacy-ml/binutils/2009-11/msg00387.html
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
+---
+
+--- a/arch/mips/include/asm/uasm.h
++++ b/arch/mips/include/asm/uasm.h
+@@ -249,7 +249,11 @@ static inline void uasm_l##lb(struct uas
+ #define uasm_i_bnezl(buf, rs, off) uasm_i_bnel(buf, rs, 0, off)
+ #define uasm_i_ehb(buf) uasm_i_sll(buf, 0, 0, 3)
+ #define uasm_i_move(buf, a, b) UASM_i_ADDU(buf, a, 0, b)
++#ifdef CONFIG_CPU_NOP_WORKAROUNDS
++#define uasm_i_nop(buf) uasm_i_or(buf, 1, 1, 0)
++#else
+ #define uasm_i_nop(buf) uasm_i_sll(buf, 0, 0, 0)
++#endif
+ #define uasm_i_ssnop(buf) uasm_i_sll(buf, 0, 0, 1)
+
+ static inline void uasm_i_drotr_safe(u32 **p, unsigned int a1,
diff --git a/target/linux/generic/backport-6.1/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch b/target/linux/generic/backport-6.1/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch
new file mode 100644
index 0000000000..7980659961
--- /dev/null
+++ b/target/linux/generic/backport-6.1/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch
@@ -0,0 +1,3078 @@
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:04 +0200
+Subject: [PATCH] mips: bpf: Add eBPF JIT for 32-bit MIPS
+
+This is an implementation of an eBPF JIT for 32-bit MIPS I-V and MIPS32.
+The implementation supports all 32-bit and 64-bit ALU and JMP operations,
+including the recently-added atomics. 64-bit div/mod and 64-bit atomics
+are implemented using function calls to math64 and atomic64 functions,
+respectively. All 32-bit operations are implemented natively by the JIT,
+except if the CPU lacks ll/sc instructions.
+
+Register mapping
+================
+All 64-bit eBPF registers are mapped to native 32-bit MIPS register pairs,
+and does not use any stack scratch space for register swapping. This means
+that all eBPF register data is kept in CPU registers all the time, and
+this simplifies the register management a lot. It also reduces the JIT's
+pressure on temporary registers since we do not have to move data around.
+
+Native register pairs are ordered according to CPU endiannes, following
+the O32 calling convention for passing 64-bit arguments and return values.
+The eBPF return value, arguments and callee-saved registers are mapped to
+their native MIPS equivalents.
+
+Since the 32 highest bits in the eBPF FP (frame pointer) register are
+always zero, only one general-purpose register is actually needed for the
+mapping. The MIPS fp register is used for this purpose. The high bits are
+mapped to MIPS register r0. This saves us one CPU register, which is much
+needed for temporaries, while still allowing us to treat the R10 (FP)
+register just like any other eBPF register in the JIT.
+
+The MIPS gp (global pointer) and at (assembler temporary) registers are
+used as internal temporary registers for constant blinding. CPU registers
+t6-t9 are used internally by the JIT when constructing more complex 64-bit
+operations. This is precisely what is needed - two registers to store an
+operand value, and two more as scratch registers when performing the
+operation.
+
+The register mapping is shown below.
+
+ R0 - $v1, $v0 return value
+ R1 - $a1, $a0 argument 1, passed in registers
+ R2 - $a3, $a2 argument 2, passed in registers
+ R3 - $t1, $t0 argument 3, passed on stack
+ R4 - $t3, $t2 argument 4, passed on stack
+ R5 - $t4, $t3 argument 5, passed on stack
+ R6 - $s1, $s0 callee-saved
+ R7 - $s3, $s2 callee-saved
+ R8 - $s5, $s4 callee-saved
+ R9 - $s7, $s6 callee-saved
+ FP - $r0, $fp 32-bit frame pointer
+ AX - $gp, $at constant-blinding
+ $t6 - $t9 unallocated, JIT temporaries
+
+Jump offsets
+============
+The JIT tries to map all conditional JMP operations to MIPS conditional
+PC-relative branches. The MIPS branch offset field is 18 bits, in bytes,
+which is equivalent to the eBPF 16-bit instruction offset. However, since
+the JIT may emit more than one CPU instruction per eBPF instruction, the
+field width may overflow. If that happens, the JIT converts the long
+conditional jump to a short PC-relative branch with the condition
+inverted, jumping over a long unconditional absolute jmp (j).
+
+This conversion will change the instruction offset mapping used for jumps,
+and may in turn result in more branch offset overflows. The JIT therefore
+dry-runs the translation until no more branches are converted and the
+offsets do not change anymore. There is an upper bound on this of course,
+and if the JIT hits that limit, the last two iterations are run with all
+branches being converted.
+
+Tail call count
+===============
+The current tail call count is stored in the 16-byte area of the caller's
+stack frame that is reserved for the callee in the o32 ABI. The value is
+initialized in the prologue, and propagated to the tail-callee by skipping
+the initialization instructions when emitting the tail call.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+ create mode 100644 arch/mips/net/bpf_jit_comp.c
+ create mode 100644 arch/mips/net/bpf_jit_comp.h
+ create mode 100644 arch/mips/net/bpf_jit_comp32.c
+
+--- a/arch/mips/net/Makefile
++++ b/arch/mips/net/Makefile
+@@ -2,4 +2,9 @@
+ # MIPS networking code
+
+ obj-$(CONFIG_MIPS_CBPF_JIT) += bpf_jit.o bpf_jit_asm.o
+-obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o
++
++ifeq ($(CONFIG_32BIT),y)
++ obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp.o bpf_jit_comp32.o
++else
++ obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o
++endif
+--- /dev/null
++++ b/arch/mips/net/bpf_jit_comp.c
+@@ -0,0 +1,1020 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Just-In-Time compiler for eBPF bytecode on MIPS.
++ * Implementation of JIT functions common to 32-bit and 64-bit CPUs.
++ *
++ * Copyright (c) 2021 Anyfi Networks AB.
++ * Author: Johan Almbladh <johan.almbladh@gmail.com>
++ *
++ * Based on code and ideas from
++ * Copyright (c) 2017 Cavium, Inc.
++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
++ */
++
++/*
++ * Code overview
++ * =============
++ *
++ * - bpf_jit_comp.h
++ * Common definitions and utilities.
++ *
++ * - bpf_jit_comp.c
++ * Implementation of JIT top-level logic and exported JIT API functions.
++ * Implementation of internal operations shared by 32-bit and 64-bit code.
++ * JMP and ALU JIT control code, register control code, shared ALU and
++ * JMP/JMP32 JIT operations.
++ *
++ * - bpf_jit_comp32.c
++ * Implementation of functions to JIT prologue, epilogue and a single eBPF
++ * instruction for 32-bit MIPS CPUs. The functions use shared operations
++ * where possible, and implement the rest for 32-bit MIPS such as ALU64
++ * operations.
++ *
++ * - bpf_jit_comp64.c
++ * Ditto, for 64-bit MIPS CPUs.
++ *
++ * Zero and sign extension
++ * ========================
++ * 32-bit MIPS instructions on 64-bit MIPS registers use sign extension,
++ * but the eBPF instruction set mandates zero extension. We let the verifier
++ * insert explicit zero-extensions after 32-bit ALU operations, both for
++ * 32-bit and 64-bit MIPS JITs. Conditional JMP32 operations on 64-bit MIPs
++ * are JITed with sign extensions inserted when so expected.
++ *
++ * ALU operations
++ * ==============
++ * ALU operations on 32/64-bit MIPS and ALU64 operations on 64-bit MIPS are
++ * JITed in the following steps. ALU64 operations on 32-bit MIPS are more
++ * complicated and therefore only processed by special implementations in
++ * step (3).
++ *
++ * 1) valid_alu_i:
++ * Determine if an immediate operation can be emitted as such, or if
++ * we must fall back to the register version.
++ *
++ * 2) rewrite_alu_i:
++ * Convert BPF operation and immediate value to a canonical form for
++ * JITing. In some degenerate cases this form may be a no-op.
++ *
++ * 3) emit_alu_{i,i64,r,64}:
++ * Emit instructions for an ALU or ALU64 immediate or register operation.
++ *
++ * JMP operations
++ * ==============
++ * JMP and JMP32 operations require an JIT instruction offset table for
++ * translating the jump offset. This table is computed by dry-running the
++ * JIT without actually emitting anything. However, the computed PC-relative
++ * offset may overflow the 18-bit offset field width of the native MIPS
++ * branch instruction. In such cases, the long jump is converted into the
++ * following sequence.
++ *
++ * <branch> !<cond> +2 Inverted PC-relative branch
++ * nop Delay slot
++ * j <offset> Unconditional absolute long jump
++ * nop Delay slot
++ *
++ * Since this converted sequence alters the offset table, all offsets must
++ * be re-calculated. This may in turn trigger new branch conversions, so
++ * the process is repeated until no further changes are made. Normally it
++ * completes in 1-2 iterations. If JIT_MAX_ITERATIONS should reached, we
++ * fall back to converting every remaining jump operation. The branch
++ * conversion is independent of how the JMP or JMP32 condition is JITed.
++ *
++ * JMP32 and JMP operations are JITed as follows.
++ *
++ * 1) setup_jmp_{i,r}:
++ * Convert jump conditional and offset into a form that can be JITed.
++ * This form may be a no-op, a canonical form, or an inverted PC-relative
++ * jump if branch conversion is necessary.
++ *
++ * 2) valid_jmp_i:
++ * Determine if an immediate operations can be emitted as such, or if
++ * we must fall back to the register version. Applies to JMP32 for 32-bit
++ * MIPS, and both JMP and JMP32 for 64-bit MIPS.
++ *
++ * 3) emit_jmp_{i,i64,r,r64}:
++ * Emit instructions for an JMP or JMP32 immediate or register operation.
++ *
++ * 4) finish_jmp_{i,r}:
++ * Emit any instructions needed to finish the jump. This includes a nop
++ * for the delay slot if a branch was emitted, and a long absolute jump
++ * if the branch was converted.
++ */
++
++#include <linux/limits.h>
++#include <linux/bitops.h>
++#include <linux/errno.h>
++#include <linux/filter.h>
++#include <linux/bpf.h>
++#include <linux/slab.h>
++#include <asm/bitops.h>
++#include <asm/cacheflush.h>
++#include <asm/cpu-features.h>
++#include <asm/isa-rev.h>
++#include <asm/uasm.h>
++
++#include "bpf_jit_comp.h"
++
++/* Convenience macros for descriptor access */
++#define CONVERTED(desc) ((desc) & JIT_DESC_CONVERT)
++#define INDEX(desc) ((desc) & ~JIT_DESC_CONVERT)
++
++/*
++ * Push registers on the stack, starting at a given depth from the stack
++ * pointer and increasing. The next depth to be written is returned.
++ */
++int push_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth)
++{
++ int reg;
++
++ for (reg = 0; reg < BITS_PER_BYTE * sizeof(mask); reg++)
++ if (mask & BIT(reg)) {
++ if ((excl & BIT(reg)) == 0) {
++ if (sizeof(long) == 4)
++ emit(ctx, sw, reg, depth, MIPS_R_SP);
++ else /* sizeof(long) == 8 */
++ emit(ctx, sd, reg, depth, MIPS_R_SP);
++ }
++ depth += sizeof(long);
++ }
++
++ ctx->stack_used = max((int)ctx->stack_used, depth);
++ return depth;
++}
++
++/*
++ * Pop registers from the stack, starting at a given depth from the stack
++ * pointer and increasing. The next depth to be read is returned.
++ */
++int pop_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth)
++{
++ int reg;
++
++ for (reg = 0; reg < BITS_PER_BYTE * sizeof(mask); reg++)
++ if (mask & BIT(reg)) {
++ if ((excl & BIT(reg)) == 0) {
++ if (sizeof(long) == 4)
++ emit(ctx, lw, reg, depth, MIPS_R_SP);
++ else /* sizeof(long) == 8 */
++ emit(ctx, ld, reg, depth, MIPS_R_SP);
++ }
++ depth += sizeof(long);
++ }
++
++ return depth;
++}
++
++/* Compute the 28-bit jump target address from a BPF program location */
++int get_target(struct jit_context *ctx, u32 loc)
++{
++ u32 index = INDEX(ctx->descriptors[loc]);
++ unsigned long pc = (unsigned long)&ctx->target[ctx->jit_index];
++ unsigned long addr = (unsigned long)&ctx->target[index];
++
++ if (!ctx->target)
++ return 0;
++
++ if ((addr ^ pc) & ~MIPS_JMP_MASK)
++ return -1;
++
++ return addr & MIPS_JMP_MASK;
++}
++
++/* Compute the PC-relative offset to relative BPF program offset */
++int get_offset(const struct jit_context *ctx, int off)
++{
++ return (INDEX(ctx->descriptors[ctx->bpf_index + off]) -
++ ctx->jit_index - 1) * sizeof(u32);
++}
++
++/* dst = imm (register width) */
++void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm)
++{
++ if (imm >= -0x8000 && imm <= 0x7fff) {
++ emit(ctx, addiu, dst, MIPS_R_ZERO, imm);
++ } else {
++ emit(ctx, lui, dst, (s16)((u32)imm >> 16));
++ emit(ctx, ori, dst, dst, (u16)(imm & 0xffff));
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* dst = src (register width) */
++void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src)
++{
++ emit(ctx, ori, dst, src, 0);
++ clobber_reg(ctx, dst);
++}
++
++/* Validate ALU immediate range */
++bool valid_alu_i(u8 op, s32 imm)
++{
++ switch (BPF_OP(op)) {
++ case BPF_NEG:
++ case BPF_LSH:
++ case BPF_RSH:
++ case BPF_ARSH:
++ /* All legal eBPF values are valid */
++ return true;
++ case BPF_ADD:
++ /* imm must be 16 bits */
++ return imm >= -0x8000 && imm <= 0x7fff;
++ case BPF_SUB:
++ /* -imm must be 16 bits */
++ return imm >= -0x7fff && imm <= 0x8000;
++ case BPF_AND:
++ case BPF_OR:
++ case BPF_XOR:
++ /* imm must be 16 bits unsigned */
++ return imm >= 0 && imm <= 0xffff;
++ case BPF_MUL:
++ /* imm must be zero or a positive power of two */
++ return imm == 0 || (imm > 0 && is_power_of_2(imm));
++ case BPF_DIV:
++ case BPF_MOD:
++ /* imm must be an 17-bit power of two */
++ return (u32)imm <= 0x10000 && is_power_of_2((u32)imm);
++ }
++ return false;
++}
++
++/* Rewrite ALU immediate operation */
++bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val)
++{
++ bool act = true;
++
++ switch (BPF_OP(op)) {
++ case BPF_LSH:
++ case BPF_RSH:
++ case BPF_ARSH:
++ case BPF_ADD:
++ case BPF_SUB:
++ case BPF_OR:
++ case BPF_XOR:
++ /* imm == 0 is a no-op */
++ act = imm != 0;
++ break;
++ case BPF_MUL:
++ if (imm == 1) {
++ /* dst * 1 is a no-op */
++ act = false;
++ } else if (imm == 0) {
++ /* dst * 0 is dst & 0 */
++ op = BPF_AND;
++ } else {
++ /* dst * (1 << n) is dst << n */
++ op = BPF_LSH;
++ imm = ilog2(abs(imm));
++ }
++ break;
++ case BPF_DIV:
++ if (imm == 1) {
++ /* dst / 1 is a no-op */
++ act = false;
++ } else {
++ /* dst / (1 << n) is dst >> n */
++ op = BPF_RSH;
++ imm = ilog2(imm);
++ }
++ break;
++ case BPF_MOD:
++ /* dst % (1 << n) is dst & ((1 << n) - 1) */
++ op = BPF_AND;
++ imm--;
++ break;
++ }
++
++ *alu = op;
++ *val = imm;
++ return act;
++}
++
++/* ALU immediate operation (32-bit) */
++void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = -dst */
++ case BPF_NEG:
++ emit(ctx, subu, dst, MIPS_R_ZERO, dst);
++ break;
++ /* dst = dst & imm */
++ case BPF_AND:
++ emit(ctx, andi, dst, dst, (u16)imm);
++ break;
++ /* dst = dst | imm */
++ case BPF_OR:
++ emit(ctx, ori, dst, dst, (u16)imm);
++ break;
++ /* dst = dst ^ imm */
++ case BPF_XOR:
++ emit(ctx, xori, dst, dst, (u16)imm);
++ break;
++ /* dst = dst << imm */
++ case BPF_LSH:
++ emit(ctx, sll, dst, dst, imm);
++ break;
++ /* dst = dst >> imm */
++ case BPF_RSH:
++ emit(ctx, srl, dst, dst, imm);
++ break;
++ /* dst = dst >> imm (arithmetic) */
++ case BPF_ARSH:
++ emit(ctx, sra, dst, dst, imm);
++ break;
++ /* dst = dst + imm */
++ case BPF_ADD:
++ emit(ctx, addiu, dst, dst, imm);
++ break;
++ /* dst = dst - imm */
++ case BPF_SUB:
++ emit(ctx, addiu, dst, dst, -imm);
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* ALU register operation (32-bit) */
++void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst & src */
++ case BPF_AND:
++ emit(ctx, and, dst, dst, src);
++ break;
++ /* dst = dst | src */
++ case BPF_OR:
++ emit(ctx, or, dst, dst, src);
++ break;
++ /* dst = dst ^ src */
++ case BPF_XOR:
++ emit(ctx, xor, dst, dst, src);
++ break;
++ /* dst = dst << src */
++ case BPF_LSH:
++ emit(ctx, sllv, dst, dst, src);
++ break;
++ /* dst = dst >> src */
++ case BPF_RSH:
++ emit(ctx, srlv, dst, dst, src);
++ break;
++ /* dst = dst >> src (arithmetic) */
++ case BPF_ARSH:
++ emit(ctx, srav, dst, dst, src);
++ break;
++ /* dst = dst + src */
++ case BPF_ADD:
++ emit(ctx, addu, dst, dst, src);
++ break;
++ /* dst = dst - src */
++ case BPF_SUB:
++ emit(ctx, subu, dst, dst, src);
++ break;
++ /* dst = dst * src */
++ case BPF_MUL:
++ if (cpu_has_mips32r1 || cpu_has_mips32r6) {
++ emit(ctx, mul, dst, dst, src);
++ } else {
++ emit(ctx, multu, dst, src);
++ emit(ctx, mflo, dst);
++ }
++ break;
++ /* dst = dst / src */
++ case BPF_DIV:
++ if (cpu_has_mips32r6) {
++ emit(ctx, divu_r6, dst, dst, src);
++ } else {
++ emit(ctx, divu, dst, src);
++ emit(ctx, mflo, dst);
++ }
++ break;
++ /* dst = dst % src */
++ case BPF_MOD:
++ if (cpu_has_mips32r6) {
++ emit(ctx, modu, dst, dst, src);
++ } else {
++ emit(ctx, divu, dst, src);
++ emit(ctx, mfhi, dst);
++ }
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Atomic read-modify-write (32-bit) */
++void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code)
++{
++ emit(ctx, ll, MIPS_R_T9, off, dst);
++ switch (code) {
++ case BPF_ADD:
++ emit(ctx, addu, MIPS_R_T8, MIPS_R_T9, src);
++ break;
++ case BPF_AND:
++ emit(ctx, and, MIPS_R_T8, MIPS_R_T9, src);
++ break;
++ case BPF_OR:
++ emit(ctx, or, MIPS_R_T8, MIPS_R_T9, src);
++ break;
++ case BPF_XOR:
++ emit(ctx, xor, MIPS_R_T8, MIPS_R_T9, src);
++ break;
++ }
++ emit(ctx, sc, MIPS_R_T8, off, dst);
++ emit(ctx, beqz, MIPS_R_T8, -16);
++ emit(ctx, nop); /* Delay slot */
++}
++
++/* Atomic compare-and-exchange (32-bit) */
++void emit_cmpxchg_r(struct jit_context *ctx, u8 dst, u8 src, u8 res, s16 off)
++{
++ emit(ctx, ll, MIPS_R_T9, off, dst);
++ emit(ctx, bne, MIPS_R_T9, res, 12);
++ emit(ctx, move, MIPS_R_T8, src); /* Delay slot */
++ emit(ctx, sc, MIPS_R_T8, off, dst);
++ emit(ctx, beqz, MIPS_R_T8, -20);
++ emit(ctx, move, res, MIPS_R_T9); /* Delay slot */
++ clobber_reg(ctx, res);
++}
++
++/* Swap bytes and truncate a register word or half word */
++void emit_bswap_r(struct jit_context *ctx, u8 dst, u32 width)
++{
++ u8 tmp = MIPS_R_T8;
++ u8 msk = MIPS_R_T9;
++
++ switch (width) {
++ /* Swap bytes in a word */
++ case 32:
++ if (cpu_has_mips32r2 || cpu_has_mips32r6) {
++ emit(ctx, wsbh, dst, dst);
++ emit(ctx, rotr, dst, dst, 16);
++ } else {
++ emit(ctx, sll, tmp, dst, 16); /* tmp = dst << 16 */
++ emit(ctx, srl, dst, dst, 16); /* dst = dst >> 16 */
++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */
++
++ emit(ctx, lui, msk, 0xff); /* msk = 0x00ff0000 */
++ emit(ctx, ori, msk, msk, 0xff); /* msk = msk | 0xff */
++
++ emit(ctx, and, tmp, dst, msk); /* tmp = dst & msk */
++ emit(ctx, sll, tmp, tmp, 8); /* tmp = tmp << 8 */
++ emit(ctx, srl, dst, dst, 8); /* dst = dst >> 8 */
++ emit(ctx, and, dst, dst, msk); /* dst = dst & msk */
++ emit(ctx, or, dst, dst, tmp); /* reg = dst | tmp */
++ }
++ break;
++ /* Swap bytes in a half word */
++ case 16:
++ if (cpu_has_mips32r2 || cpu_has_mips32r6) {
++ emit(ctx, wsbh, dst, dst);
++ emit(ctx, andi, dst, dst, 0xffff);
++ } else {
++ emit(ctx, andi, tmp, dst, 0xff00); /* t = d & 0xff00 */
++ emit(ctx, srl, tmp, tmp, 8); /* t = t >> 8 */
++ emit(ctx, andi, dst, dst, 0x00ff); /* d = d & 0x00ff */
++ emit(ctx, sll, dst, dst, 8); /* d = d << 8 */
++ emit(ctx, or, dst, dst, tmp); /* d = d | t */
++ }
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Validate jump immediate range */
++bool valid_jmp_i(u8 op, s32 imm)
++{
++ switch (op) {
++ case JIT_JNOP:
++ /* Immediate value not used */
++ return true;
++ case BPF_JEQ:
++ case BPF_JNE:
++ /* No immediate operation */
++ return false;
++ case BPF_JSET:
++ case JIT_JNSET:
++ /* imm must be 16 bits unsigned */
++ return imm >= 0 && imm <= 0xffff;
++ case BPF_JGE:
++ case BPF_JLT:
++ case BPF_JSGE:
++ case BPF_JSLT:
++ /* imm must be 16 bits */
++ return imm >= -0x8000 && imm <= 0x7fff;
++ case BPF_JGT:
++ case BPF_JLE:
++ case BPF_JSGT:
++ case BPF_JSLE:
++ /* imm + 1 must be 16 bits */
++ return imm >= -0x8001 && imm <= 0x7ffe;
++ }
++ return false;
++}
++
++/* Invert a conditional jump operation */
++static u8 invert_jmp(u8 op)
++{
++ switch (op) {
++ case BPF_JA: return JIT_JNOP;
++ case BPF_JEQ: return BPF_JNE;
++ case BPF_JNE: return BPF_JEQ;
++ case BPF_JSET: return JIT_JNSET;
++ case BPF_JGT: return BPF_JLE;
++ case BPF_JGE: return BPF_JLT;
++ case BPF_JLT: return BPF_JGE;
++ case BPF_JLE: return BPF_JGT;
++ case BPF_JSGT: return BPF_JSLE;
++ case BPF_JSGE: return BPF_JSLT;
++ case BPF_JSLT: return BPF_JSGE;
++ case BPF_JSLE: return BPF_JSGT;
++ }
++ return 0;
++}
++
++/* Prepare a PC-relative jump operation */
++static void setup_jmp(struct jit_context *ctx, u8 bpf_op,
++ s16 bpf_off, u8 *jit_op, s32 *jit_off)
++{
++ u32 *descp = &ctx->descriptors[ctx->bpf_index];
++ int op = bpf_op;
++ int offset = 0;
++
++ /* Do not compute offsets on the first pass */
++ if (INDEX(*descp) == 0)
++ goto done;
++
++ /* Skip jumps never taken */
++ if (bpf_op == JIT_JNOP)
++ goto done;
++
++ /* Convert jumps always taken */
++ if (bpf_op == BPF_JA)
++ *descp |= JIT_DESC_CONVERT;
++
++ /*
++ * Current ctx->jit_index points to the start of the branch preamble.
++ * Since the preamble differs among different branch conditionals,
++ * the current index cannot be used to compute the branch offset.
++ * Instead, we use the offset table value for the next instruction,
++ * which gives the index immediately after the branch delay slot.
++ */
++ if (!CONVERTED(*descp)) {
++ int target = ctx->bpf_index + bpf_off + 1;
++ int origin = ctx->bpf_index + 1;
++
++ offset = (INDEX(ctx->descriptors[target]) -
++ INDEX(ctx->descriptors[origin]) + 1) * sizeof(u32);
++ }
++
++ /*
++ * The PC-relative branch offset field on MIPS is 18 bits signed,
++ * so if the computed offset is larger than this we generate a an
++ * absolute jump that we skip with an inverted conditional branch.
++ */
++ if (CONVERTED(*descp) || offset < -0x20000 || offset > 0x1ffff) {
++ offset = 3 * sizeof(u32);
++ op = invert_jmp(bpf_op);
++ ctx->changes += !CONVERTED(*descp);
++ *descp |= JIT_DESC_CONVERT;
++ }
++
++done:
++ *jit_off = offset;
++ *jit_op = op;
++}
++
++/* Prepare a PC-relative jump operation with immediate conditional */
++void setup_jmp_i(struct jit_context *ctx, s32 imm, u8 width,
++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off)
++{
++ bool always = false;
++ bool never = false;
++
++ switch (bpf_op) {
++ case BPF_JEQ:
++ case BPF_JNE:
++ break;
++ case BPF_JSET:
++ case BPF_JLT:
++ never = imm == 0;
++ break;
++ case BPF_JGE:
++ always = imm == 0;
++ break;
++ case BPF_JGT:
++ never = (u32)imm == U32_MAX;
++ break;
++ case BPF_JLE:
++ always = (u32)imm == U32_MAX;
++ break;
++ case BPF_JSGT:
++ never = imm == S32_MAX && width == 32;
++ break;
++ case BPF_JSGE:
++ always = imm == S32_MIN && width == 32;
++ break;
++ case BPF_JSLT:
++ never = imm == S32_MIN && width == 32;
++ break;
++ case BPF_JSLE:
++ always = imm == S32_MAX && width == 32;
++ break;
++ }
++
++ if (never)
++ bpf_op = JIT_JNOP;
++ if (always)
++ bpf_op = BPF_JA;
++ setup_jmp(ctx, bpf_op, bpf_off, jit_op, jit_off);
++}
++
++/* Prepare a PC-relative jump operation with register conditional */
++void setup_jmp_r(struct jit_context *ctx, bool same_reg,
++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off)
++{
++ switch (bpf_op) {
++ case BPF_JSET:
++ break;
++ case BPF_JEQ:
++ case BPF_JGE:
++ case BPF_JLE:
++ case BPF_JSGE:
++ case BPF_JSLE:
++ if (same_reg)
++ bpf_op = BPF_JA;
++ break;
++ case BPF_JNE:
++ case BPF_JLT:
++ case BPF_JGT:
++ case BPF_JSGT:
++ case BPF_JSLT:
++ if (same_reg)
++ bpf_op = JIT_JNOP;
++ break;
++ }
++ setup_jmp(ctx, bpf_op, bpf_off, jit_op, jit_off);
++}
++
++/* Finish a PC-relative jump operation */
++int finish_jmp(struct jit_context *ctx, u8 jit_op, s16 bpf_off)
++{
++ /* Emit conditional branch delay slot */
++ if (jit_op != JIT_JNOP)
++ emit(ctx, nop);
++ /*
++ * Emit an absolute long jump with delay slot,
++ * if the PC-relative branch was converted.
++ */
++ if (CONVERTED(ctx->descriptors[ctx->bpf_index])) {
++ int target = get_target(ctx, ctx->bpf_index + bpf_off + 1);
++
++ if (target < 0)
++ return -1;
++ emit(ctx, j, target);
++ emit(ctx, nop);
++ }
++ return 0;
++}
++
++/* Jump immediate (32-bit) */
++void emit_jmp_i(struct jit_context *ctx, u8 dst, s32 imm, s32 off, u8 op)
++{
++ switch (op) {
++ /* No-op, used internally for branch optimization */
++ case JIT_JNOP:
++ break;
++ /* PC += off if dst & imm */
++ case BPF_JSET:
++ emit(ctx, andi, MIPS_R_T9, dst, (u16)imm);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */
++ case JIT_JNSET:
++ emit(ctx, andi, MIPS_R_T9, dst, (u16)imm);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst > imm */
++ case BPF_JGT:
++ emit(ctx, sltiu, MIPS_R_T9, dst, imm + 1);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst >= imm */
++ case BPF_JGE:
++ emit(ctx, sltiu, MIPS_R_T9, dst, imm);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst < imm */
++ case BPF_JLT:
++ emit(ctx, sltiu, MIPS_R_T9, dst, imm);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst <= imm */
++ case BPF_JLE:
++ emit(ctx, sltiu, MIPS_R_T9, dst, imm + 1);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst > imm (signed) */
++ case BPF_JSGT:
++ emit(ctx, slti, MIPS_R_T9, dst, imm + 1);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst >= imm (signed) */
++ case BPF_JSGE:
++ emit(ctx, slti, MIPS_R_T9, dst, imm);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst < imm (signed) */
++ case BPF_JSLT:
++ emit(ctx, slti, MIPS_R_T9, dst, imm);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JSLE:
++ emit(ctx, slti, MIPS_R_T9, dst, imm + 1);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ }
++}
++
++/* Jump register (32-bit) */
++void emit_jmp_r(struct jit_context *ctx, u8 dst, u8 src, s32 off, u8 op)
++{
++ switch (op) {
++ /* No-op, used internally for branch optimization */
++ case JIT_JNOP:
++ break;
++ /* PC += off if dst == src */
++ case BPF_JEQ:
++ emit(ctx, beq, dst, src, off);
++ break;
++ /* PC += off if dst != src */
++ case BPF_JNE:
++ emit(ctx, bne, dst, src, off);
++ break;
++ /* PC += off if dst & src */
++ case BPF_JSET:
++ emit(ctx, and, MIPS_R_T9, dst, src);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */
++ case JIT_JNSET:
++ emit(ctx, and, MIPS_R_T9, dst, src);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst > src */
++ case BPF_JGT:
++ emit(ctx, sltu, MIPS_R_T9, src, dst);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst >= src */
++ case BPF_JGE:
++ emit(ctx, sltu, MIPS_R_T9, dst, src);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst < src */
++ case BPF_JLT:
++ emit(ctx, sltu, MIPS_R_T9, dst, src);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst <= src */
++ case BPF_JLE:
++ emit(ctx, sltu, MIPS_R_T9, src, dst);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst > src (signed) */
++ case BPF_JSGT:
++ emit(ctx, slt, MIPS_R_T9, src, dst);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst >= src (signed) */
++ case BPF_JSGE:
++ emit(ctx, slt, MIPS_R_T9, dst, src);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst < src (signed) */
++ case BPF_JSLT:
++ emit(ctx, slt, MIPS_R_T9, dst, src);
++ emit(ctx, bnez, MIPS_R_T9, off);
++ break;
++ /* PC += off if dst <= src (signed) */
++ case BPF_JSLE:
++ emit(ctx, slt, MIPS_R_T9, src, dst);
++ emit(ctx, beqz, MIPS_R_T9, off);
++ break;
++ }
++}
++
++/* Jump always */
++int emit_ja(struct jit_context *ctx, s16 off)
++{
++ int target = get_target(ctx, ctx->bpf_index + off + 1);
++
++ if (target < 0)
++ return -1;
++ emit(ctx, j, target);
++ emit(ctx, nop);
++ return 0;
++}
++
++/* Jump to epilogue */
++int emit_exit(struct jit_context *ctx)
++{
++ int target = get_target(ctx, ctx->program->len);
++
++ if (target < 0)
++ return -1;
++ emit(ctx, j, target);
++ emit(ctx, nop);
++ return 0;
++}
++
++/* Build the program body from eBPF bytecode */
++static int build_body(struct jit_context *ctx)
++{
++ const struct bpf_prog *prog = ctx->program;
++ unsigned int i;
++
++ ctx->stack_used = 0;
++ for (i = 0; i < prog->len; i++) {
++ const struct bpf_insn *insn = &prog->insnsi[i];
++ u32 *descp = &ctx->descriptors[i];
++ int ret;
++
++ access_reg(ctx, insn->src_reg);
++ access_reg(ctx, insn->dst_reg);
++
++ ctx->bpf_index = i;
++ if (ctx->target == NULL) {
++ ctx->changes += INDEX(*descp) != ctx->jit_index;
++ *descp &= JIT_DESC_CONVERT;
++ *descp |= ctx->jit_index;
++ }
++
++ ret = build_insn(insn, ctx);
++ if (ret < 0)
++ return ret;
++
++ if (ret > 0) {
++ i++;
++ if (ctx->target == NULL)
++ descp[1] = ctx->jit_index;
++ }
++ }
++
++ /* Store the end offset, where the epilogue begins */
++ ctx->descriptors[prog->len] = ctx->jit_index;
++ return 0;
++}
++
++/* Set the branch conversion flag on all instructions */
++static void set_convert_flag(struct jit_context *ctx, bool enable)
++{
++ const struct bpf_prog *prog = ctx->program;
++ u32 flag = enable ? JIT_DESC_CONVERT : 0;
++ unsigned int i;
++
++ for (i = 0; i <= prog->len; i++)
++ ctx->descriptors[i] = INDEX(ctx->descriptors[i]) | flag;
++}
++
++static void jit_fill_hole(void *area, unsigned int size)
++{
++ u32 *p;
++
++ /* We are guaranteed to have aligned memory. */
++ for (p = area; size >= sizeof(u32); size -= sizeof(u32))
++ uasm_i_break(&p, BRK_BUG); /* Increments p */
++}
++
++bool bpf_jit_needs_zext(void)
++{
++ return true;
++}
++
++struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
++{
++ struct bpf_prog *tmp, *orig_prog = prog;
++ struct bpf_binary_header *header = NULL;
++ struct jit_context ctx;
++ bool tmp_blinded = false;
++ unsigned int tmp_idx;
++ unsigned int image_size;
++ u8 *image_ptr;
++ int tries;
++
++ /*
++ * If BPF JIT was not enabled then we must fall back to
++ * the interpreter.
++ */
++ if (!prog->jit_requested)
++ return orig_prog;
++ /*
++ * If constant blinding was enabled and we failed during blinding
++ * then we must fall back to the interpreter. Otherwise, we save
++ * the new JITed code.
++ */
++ tmp = bpf_jit_blind_constants(prog);
++ if (IS_ERR(tmp))
++ return orig_prog;
++ if (tmp != prog) {
++ tmp_blinded = true;
++ prog = tmp;
++ }
++
++ memset(&ctx, 0, sizeof(ctx));
++ ctx.program = prog;
++
++ /*
++ * Not able to allocate memory for descriptors[], then
++ * we must fall back to the interpreter
++ */
++ ctx.descriptors = kcalloc(prog->len + 1, sizeof(*ctx.descriptors),
++ GFP_KERNEL);
++ if (ctx.descriptors == NULL)
++ goto out_err;
++
++ /* First pass discovers used resources */
++ if (build_body(&ctx) < 0)
++ goto out_err;
++ /*
++ * Second pass computes instruction offsets.
++ * If any PC-relative branches are out of range, a sequence of
++ * a PC-relative branch + a jump is generated, and we have to
++ * try again from the beginning to generate the new offsets.
++ * This is done until no additional conversions are necessary.
++ * The last two iterations are done with all branches being
++ * converted, to guarantee offset table convergence within a
++ * fixed number of iterations.
++ */
++ ctx.jit_index = 0;
++ build_prologue(&ctx);
++ tmp_idx = ctx.jit_index;
++
++ tries = JIT_MAX_ITERATIONS;
++ do {
++ ctx.jit_index = tmp_idx;
++ ctx.changes = 0;
++ if (tries == 2)
++ set_convert_flag(&ctx, true);
++ if (build_body(&ctx) < 0)
++ goto out_err;
++ } while (ctx.changes > 0 && --tries > 0);
++
++ if (WARN_ONCE(ctx.changes > 0, "JIT offsets failed to converge"))
++ goto out_err;
++
++ build_epilogue(&ctx, MIPS_R_RA);
++
++ /* Now we know the size of the structure to make */
++ image_size = sizeof(u32) * ctx.jit_index;
++ header = bpf_jit_binary_alloc(image_size, &image_ptr,
++ sizeof(u32), jit_fill_hole);
++ /*
++ * Not able to allocate memory for the structure then
++ * we must fall back to the interpretation
++ */
++ if (header == NULL)
++ goto out_err;
++
++ /* Actual pass to generate final JIT code */
++ ctx.target = (u32 *)image_ptr;
++ ctx.jit_index = 0;
++
++ /*
++ * If building the JITed code fails somehow,
++ * we fall back to the interpretation.
++ */
++ build_prologue(&ctx);
++ if (build_body(&ctx) < 0)
++ goto out_err;
++ build_epilogue(&ctx, MIPS_R_RA);
++
++ /* Populate line info meta data */
++ set_convert_flag(&ctx, false);
++ bpf_prog_fill_jited_linfo(prog, &ctx.descriptors[1]);
++
++ /* Set as read-only exec and flush instruction cache */
++ bpf_jit_binary_lock_ro(header);
++ flush_icache_range((unsigned long)header,
++ (unsigned long)&ctx.target[ctx.jit_index]);
++
++ if (bpf_jit_enable > 1)
++ bpf_jit_dump(prog->len, image_size, 2, ctx.target);
++
++ prog->bpf_func = (void *)ctx.target;
++ prog->jited = 1;
++ prog->jited_len = image_size;
++
++out:
++ if (tmp_blinded)
++ bpf_jit_prog_release_other(prog, prog == orig_prog ?
++ tmp : orig_prog);
++ kfree(ctx.descriptors);
++ return prog;
++
++out_err:
++ prog = orig_prog;
++ if (header)
++ bpf_jit_binary_free(header);
++ goto out;
++}
+--- /dev/null
++++ b/arch/mips/net/bpf_jit_comp.h
+@@ -0,0 +1,211 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Just-In-Time compiler for eBPF bytecode on 32-bit and 64-bit MIPS.
++ *
++ * Copyright (c) 2021 Anyfi Networks AB.
++ * Author: Johan Almbladh <johan.almbladh@gmail.com>
++ *
++ * Based on code and ideas from
++ * Copyright (c) 2017 Cavium, Inc.
++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
++ */
++
++#ifndef _BPF_JIT_COMP_H
++#define _BPF_JIT_COMP_H
++
++/* MIPS registers */
++#define MIPS_R_ZERO 0 /* Const zero */
++#define MIPS_R_AT 1 /* Asm temp */
++#define MIPS_R_V0 2 /* Result */
++#define MIPS_R_V1 3 /* Result */
++#define MIPS_R_A0 4 /* Argument */
++#define MIPS_R_A1 5 /* Argument */
++#define MIPS_R_A2 6 /* Argument */
++#define MIPS_R_A3 7 /* Argument */
++#define MIPS_R_A4 8 /* Arg (n64) */
++#define MIPS_R_A5 9 /* Arg (n64) */
++#define MIPS_R_A6 10 /* Arg (n64) */
++#define MIPS_R_A7 11 /* Arg (n64) */
++#define MIPS_R_T0 8 /* Temp (o32) */
++#define MIPS_R_T1 9 /* Temp (o32) */
++#define MIPS_R_T2 10 /* Temp (o32) */
++#define MIPS_R_T3 11 /* Temp (o32) */
++#define MIPS_R_T4 12 /* Temporary */
++#define MIPS_R_T5 13 /* Temporary */
++#define MIPS_R_T6 14 /* Temporary */
++#define MIPS_R_T7 15 /* Temporary */
++#define MIPS_R_S0 16 /* Saved */
++#define MIPS_R_S1 17 /* Saved */
++#define MIPS_R_S2 18 /* Saved */
++#define MIPS_R_S3 19 /* Saved */
++#define MIPS_R_S4 20 /* Saved */
++#define MIPS_R_S5 21 /* Saved */
++#define MIPS_R_S6 22 /* Saved */
++#define MIPS_R_S7 23 /* Saved */
++#define MIPS_R_T8 24 /* Temporary */
++#define MIPS_R_T9 25 /* Temporary */
++/* MIPS_R_K0 26 Reserved */
++/* MIPS_R_K1 27 Reserved */
++#define MIPS_R_GP 28 /* Global ptr */
++#define MIPS_R_SP 29 /* Stack ptr */
++#define MIPS_R_FP 30 /* Frame ptr */
++#define MIPS_R_RA 31 /* Return */
++
++/*
++ * Jump address mask for immediate jumps. The four most significant bits
++ * must be equal to PC.
++ */
++#define MIPS_JMP_MASK 0x0fffffffUL
++
++/* Maximum number of iterations in offset table computation */
++#define JIT_MAX_ITERATIONS 8
++
++/*
++ * Jump pseudo-instructions used internally
++ * for branch conversion and branch optimization.
++ */
++#define JIT_JNSET 0xe0
++#define JIT_JNOP 0xf0
++
++/* Descriptor flag for PC-relative branch conversion */
++#define JIT_DESC_CONVERT BIT(31)
++
++/* JIT context for an eBPF program */
++struct jit_context {
++ struct bpf_prog *program; /* The eBPF program being JITed */
++ u32 *descriptors; /* eBPF to JITed CPU insn descriptors */
++ u32 *target; /* JITed code buffer */
++ u32 bpf_index; /* Index of current BPF program insn */
++ u32 jit_index; /* Index of current JIT target insn */
++ u32 changes; /* Number of PC-relative branch conv */
++ u32 accessed; /* Bit mask of read eBPF registers */
++ u32 clobbered; /* Bit mask of modified CPU registers */
++ u32 stack_size; /* Total allocated stack size in bytes */
++ u32 saved_size; /* Size of callee-saved registers */
++ u32 stack_used; /* Stack size used for function calls */
++};
++
++/* Emit the instruction if the JIT memory space has been allocated */
++#define emit(ctx, func, ...) \
++do { \
++ if ((ctx)->target != NULL) { \
++ u32 *p = &(ctx)->target[ctx->jit_index]; \
++ uasm_i_##func(&p, ##__VA_ARGS__); \
++ } \
++ (ctx)->jit_index++; \
++} while (0)
++
++/*
++ * Mark a BPF register as accessed, it needs to be
++ * initialized by the program if expected, e.g. FP.
++ */
++static inline void access_reg(struct jit_context *ctx, u8 reg)
++{
++ ctx->accessed |= BIT(reg);
++}
++
++/*
++ * Mark a CPU register as clobbered, it needs to be
++ * saved/restored by the program if callee-saved.
++ */
++static inline void clobber_reg(struct jit_context *ctx, u8 reg)
++{
++ ctx->clobbered |= BIT(reg);
++}
++
++/*
++ * Push registers on the stack, starting at a given depth from the stack
++ * pointer and increasing. The next depth to be written is returned.
++ */
++int push_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth);
++
++/*
++ * Pop registers from the stack, starting at a given depth from the stack
++ * pointer and increasing. The next depth to be read is returned.
++ */
++int pop_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth);
++
++/* Compute the 28-bit jump target address from a BPF program location */
++int get_target(struct jit_context *ctx, u32 loc);
++
++/* Compute the PC-relative offset to relative BPF program offset */
++int get_offset(const struct jit_context *ctx, int off);
++
++/* dst = imm (32-bit) */
++void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm);
++
++/* dst = src (32-bit) */
++void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src);
++
++/* Validate ALU/ALU64 immediate range */
++bool valid_alu_i(u8 op, s32 imm);
++
++/* Rewrite ALU/ALU64 immediate operation */
++bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val);
++
++/* ALU immediate operation (32-bit) */
++void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op);
++
++/* ALU register operation (32-bit) */
++void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op);
++
++/* Atomic read-modify-write (32-bit) */
++void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code);
++
++/* Atomic compare-and-exchange (32-bit) */
++void emit_cmpxchg_r(struct jit_context *ctx, u8 dst, u8 src, u8 res, s16 off);
++
++/* Swap bytes and truncate a register word or half word */
++void emit_bswap_r(struct jit_context *ctx, u8 dst, u32 width);
++
++/* Validate JMP/JMP32 immediate range */
++bool valid_jmp_i(u8 op, s32 imm);
++
++/* Prepare a PC-relative jump operation with immediate conditional */
++void setup_jmp_i(struct jit_context *ctx, s32 imm, u8 width,
++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off);
++
++/* Prepare a PC-relative jump operation with register conditional */
++void setup_jmp_r(struct jit_context *ctx, bool same_reg,
++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off);
++
++/* Finish a PC-relative jump operation */
++int finish_jmp(struct jit_context *ctx, u8 jit_op, s16 bpf_off);
++
++/* Conditional JMP/JMP32 immediate */
++void emit_jmp_i(struct jit_context *ctx, u8 dst, s32 imm, s32 off, u8 op);
++
++/* Conditional JMP/JMP32 register */
++void emit_jmp_r(struct jit_context *ctx, u8 dst, u8 src, s32 off, u8 op);
++
++/* Jump always */
++int emit_ja(struct jit_context *ctx, s16 off);
++
++/* Jump to epilogue */
++int emit_exit(struct jit_context *ctx);
++
++/*
++ * Build program prologue to set up the stack and registers.
++ * This function is implemented separately for 32-bit and 64-bit JITs.
++ */
++void build_prologue(struct jit_context *ctx);
++
++/*
++ * Build the program epilogue to restore the stack and registers.
++ * This function is implemented separately for 32-bit and 64-bit JITs.
++ */
++void build_epilogue(struct jit_context *ctx, int dest_reg);
++
++/*
++ * Convert an eBPF instruction to native instruction, i.e
++ * JITs an eBPF instruction.
++ * Returns :
++ * 0 - Successfully JITed an 8-byte eBPF instruction
++ * >0 - Successfully JITed a 16-byte eBPF instruction
++ * <0 - Failed to JIT.
++ * This function is implemented separately for 32-bit and 64-bit JITs.
++ */
++int build_insn(const struct bpf_insn *insn, struct jit_context *ctx);
++
++#endif /* _BPF_JIT_COMP_H */
+--- /dev/null
++++ b/arch/mips/net/bpf_jit_comp32.c
+@@ -0,0 +1,1741 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Just-In-Time compiler for eBPF bytecode on MIPS.
++ * Implementation of JIT functions for 32-bit CPUs.
++ *
++ * Copyright (c) 2021 Anyfi Networks AB.
++ * Author: Johan Almbladh <johan.almbladh@gmail.com>
++ *
++ * Based on code and ideas from
++ * Copyright (c) 2017 Cavium, Inc.
++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
++ */
++
++#include <linux/math64.h>
++#include <linux/errno.h>
++#include <linux/filter.h>
++#include <linux/bpf.h>
++#include <asm/cpu-features.h>
++#include <asm/isa-rev.h>
++#include <asm/uasm.h>
++
++#include "bpf_jit_comp.h"
++
++/* MIPS a4-a7 are not available in the o32 ABI */
++#undef MIPS_R_A4
++#undef MIPS_R_A5
++#undef MIPS_R_A6
++#undef MIPS_R_A7
++
++/* Stack is 8-byte aligned in o32 ABI */
++#define MIPS_STACK_ALIGNMENT 8
++
++/*
++ * The top 16 bytes of a stack frame is reserved for the callee in O32 ABI.
++ * This corresponds to stack space for register arguments a0-a3.
++ */
++#define JIT_RESERVED_STACK 16
++
++/* Temporary 64-bit register used by JIT */
++#define JIT_REG_TMP MAX_BPF_JIT_REG
++
++/*
++ * Number of prologue bytes to skip when doing a tail call.
++ * Tail call count (TCC) initialization (8 bytes) always, plus
++ * R0-to-v0 assignment (4 bytes) if big endian.
++ */
++#ifdef __BIG_ENDIAN
++#define JIT_TCALL_SKIP 12
++#else
++#define JIT_TCALL_SKIP 8
++#endif
++
++/* CPU registers holding the callee return value */
++#define JIT_RETURN_REGS \
++ (BIT(MIPS_R_V0) | \
++ BIT(MIPS_R_V1))
++
++/* CPU registers arguments passed to callee directly */
++#define JIT_ARG_REGS \
++ (BIT(MIPS_R_A0) | \
++ BIT(MIPS_R_A1) | \
++ BIT(MIPS_R_A2) | \
++ BIT(MIPS_R_A3))
++
++/* CPU register arguments passed to callee on stack */
++#define JIT_STACK_REGS \
++ (BIT(MIPS_R_T0) | \
++ BIT(MIPS_R_T1) | \
++ BIT(MIPS_R_T2) | \
++ BIT(MIPS_R_T3) | \
++ BIT(MIPS_R_T4) | \
++ BIT(MIPS_R_T5))
++
++/* Caller-saved CPU registers */
++#define JIT_CALLER_REGS \
++ (JIT_RETURN_REGS | \
++ JIT_ARG_REGS | \
++ JIT_STACK_REGS)
++
++/* Callee-saved CPU registers */
++#define JIT_CALLEE_REGS \
++ (BIT(MIPS_R_S0) | \
++ BIT(MIPS_R_S1) | \
++ BIT(MIPS_R_S2) | \
++ BIT(MIPS_R_S3) | \
++ BIT(MIPS_R_S4) | \
++ BIT(MIPS_R_S5) | \
++ BIT(MIPS_R_S6) | \
++ BIT(MIPS_R_S7) | \
++ BIT(MIPS_R_GP) | \
++ BIT(MIPS_R_FP) | \
++ BIT(MIPS_R_RA))
++
++/*
++ * Mapping of 64-bit eBPF registers to 32-bit native MIPS registers.
++ *
++ * 1) Native register pairs are ordered according to CPU endiannes, following
++ * the MIPS convention for passing 64-bit arguments and return values.
++ * 2) The eBPF return value, arguments and callee-saved registers are mapped
++ * to their native MIPS equivalents.
++ * 3) Since the 32 highest bits in the eBPF FP register are always zero,
++ * only one general-purpose register is actually needed for the mapping.
++ * We use the fp register for this purpose, and map the highest bits to
++ * the MIPS register r0 (zero).
++ * 4) We use the MIPS gp and at registers as internal temporary registers
++ * for constant blinding. The gp register is callee-saved.
++ * 5) One 64-bit temporary register is mapped for use when sign-extending
++ * immediate operands. MIPS registers t6-t9 are available to the JIT
++ * for as temporaries when implementing complex 64-bit operations.
++ *
++ * With this scheme all eBPF registers are being mapped to native MIPS
++ * registers without having to use any stack scratch space. The direct
++ * register mapping (2) simplifies the handling of function calls.
++ */
++static const u8 bpf2mips32[][2] = {
++ /* Return value from in-kernel function, and exit value from eBPF */
++ [BPF_REG_0] = {MIPS_R_V1, MIPS_R_V0},
++ /* Arguments from eBPF program to in-kernel function */
++ [BPF_REG_1] = {MIPS_R_A1, MIPS_R_A0},
++ [BPF_REG_2] = {MIPS_R_A3, MIPS_R_A2},
++ /* Remaining arguments, to be passed on the stack per O32 ABI */
++ [BPF_REG_3] = {MIPS_R_T1, MIPS_R_T0},
++ [BPF_REG_4] = {MIPS_R_T3, MIPS_R_T2},
++ [BPF_REG_5] = {MIPS_R_T5, MIPS_R_T4},
++ /* Callee-saved registers that in-kernel function will preserve */
++ [BPF_REG_6] = {MIPS_R_S1, MIPS_R_S0},
++ [BPF_REG_7] = {MIPS_R_S3, MIPS_R_S2},
++ [BPF_REG_8] = {MIPS_R_S5, MIPS_R_S4},
++ [BPF_REG_9] = {MIPS_R_S7, MIPS_R_S6},
++ /* Read-only frame pointer to access the eBPF stack */
++#ifdef __BIG_ENDIAN
++ [BPF_REG_FP] = {MIPS_R_FP, MIPS_R_ZERO},
++#else
++ [BPF_REG_FP] = {MIPS_R_ZERO, MIPS_R_FP},
++#endif
++ /* Temporary register for blinding constants */
++ [BPF_REG_AX] = {MIPS_R_GP, MIPS_R_AT},
++ /* Temporary register for internal JIT use */
++ [JIT_REG_TMP] = {MIPS_R_T7, MIPS_R_T6},
++};
++
++/* Get low CPU register for a 64-bit eBPF register mapping */
++static inline u8 lo(const u8 reg[])
++{
++#ifdef __BIG_ENDIAN
++ return reg[0];
++#else
++ return reg[1];
++#endif
++}
++
++/* Get high CPU register for a 64-bit eBPF register mapping */
++static inline u8 hi(const u8 reg[])
++{
++#ifdef __BIG_ENDIAN
++ return reg[1];
++#else
++ return reg[0];
++#endif
++}
++
++/*
++ * Mark a 64-bit CPU register pair as clobbered, it needs to be
++ * saved/restored by the program if callee-saved.
++ */
++static void clobber_reg64(struct jit_context *ctx, const u8 reg[])
++{
++ clobber_reg(ctx, reg[0]);
++ clobber_reg(ctx, reg[1]);
++}
++
++/* dst = imm (sign-extended) */
++static void emit_mov_se_i64(struct jit_context *ctx, const u8 dst[], s32 imm)
++{
++ emit_mov_i(ctx, lo(dst), imm);
++ if (imm < 0)
++ emit(ctx, addiu, hi(dst), MIPS_R_ZERO, -1);
++ else
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ clobber_reg64(ctx, dst);
++}
++
++/* Zero extension, if verifier does not do it for us */
++static void emit_zext_ver(struct jit_context *ctx, const u8 dst[])
++{
++ if (!ctx->program->aux->verifier_zext) {
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ clobber_reg(ctx, hi(dst));
++ }
++}
++
++/* Load delay slot, if ISA mandates it */
++static void emit_load_delay(struct jit_context *ctx)
++{
++ if (!cpu_has_mips_2_3_4_5_r)
++ emit(ctx, nop);
++}
++
++/* ALU immediate operation (64-bit) */
++static void emit_alu_i64(struct jit_context *ctx,
++ const u8 dst[], s32 imm, u8 op)
++{
++ u8 src = MIPS_R_T6;
++
++ /*
++ * ADD/SUB with all but the max negative imm can be handled by
++ * inverting the operation and the imm value, saving one insn.
++ */
++ if (imm > S32_MIN && imm < 0)
++ switch (op) {
++ case BPF_ADD:
++ op = BPF_SUB;
++ imm = -imm;
++ break;
++ case BPF_SUB:
++ op = BPF_ADD;
++ imm = -imm;
++ break;
++ }
++
++ /* Move immediate to temporary register */
++ emit_mov_i(ctx, src, imm);
++
++ switch (op) {
++ /* dst = dst + imm */
++ case BPF_ADD:
++ emit(ctx, addu, lo(dst), lo(dst), src);
++ emit(ctx, sltu, MIPS_R_T9, lo(dst), src);
++ emit(ctx, addu, hi(dst), hi(dst), MIPS_R_T9);
++ if (imm < 0)
++ emit(ctx, addiu, hi(dst), hi(dst), -1);
++ break;
++ /* dst = dst - imm */
++ case BPF_SUB:
++ emit(ctx, sltu, MIPS_R_T9, lo(dst), src);
++ emit(ctx, subu, lo(dst), lo(dst), src);
++ emit(ctx, subu, hi(dst), hi(dst), MIPS_R_T9);
++ if (imm < 0)
++ emit(ctx, addiu, hi(dst), hi(dst), 1);
++ break;
++ /* dst = dst | imm */
++ case BPF_OR:
++ emit(ctx, or, lo(dst), lo(dst), src);
++ if (imm < 0)
++ emit(ctx, addiu, hi(dst), MIPS_R_ZERO, -1);
++ break;
++ /* dst = dst & imm */
++ case BPF_AND:
++ emit(ctx, and, lo(dst), lo(dst), src);
++ if (imm >= 0)
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ /* dst = dst ^ imm */
++ case BPF_XOR:
++ emit(ctx, xor, lo(dst), lo(dst), src);
++ if (imm < 0) {
++ emit(ctx, subu, hi(dst), MIPS_R_ZERO, hi(dst));
++ emit(ctx, addiu, hi(dst), hi(dst), -1);
++ }
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU register operation (64-bit) */
++static void emit_alu_r64(struct jit_context *ctx,
++ const u8 dst[], const u8 src[], u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst + src */
++ case BPF_ADD:
++ if (src == dst) {
++ emit(ctx, srl, MIPS_R_T9, lo(dst), 31);
++ emit(ctx, addu, lo(dst), lo(dst), lo(dst));
++ } else {
++ emit(ctx, addu, lo(dst), lo(dst), lo(src));
++ emit(ctx, sltu, MIPS_R_T9, lo(dst), lo(src));
++ }
++ emit(ctx, addu, hi(dst), hi(dst), hi(src));
++ emit(ctx, addu, hi(dst), hi(dst), MIPS_R_T9);
++ break;
++ /* dst = dst - src */
++ case BPF_SUB:
++ emit(ctx, sltu, MIPS_R_T9, lo(dst), lo(src));
++ emit(ctx, subu, lo(dst), lo(dst), lo(src));
++ emit(ctx, subu, hi(dst), hi(dst), hi(src));
++ emit(ctx, subu, hi(dst), hi(dst), MIPS_R_T9);
++ break;
++ /* dst = dst | src */
++ case BPF_OR:
++ emit(ctx, or, lo(dst), lo(dst), lo(src));
++ emit(ctx, or, hi(dst), hi(dst), hi(src));
++ break;
++ /* dst = dst & src */
++ case BPF_AND:
++ emit(ctx, and, lo(dst), lo(dst), lo(src));
++ emit(ctx, and, hi(dst), hi(dst), hi(src));
++ break;
++ /* dst = dst ^ src */
++ case BPF_XOR:
++ emit(ctx, xor, lo(dst), lo(dst), lo(src));
++ emit(ctx, xor, hi(dst), hi(dst), hi(src));
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU invert (64-bit) */
++static void emit_neg_i64(struct jit_context *ctx, const u8 dst[])
++{
++ emit(ctx, sltu, MIPS_R_T9, MIPS_R_ZERO, lo(dst));
++ emit(ctx, subu, lo(dst), MIPS_R_ZERO, lo(dst));
++ emit(ctx, subu, hi(dst), MIPS_R_ZERO, hi(dst));
++ emit(ctx, subu, hi(dst), hi(dst), MIPS_R_T9);
++
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU shift immediate (64-bit) */
++static void emit_shift_i64(struct jit_context *ctx,
++ const u8 dst[], u32 imm, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst << imm */
++ case BPF_LSH:
++ if (imm < 32) {
++ emit(ctx, srl, MIPS_R_T9, lo(dst), 32 - imm);
++ emit(ctx, sll, lo(dst), lo(dst), imm);
++ emit(ctx, sll, hi(dst), hi(dst), imm);
++ emit(ctx, or, hi(dst), hi(dst), MIPS_R_T9);
++ } else {
++ emit(ctx, sll, hi(dst), lo(dst), imm - 32);
++ emit(ctx, move, lo(dst), MIPS_R_ZERO);
++ }
++ break;
++ /* dst = dst >> imm */
++ case BPF_RSH:
++ if (imm < 32) {
++ emit(ctx, sll, MIPS_R_T9, hi(dst), 32 - imm);
++ emit(ctx, srl, lo(dst), lo(dst), imm);
++ emit(ctx, srl, hi(dst), hi(dst), imm);
++ emit(ctx, or, lo(dst), lo(dst), MIPS_R_T9);
++ } else {
++ emit(ctx, srl, lo(dst), hi(dst), imm - 32);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ }
++ break;
++ /* dst = dst >> imm (arithmetic) */
++ case BPF_ARSH:
++ if (imm < 32) {
++ emit(ctx, sll, MIPS_R_T9, hi(dst), 32 - imm);
++ emit(ctx, srl, lo(dst), lo(dst), imm);
++ emit(ctx, sra, hi(dst), hi(dst), imm);
++ emit(ctx, or, lo(dst), lo(dst), MIPS_R_T9);
++ } else {
++ emit(ctx, sra, lo(dst), hi(dst), imm - 32);
++ emit(ctx, sra, hi(dst), hi(dst), 31);
++ }
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU shift register (64-bit) */
++static void emit_shift_r64(struct jit_context *ctx,
++ const u8 dst[], u8 src, u8 op)
++{
++ u8 t1 = MIPS_R_T8;
++ u8 t2 = MIPS_R_T9;
++
++ emit(ctx, andi, t1, src, 32); /* t1 = src & 32 */
++ emit(ctx, beqz, t1, 16); /* PC += 16 if t1 == 0 */
++ emit(ctx, nor, t2, src, MIPS_R_ZERO); /* t2 = ~src (delay slot) */
++
++ switch (BPF_OP(op)) {
++ /* dst = dst << src */
++ case BPF_LSH:
++ /* Next: shift >= 32 */
++ emit(ctx, sllv, hi(dst), lo(dst), src); /* dh = dl << src */
++ emit(ctx, move, lo(dst), MIPS_R_ZERO); /* dl = 0 */
++ emit(ctx, b, 20); /* PC += 20 */
++ /* +16: shift < 32 */
++ emit(ctx, srl, t1, lo(dst), 1); /* t1 = dl >> 1 */
++ emit(ctx, srlv, t1, t1, t2); /* t1 = t1 >> t2 */
++ emit(ctx, sllv, lo(dst), lo(dst), src); /* dl = dl << src */
++ emit(ctx, sllv, hi(dst), hi(dst), src); /* dh = dh << src */
++ emit(ctx, or, hi(dst), hi(dst), t1); /* dh = dh | t1 */
++ break;
++ /* dst = dst >> src */
++ case BPF_RSH:
++ /* Next: shift >= 32 */
++ emit(ctx, srlv, lo(dst), hi(dst), src); /* dl = dh >> src */
++ emit(ctx, move, hi(dst), MIPS_R_ZERO); /* dh = 0 */
++ emit(ctx, b, 20); /* PC += 20 */
++ /* +16: shift < 32 */
++ emit(ctx, sll, t1, hi(dst), 1); /* t1 = dl << 1 */
++ emit(ctx, sllv, t1, t1, t2); /* t1 = t1 << t2 */
++ emit(ctx, srlv, lo(dst), lo(dst), src); /* dl = dl >> src */
++ emit(ctx, srlv, hi(dst), hi(dst), src); /* dh = dh >> src */
++ emit(ctx, or, lo(dst), lo(dst), t1); /* dl = dl | t1 */
++ break;
++ /* dst = dst >> src (arithmetic) */
++ case BPF_ARSH:
++ /* Next: shift >= 32 */
++ emit(ctx, srav, lo(dst), hi(dst), src); /* dl = dh >>a src */
++ emit(ctx, sra, hi(dst), hi(dst), 31); /* dh = dh >>a 31 */
++ emit(ctx, b, 20); /* PC += 20 */
++ /* +16: shift < 32 */
++ emit(ctx, sll, t1, hi(dst), 1); /* t1 = dl << 1 */
++ emit(ctx, sllv, t1, t1, t2); /* t1 = t1 << t2 */
++ emit(ctx, srlv, lo(dst), lo(dst), src); /* dl = dl >>a src */
++ emit(ctx, srav, hi(dst), hi(dst), src); /* dh = dh >> src */
++ emit(ctx, or, lo(dst), lo(dst), t1); /* dl = dl | t1 */
++ break;
++ }
++
++ /* +20: Done */
++ clobber_reg64(ctx, dst);
++}
++
++/* ALU mul immediate (64x32-bit) */
++static void emit_mul_i64(struct jit_context *ctx, const u8 dst[], s32 imm)
++{
++ u8 src = MIPS_R_T6;
++ u8 tmp = MIPS_R_T9;
++
++ switch (imm) {
++ /* dst = dst * 1 is a no-op */
++ case 1:
++ break;
++ /* dst = dst * -1 */
++ case -1:
++ emit_neg_i64(ctx, dst);
++ break;
++ case 0:
++ emit_mov_r(ctx, lo(dst), MIPS_R_ZERO);
++ emit_mov_r(ctx, hi(dst), MIPS_R_ZERO);
++ break;
++ /* Full 64x32 multiply */
++ default:
++ /* hi(dst) = hi(dst) * src(imm) */
++ emit_mov_i(ctx, src, imm);
++ if (cpu_has_mips32r1 || cpu_has_mips32r6) {
++ emit(ctx, mul, hi(dst), hi(dst), src);
++ } else {
++ emit(ctx, multu, hi(dst), src);
++ emit(ctx, mflo, hi(dst));
++ }
++
++ /* hi(dst) = hi(dst) - lo(dst) */
++ if (imm < 0)
++ emit(ctx, subu, hi(dst), hi(dst), lo(dst));
++
++ /* tmp = lo(dst) * src(imm) >> 32 */
++ /* lo(dst) = lo(dst) * src(imm) */
++ if (cpu_has_mips32r6) {
++ emit(ctx, muhu, tmp, lo(dst), src);
++ emit(ctx, mulu, lo(dst), lo(dst), src);
++ } else {
++ emit(ctx, multu, lo(dst), src);
++ emit(ctx, mflo, lo(dst));
++ emit(ctx, mfhi, tmp);
++ }
++
++ /* hi(dst) += tmp */
++ emit(ctx, addu, hi(dst), hi(dst), tmp);
++ clobber_reg64(ctx, dst);
++ break;
++ }
++}
++
++/* ALU mul register (64x64-bit) */
++static void emit_mul_r64(struct jit_context *ctx,
++ const u8 dst[], const u8 src[])
++{
++ u8 acc = MIPS_R_T8;
++ u8 tmp = MIPS_R_T9;
++
++ /* acc = hi(dst) * lo(src) */
++ if (cpu_has_mips32r1 || cpu_has_mips32r6) {
++ emit(ctx, mul, acc, hi(dst), lo(src));
++ } else {
++ emit(ctx, multu, hi(dst), lo(src));
++ emit(ctx, mflo, acc);
++ }
++
++ /* tmp = lo(dst) * hi(src) */
++ if (cpu_has_mips32r1 || cpu_has_mips32r6) {
++ emit(ctx, mul, tmp, lo(dst), hi(src));
++ } else {
++ emit(ctx, multu, lo(dst), hi(src));
++ emit(ctx, mflo, tmp);
++ }
++
++ /* acc += tmp */
++ emit(ctx, addu, acc, acc, tmp);
++
++ /* tmp = lo(dst) * lo(src) >> 32 */
++ /* lo(dst) = lo(dst) * lo(src) */
++ if (cpu_has_mips32r6) {
++ emit(ctx, muhu, tmp, lo(dst), lo(src));
++ emit(ctx, mulu, lo(dst), lo(dst), lo(src));
++ } else {
++ emit(ctx, multu, lo(dst), lo(src));
++ emit(ctx, mflo, lo(dst));
++ emit(ctx, mfhi, tmp);
++ }
++
++ /* hi(dst) = acc + tmp */
++ emit(ctx, addu, hi(dst), acc, tmp);
++ clobber_reg64(ctx, dst);
++}
++
++/* Helper function for 64-bit modulo */
++static u64 jit_mod64(u64 a, u64 b)
++{
++ u64 rem;
++
++ div64_u64_rem(a, b, &rem);
++ return rem;
++}
++
++/* ALU div/mod register (64-bit) */
++static void emit_divmod_r64(struct jit_context *ctx,
++ const u8 dst[], const u8 src[], u8 op)
++{
++ const u8 *r0 = bpf2mips32[BPF_REG_0]; /* Mapped to v0-v1 */
++ const u8 *r1 = bpf2mips32[BPF_REG_1]; /* Mapped to a0-a1 */
++ const u8 *r2 = bpf2mips32[BPF_REG_2]; /* Mapped to a2-a3 */
++ int exclude, k;
++ u32 addr = 0;
++
++ /* Push caller-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ 0, JIT_RESERVED_STACK);
++
++ /* Put 64-bit arguments 1 and 2 in registers a0-a3 */
++ for (k = 0; k < 2; k++) {
++ emit(ctx, move, MIPS_R_T9, src[k]);
++ emit(ctx, move, r1[k], dst[k]);
++ emit(ctx, move, r2[k], MIPS_R_T9);
++ }
++
++ /* Emit function call */
++ switch (BPF_OP(op)) {
++ /* dst = dst / src */
++ case BPF_DIV:
++ addr = (u32)&div64_u64;
++ break;
++ /* dst = dst % src */
++ case BPF_MOD:
++ addr = (u32)&jit_mod64;
++ break;
++ }
++ emit_mov_i(ctx, MIPS_R_T9, addr);
++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9);
++ emit(ctx, nop); /* Delay slot */
++
++ /* Store the 64-bit result in dst */
++ emit(ctx, move, dst[0], r0[0]);
++ emit(ctx, move, dst[1], r0[1]);
++
++ /* Restore caller-saved registers, excluding the computed result */
++ exclude = BIT(lo(dst)) | BIT(hi(dst));
++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ exclude, JIT_RESERVED_STACK);
++ emit_load_delay(ctx);
++
++ clobber_reg64(ctx, dst);
++ clobber_reg(ctx, MIPS_R_V0);
++ clobber_reg(ctx, MIPS_R_V1);
++ clobber_reg(ctx, MIPS_R_RA);
++}
++
++/* Swap bytes in a register word */
++static void emit_swap8_r(struct jit_context *ctx, u8 dst, u8 src, u8 mask)
++{
++ u8 tmp = MIPS_R_T9;
++
++ emit(ctx, and, tmp, src, mask); /* tmp = src & 0x00ff00ff */
++ emit(ctx, sll, tmp, tmp, 8); /* tmp = tmp << 8 */
++ emit(ctx, srl, dst, src, 8); /* dst = src >> 8 */
++ emit(ctx, and, dst, dst, mask); /* dst = dst & 0x00ff00ff */
++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */
++}
++
++/* Swap half words in a register word */
++static void emit_swap16_r(struct jit_context *ctx, u8 dst, u8 src)
++{
++ u8 tmp = MIPS_R_T9;
++
++ emit(ctx, sll, tmp, src, 16); /* tmp = src << 16 */
++ emit(ctx, srl, dst, src, 16); /* dst = src >> 16 */
++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */
++}
++
++/* Swap bytes and truncate a register double word, word or half word */
++static void emit_bswap_r64(struct jit_context *ctx, const u8 dst[], u32 width)
++{
++ u8 tmp = MIPS_R_T8;
++
++ switch (width) {
++ /* Swap bytes in a double word */
++ case 64:
++ if (cpu_has_mips32r2 || cpu_has_mips32r6) {
++ emit(ctx, rotr, tmp, hi(dst), 16);
++ emit(ctx, rotr, hi(dst), lo(dst), 16);
++ emit(ctx, wsbh, lo(dst), tmp);
++ emit(ctx, wsbh, hi(dst), hi(dst));
++ } else {
++ emit_swap16_r(ctx, tmp, lo(dst));
++ emit_swap16_r(ctx, lo(dst), hi(dst));
++ emit(ctx, move, hi(dst), tmp);
++
++ emit(ctx, lui, tmp, 0xff); /* tmp = 0x00ff0000 */
++ emit(ctx, ori, tmp, tmp, 0xff); /* tmp = 0x00ff00ff */
++ emit_swap8_r(ctx, lo(dst), lo(dst), tmp);
++ emit_swap8_r(ctx, hi(dst), hi(dst), tmp);
++ }
++ break;
++ /* Swap bytes in a word */
++ /* Swap bytes in a half word */
++ case 32:
++ case 16:
++ emit_bswap_r(ctx, lo(dst), width);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* Truncate a register double word, word or half word */
++static void emit_trunc_r64(struct jit_context *ctx, const u8 dst[], u32 width)
++{
++ switch (width) {
++ case 64:
++ break;
++ /* Zero-extend a word */
++ case 32:
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ clobber_reg(ctx, hi(dst));
++ break;
++ /* Zero-extend a half word */
++ case 16:
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ emit(ctx, andi, lo(dst), lo(dst), 0xffff);
++ clobber_reg64(ctx, dst);
++ break;
++ }
++}
++
++/* Load operation: dst = *(size*)(src + off) */
++static void emit_ldx(struct jit_context *ctx,
++ const u8 dst[], u8 src, s16 off, u8 size)
++{
++ switch (size) {
++ /* Load a byte */
++ case BPF_B:
++ emit(ctx, lbu, lo(dst), off, src);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ /* Load a half word */
++ case BPF_H:
++ emit(ctx, lhu, lo(dst), off, src);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ /* Load a word */
++ case BPF_W:
++ emit(ctx, lw, lo(dst), off, src);
++ emit(ctx, move, hi(dst), MIPS_R_ZERO);
++ break;
++ /* Load a double word */
++ case BPF_DW:
++ if (dst[1] == src) {
++ emit(ctx, lw, dst[0], off + 4, src);
++ emit(ctx, lw, dst[1], off, src);
++ } else {
++ emit(ctx, lw, dst[1], off, src);
++ emit(ctx, lw, dst[0], off + 4, src);
++ }
++ emit_load_delay(ctx);
++ break;
++ }
++ clobber_reg64(ctx, dst);
++}
++
++/* Store operation: *(size *)(dst + off) = src */
++static void emit_stx(struct jit_context *ctx,
++ const u8 dst, const u8 src[], s16 off, u8 size)
++{
++ switch (size) {
++ /* Store a byte */
++ case BPF_B:
++ emit(ctx, sb, lo(src), off, dst);
++ break;
++ /* Store a half word */
++ case BPF_H:
++ emit(ctx, sh, lo(src), off, dst);
++ break;
++ /* Store a word */
++ case BPF_W:
++ emit(ctx, sw, lo(src), off, dst);
++ break;
++ /* Store a double word */
++ case BPF_DW:
++ emit(ctx, sw, src[1], off, dst);
++ emit(ctx, sw, src[0], off + 4, dst);
++ break;
++ }
++}
++
++/* Atomic read-modify-write (32-bit, non-ll/sc fallback) */
++static void emit_atomic_r32(struct jit_context *ctx,
++ u8 dst, u8 src, s16 off, u8 code)
++{
++ u32 exclude = 0;
++ u32 addr = 0;
++
++ /* Push caller-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ 0, JIT_RESERVED_STACK);
++ /*
++ * Argument 1: dst+off if xchg, otherwise src, passed in register a0
++ * Argument 2: src if xchg, othersize dst+off, passed in register a1
++ */
++ emit(ctx, move, MIPS_R_T9, dst);
++ emit(ctx, move, MIPS_R_A0, src);
++ emit(ctx, addiu, MIPS_R_A1, MIPS_R_T9, off);
++
++ /* Emit function call */
++ switch (code) {
++ case BPF_ADD:
++ addr = (u32)&atomic_add;
++ break;
++ case BPF_SUB:
++ addr = (u32)&atomic_sub;
++ break;
++ case BPF_OR:
++ addr = (u32)&atomic_or;
++ break;
++ case BPF_AND:
++ addr = (u32)&atomic_and;
++ break;
++ case BPF_XOR:
++ addr = (u32)&atomic_xor;
++ break;
++ }
++ emit_mov_i(ctx, MIPS_R_T9, addr);
++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9);
++ emit(ctx, nop); /* Delay slot */
++
++ /* Restore caller-saved registers, except any fetched value */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ exclude, JIT_RESERVED_STACK);
++ emit_load_delay(ctx);
++ clobber_reg(ctx, MIPS_R_RA);
++}
++
++/* Atomic read-modify-write (64-bit) */
++static void emit_atomic_r64(struct jit_context *ctx,
++ u8 dst, const u8 src[], s16 off, u8 code)
++{
++ const u8 *r1 = bpf2mips32[BPF_REG_1]; /* Mapped to a0-a1 */
++ u32 exclude = 0;
++ u32 addr = 0;
++
++ /* Push caller-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ 0, JIT_RESERVED_STACK);
++ /*
++ * Argument 1: 64-bit src, passed in registers a0-a1
++ * Argument 2: 32-bit dst+off, passed in register a2
++ */
++ emit(ctx, move, MIPS_R_T9, dst);
++ emit(ctx, move, r1[0], src[0]);
++ emit(ctx, move, r1[1], src[1]);
++ emit(ctx, addiu, MIPS_R_A2, MIPS_R_T9, off);
++
++ /* Emit function call */
++ switch (code) {
++ case BPF_ADD:
++ addr = (u32)&atomic64_add;
++ break;
++ case BPF_SUB:
++ addr = (u32)&atomic64_sub;
++ break;
++ case BPF_OR:
++ addr = (u32)&atomic64_or;
++ break;
++ case BPF_AND:
++ addr = (u32)&atomic64_and;
++ break;
++ case BPF_XOR:
++ addr = (u32)&atomic64_xor;
++ break;
++ }
++ emit_mov_i(ctx, MIPS_R_T9, addr);
++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9);
++ emit(ctx, nop); /* Delay slot */
++
++ /* Restore caller-saved registers, except any fetched value */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS,
++ exclude, JIT_RESERVED_STACK);
++ emit_load_delay(ctx);
++ clobber_reg(ctx, MIPS_R_RA);
++}
++
++/*
++ * Conditional movz or an emulated equivalent.
++ * Note that the rs register may be modified.
++ */
++static void emit_movz_r(struct jit_context *ctx, u8 rd, u8 rs, u8 rt)
++{
++ if (cpu_has_mips_2) {
++ emit(ctx, movz, rd, rs, rt); /* rd = rt ? rd : rs */
++ } else if (cpu_has_mips32r6) {
++ if (rs != MIPS_R_ZERO)
++ emit(ctx, seleqz, rs, rs, rt); /* rs = 0 if rt == 0 */
++ emit(ctx, selnez, rd, rd, rt); /* rd = 0 if rt != 0 */
++ if (rs != MIPS_R_ZERO)
++ emit(ctx, or, rd, rd, rs); /* rd = rd | rs */
++ } else {
++ emit(ctx, bnez, rt, 8); /* PC += 8 if rd != 0 */
++ emit(ctx, nop); /* +0: delay slot */
++ emit(ctx, or, rd, rs, MIPS_R_ZERO); /* +4: rd = rs */
++ }
++ clobber_reg(ctx, rd);
++ clobber_reg(ctx, rs);
++}
++
++/*
++ * Conditional movn or an emulated equivalent.
++ * Note that the rs register may be modified.
++ */
++static void emit_movn_r(struct jit_context *ctx, u8 rd, u8 rs, u8 rt)
++{
++ if (cpu_has_mips_2) {
++ emit(ctx, movn, rd, rs, rt); /* rd = rt ? rs : rd */
++ } else if (cpu_has_mips32r6) {
++ if (rs != MIPS_R_ZERO)
++ emit(ctx, selnez, rs, rs, rt); /* rs = 0 if rt == 0 */
++ emit(ctx, seleqz, rd, rd, rt); /* rd = 0 if rt != 0 */
++ if (rs != MIPS_R_ZERO)
++ emit(ctx, or, rd, rd, rs); /* rd = rd | rs */
++ } else {
++ emit(ctx, beqz, rt, 8); /* PC += 8 if rd == 0 */
++ emit(ctx, nop); /* +0: delay slot */
++ emit(ctx, or, rd, rs, MIPS_R_ZERO); /* +4: rd = rs */
++ }
++ clobber_reg(ctx, rd);
++ clobber_reg(ctx, rs);
++}
++
++/* Emulation of 64-bit sltiu rd, rs, imm, where imm may be S32_MAX + 1 */
++static void emit_sltiu_r64(struct jit_context *ctx, u8 rd,
++ const u8 rs[], s64 imm)
++{
++ u8 tmp = MIPS_R_T9;
++
++ if (imm < 0) {
++ emit_mov_i(ctx, rd, imm); /* rd = imm */
++ emit(ctx, sltu, rd, lo(rs), rd); /* rd = rsl < rd */
++ emit(ctx, sltiu, tmp, hi(rs), -1); /* tmp = rsh < ~0U */
++ emit(ctx, or, rd, rd, tmp); /* rd = rd | tmp */
++ } else { /* imm >= 0 */
++ if (imm > 0x7fff) {
++ emit_mov_i(ctx, rd, (s32)imm); /* rd = imm */
++ emit(ctx, sltu, rd, lo(rs), rd); /* rd = rsl < rd */
++ } else {
++ emit(ctx, sltiu, rd, lo(rs), imm); /* rd = rsl < imm */
++ }
++ emit_movn_r(ctx, rd, MIPS_R_ZERO, hi(rs)); /* rd = 0 if rsh */
++ }
++}
++
++/* Emulation of 64-bit sltu rd, rs, rt */
++static void emit_sltu_r64(struct jit_context *ctx, u8 rd,
++ const u8 rs[], const u8 rt[])
++{
++ u8 tmp = MIPS_R_T9;
++
++ emit(ctx, sltu, rd, lo(rs), lo(rt)); /* rd = rsl < rtl */
++ emit(ctx, subu, tmp, hi(rs), hi(rt)); /* tmp = rsh - rth */
++ emit_movn_r(ctx, rd, MIPS_R_ZERO, tmp); /* rd = 0 if tmp != 0 */
++ emit(ctx, sltu, tmp, hi(rs), hi(rt)); /* tmp = rsh < rth */
++ emit(ctx, or, rd, rd, tmp); /* rd = rd | tmp */
++}
++
++/* Emulation of 64-bit slti rd, rs, imm, where imm may be S32_MAX + 1 */
++static void emit_slti_r64(struct jit_context *ctx, u8 rd,
++ const u8 rs[], s64 imm)
++{
++ u8 t1 = MIPS_R_T8;
++ u8 t2 = MIPS_R_T9;
++ u8 cmp;
++
++ /*
++ * if ((rs < 0) ^ (imm < 0)) t1 = imm >u rsl
++ * else t1 = rsl <u imm
++ */
++ emit_mov_i(ctx, rd, (s32)imm);
++ emit(ctx, sltu, t1, lo(rs), rd); /* t1 = rsl <u imm */
++ emit(ctx, sltu, t2, rd, lo(rs)); /* t2 = imm <u rsl */
++ emit(ctx, srl, rd, hi(rs), 31); /* rd = rsh >> 31 */
++ if (imm < 0)
++ emit_movz_r(ctx, t1, t2, rd); /* t1 = rd ? t1 : t2 */
++ else
++ emit_movn_r(ctx, t1, t2, rd); /* t1 = rd ? t2 : t1 */
++ /*
++ * if ((imm < 0 && rsh != 0xffffffff) ||
++ * (imm >= 0 && rsh != 0))
++ * t1 = 0
++ */
++ if (imm < 0) {
++ emit(ctx, addiu, rd, hi(rs), 1); /* rd = rsh + 1 */
++ cmp = rd;
++ } else { /* imm >= 0 */
++ cmp = hi(rs);
++ }
++ emit_movn_r(ctx, t1, MIPS_R_ZERO, cmp); /* t1 = 0 if cmp != 0 */
++
++ /*
++ * if (imm < 0) rd = rsh < -1
++ * else rd = rsh != 0
++ * rd = rd | t1
++ */
++ emit(ctx, slti, rd, hi(rs), imm < 0 ? -1 : 0); /* rd = rsh < hi(imm) */
++ emit(ctx, or, rd, rd, t1); /* rd = rd | t1 */
++}
++
++/* Emulation of 64-bit(slt rd, rs, rt) */
++static void emit_slt_r64(struct jit_context *ctx, u8 rd,
++ const u8 rs[], const u8 rt[])
++{
++ u8 t1 = MIPS_R_T7;
++ u8 t2 = MIPS_R_T8;
++ u8 t3 = MIPS_R_T9;
++
++ /*
++ * if ((rs < 0) ^ (rt < 0)) t1 = rtl <u rsl
++ * else t1 = rsl <u rtl
++ * if (rsh == rth) t1 = 0
++ */
++ emit(ctx, sltu, t1, lo(rs), lo(rt)); /* t1 = rsl <u rtl */
++ emit(ctx, sltu, t2, lo(rt), lo(rs)); /* t2 = rtl <u rsl */
++ emit(ctx, xor, t3, hi(rs), hi(rt)); /* t3 = rlh ^ rth */
++ emit(ctx, srl, rd, t3, 31); /* rd = t3 >> 31 */
++ emit_movn_r(ctx, t1, t2, rd); /* t1 = rd ? t2 : t1 */
++ emit_movn_r(ctx, t1, MIPS_R_ZERO, t3); /* t1 = 0 if t3 != 0 */
++
++ /* rd = (rsh < rth) | t1 */
++ emit(ctx, slt, rd, hi(rs), hi(rt)); /* rd = rsh <s rth */
++ emit(ctx, or, rd, rd, t1); /* rd = rd | t1 */
++}
++
++/* Jump immediate (64-bit) */
++static void emit_jmp_i64(struct jit_context *ctx,
++ const u8 dst[], s32 imm, s32 off, u8 op)
++{
++ u8 tmp = MIPS_R_T6;
++
++ switch (op) {
++ /* No-op, used internally for branch optimization */
++ case JIT_JNOP:
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ case BPF_JEQ:
++ case BPF_JNE:
++ if (imm >= -0x7fff && imm <= 0x8000) {
++ emit(ctx, addiu, tmp, lo(dst), -imm);
++ } else if ((u32)imm <= 0xffff) {
++ emit(ctx, xori, tmp, lo(dst), imm);
++ } else { /* Register fallback */
++ emit_mov_i(ctx, tmp, imm);
++ emit(ctx, xor, tmp, lo(dst), tmp);
++ }
++ if (imm < 0) { /* Compare sign extension */
++ emit(ctx, addu, MIPS_R_T9, hi(dst), 1);
++ emit(ctx, or, tmp, tmp, MIPS_R_T9);
++ } else { /* Compare zero extension */
++ emit(ctx, or, tmp, tmp, hi(dst));
++ }
++ if (op == BPF_JEQ)
++ emit(ctx, beqz, tmp, off);
++ else /* BPF_JNE */
++ emit(ctx, bnez, tmp, off);
++ break;
++ /* PC += off if dst & imm */
++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */
++ case BPF_JSET:
++ case JIT_JNSET:
++ if ((u32)imm <= 0xffff) {
++ emit(ctx, andi, tmp, lo(dst), imm);
++ } else { /* Register fallback */
++ emit_mov_i(ctx, tmp, imm);
++ emit(ctx, and, tmp, lo(dst), tmp);
++ }
++ if (imm < 0) /* Sign-extension pulls in high word */
++ emit(ctx, or, tmp, tmp, hi(dst));
++ if (op == BPF_JSET)
++ emit(ctx, bnez, tmp, off);
++ else /* JIT_JNSET */
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst > imm */
++ case BPF_JGT:
++ emit_sltiu_r64(ctx, tmp, dst, (s64)imm + 1);
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst >= imm */
++ case BPF_JGE:
++ emit_sltiu_r64(ctx, tmp, dst, imm);
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst < imm */
++ case BPF_JLT:
++ emit_sltiu_r64(ctx, tmp, dst, imm);
++ emit(ctx, bnez, tmp, off);
++ break;
++ /* PC += off if dst <= imm */
++ case BPF_JLE:
++ emit_sltiu_r64(ctx, tmp, dst, (s64)imm + 1);
++ emit(ctx, bnez, tmp, off);
++ break;
++ /* PC += off if dst > imm (signed) */
++ case BPF_JSGT:
++ emit_slti_r64(ctx, tmp, dst, (s64)imm + 1);
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst >= imm (signed) */
++ case BPF_JSGE:
++ emit_slti_r64(ctx, tmp, dst, imm);
++ emit(ctx, beqz, tmp, off);
++ break;
++ /* PC += off if dst < imm (signed) */
++ case BPF_JSLT:
++ emit_slti_r64(ctx, tmp, dst, imm);
++ emit(ctx, bnez, tmp, off);
++ break;
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JSLE:
++ emit_slti_r64(ctx, tmp, dst, (s64)imm + 1);
++ emit(ctx, bnez, tmp, off);
++ break;
++ }
++}
++
++/* Jump register (64-bit) */
++static void emit_jmp_r64(struct jit_context *ctx,
++ const u8 dst[], const u8 src[], s32 off, u8 op)
++{
++ u8 t1 = MIPS_R_T6;
++ u8 t2 = MIPS_R_T7;
++
++ switch (op) {
++ /* No-op, used internally for branch optimization */
++ case JIT_JNOP:
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ case BPF_JEQ:
++ case BPF_JNE:
++ emit(ctx, subu, t1, lo(dst), lo(src));
++ emit(ctx, subu, t2, hi(dst), hi(src));
++ emit(ctx, or, t1, t1, t2);
++ if (op == BPF_JEQ)
++ emit(ctx, beqz, t1, off);
++ else /* BPF_JNE */
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst & src */
++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */
++ case BPF_JSET:
++ case JIT_JNSET:
++ emit(ctx, and, t1, lo(dst), lo(src));
++ emit(ctx, and, t2, hi(dst), hi(src));
++ emit(ctx, or, t1, t1, t2);
++ if (op == BPF_JSET)
++ emit(ctx, bnez, t1, off);
++ else /* JIT_JNSET */
++ emit(ctx, beqz, t1, off);
++ break;
++ /* PC += off if dst > src */
++ case BPF_JGT:
++ emit_sltu_r64(ctx, t1, src, dst);
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst >= src */
++ case BPF_JGE:
++ emit_sltu_r64(ctx, t1, dst, src);
++ emit(ctx, beqz, t1, off);
++ break;
++ /* PC += off if dst < src */
++ case BPF_JLT:
++ emit_sltu_r64(ctx, t1, dst, src);
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst <= src */
++ case BPF_JLE:
++ emit_sltu_r64(ctx, t1, src, dst);
++ emit(ctx, beqz, t1, off);
++ break;
++ /* PC += off if dst > src (signed) */
++ case BPF_JSGT:
++ emit_slt_r64(ctx, t1, src, dst);
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst >= src (signed) */
++ case BPF_JSGE:
++ emit_slt_r64(ctx, t1, dst, src);
++ emit(ctx, beqz, t1, off);
++ break;
++ /* PC += off if dst < src (signed) */
++ case BPF_JSLT:
++ emit_slt_r64(ctx, t1, dst, src);
++ emit(ctx, bnez, t1, off);
++ break;
++ /* PC += off if dst <= src (signed) */
++ case BPF_JSLE:
++ emit_slt_r64(ctx, t1, src, dst);
++ emit(ctx, beqz, t1, off);
++ break;
++ }
++}
++
++/* Function call */
++static int emit_call(struct jit_context *ctx, const struct bpf_insn *insn)
++{
++ bool fixed;
++ u64 addr;
++
++ /* Decode the call address */
++ if (bpf_jit_get_func_addr(ctx->program, insn, false,
++ &addr, &fixed) < 0)
++ return -1;
++ if (!fixed)
++ return -1;
++
++ /* Push stack arguments */
++ push_regs(ctx, JIT_STACK_REGS, 0, JIT_RESERVED_STACK);
++
++ /* Emit function call */
++ emit_mov_i(ctx, MIPS_R_T9, addr);
++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9);
++ emit(ctx, nop); /* Delay slot */
++
++ clobber_reg(ctx, MIPS_R_RA);
++ clobber_reg(ctx, MIPS_R_V0);
++ clobber_reg(ctx, MIPS_R_V1);
++ return 0;
++}
++
++/* Function tail call */
++static int emit_tail_call(struct jit_context *ctx)
++{
++ u8 ary = lo(bpf2mips32[BPF_REG_2]);
++ u8 ind = lo(bpf2mips32[BPF_REG_3]);
++ u8 t1 = MIPS_R_T8;
++ u8 t2 = MIPS_R_T9;
++ int off;
++
++ /*
++ * Tail call:
++ * eBPF R1 - function argument (context ptr), passed in a0-a1
++ * eBPF R2 - ptr to object with array of function entry points
++ * eBPF R3 - array index of function to be called
++ * stack[sz] - remaining tail call count, initialized in prologue
++ */
++
++ /* if (ind >= ary->map.max_entries) goto out */
++ off = offsetof(struct bpf_array, map.max_entries);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, lw, t1, off, ary); /* t1 = ary->map.max_entries*/
++ emit_load_delay(ctx); /* Load delay slot */
++ emit(ctx, sltu, t1, ind, t1); /* t1 = ind < t1 */
++ emit(ctx, beqz, t1, get_offset(ctx, 1)); /* PC += off(1) if t1 == 0 */
++ /* (next insn delay slot) */
++ /* if (TCC-- <= 0) goto out */
++ emit(ctx, lw, t2, ctx->stack_size, MIPS_R_SP); /* t2 = *(SP + size) */
++ emit_load_delay(ctx); /* Load delay slot */
++ emit(ctx, blez, t2, get_offset(ctx, 1)); /* PC += off(1) if t2 < 0 */
++ emit(ctx, addiu, t2, t2, -1); /* t2-- (delay slot) */
++ emit(ctx, sw, t2, ctx->stack_size, MIPS_R_SP); /* *(SP + size) = t2 */
++
++ /* prog = ary->ptrs[ind] */
++ off = offsetof(struct bpf_array, ptrs);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, sll, t1, ind, 2); /* t1 = ind << 2 */
++ emit(ctx, addu, t1, t1, ary); /* t1 += ary */
++ emit(ctx, lw, t2, off, t1); /* t2 = *(t1 + off) */
++ emit_load_delay(ctx); /* Load delay slot */
++
++ /* if (prog == 0) goto out */
++ emit(ctx, beqz, t2, get_offset(ctx, 1)); /* PC += off(1) if t2 == 0 */
++ emit(ctx, nop); /* Delay slot */
++
++ /* func = prog->bpf_func + 8 (prologue skip offset) */
++ off = offsetof(struct bpf_prog, bpf_func);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, lw, t1, off, t2); /* t1 = *(t2 + off) */
++ emit_load_delay(ctx); /* Load delay slot */
++ emit(ctx, addiu, t1, t1, JIT_TCALL_SKIP); /* t1 += skip (8 or 12) */
++
++ /* goto func */
++ build_epilogue(ctx, t1);
++ return 0;
++}
++
++/*
++ * Stack frame layout for a JITed program (stack grows down).
++ *
++ * Higher address : Caller's stack frame :
++ * :----------------------------:
++ * : 64-bit eBPF args r3-r5 :
++ * :----------------------------:
++ * : Reserved / tail call count :
++ * +============================+ <--- MIPS sp before call
++ * | Callee-saved registers, |
++ * | including RA and FP |
++ * +----------------------------+ <--- eBPF FP (MIPS zero,fp)
++ * | Local eBPF variables |
++ * | allocated by program |
++ * +----------------------------+
++ * | Reserved for caller-saved |
++ * | registers |
++ * +----------------------------+
++ * | Reserved for 64-bit eBPF |
++ * | args r3-r5 & args passed |
++ * | on stack in kernel calls |
++ * Lower address +============================+ <--- MIPS sp
++ */
++
++/* Build program prologue to set up the stack and registers */
++void build_prologue(struct jit_context *ctx)
++{
++ const u8 *r1 = bpf2mips32[BPF_REG_1];
++ const u8 *fp = bpf2mips32[BPF_REG_FP];
++ int stack, saved, locals, reserved;
++
++ /*
++ * The first two instructions initialize TCC in the reserved (for us)
++ * 16-byte area in the parent's stack frame. On a tail call, the
++ * calling function jumps into the prologue after these instructions.
++ */
++ emit(ctx, ori, MIPS_R_T9, MIPS_R_ZERO,
++ min(MAX_TAIL_CALL_CNT + 1, 0xffff));
++ emit(ctx, sw, MIPS_R_T9, 0, MIPS_R_SP);
++
++ /*
++ * Register eBPF R1 contains the 32-bit context pointer argument.
++ * A 32-bit argument is always passed in MIPS register a0, regardless
++ * of CPU endianness. Initialize R1 accordingly and zero-extend.
++ */
++#ifdef __BIG_ENDIAN
++ emit(ctx, move, lo(r1), MIPS_R_A0);
++#endif
++
++ /* === Entry-point for tail calls === */
++
++ /* Zero-extend the 32-bit argument */
++ emit(ctx, move, hi(r1), MIPS_R_ZERO);
++
++ /* If the eBPF frame pointer was accessed it must be saved */
++ if (ctx->accessed & BIT(BPF_REG_FP))
++ clobber_reg64(ctx, fp);
++
++ /* Compute the stack space needed for callee-saved registers */
++ saved = hweight32(ctx->clobbered & JIT_CALLEE_REGS) * sizeof(u32);
++ saved = ALIGN(saved, MIPS_STACK_ALIGNMENT);
++
++ /* Stack space used by eBPF program local data */
++ locals = ALIGN(ctx->program->aux->stack_depth, MIPS_STACK_ALIGNMENT);
++
++ /*
++ * If we are emitting function calls, reserve extra stack space for
++ * caller-saved registers and function arguments passed on the stack.
++ * The required space is computed automatically during resource
++ * usage discovery (pass 1).
++ */
++ reserved = ctx->stack_used;
++
++ /* Allocate the stack frame */
++ stack = ALIGN(saved + locals + reserved, MIPS_STACK_ALIGNMENT);
++ emit(ctx, addiu, MIPS_R_SP, MIPS_R_SP, -stack);
++
++ /* Store callee-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0, stack - saved);
++
++ /* Initialize the eBPF frame pointer if accessed */
++ if (ctx->accessed & BIT(BPF_REG_FP))
++ emit(ctx, addiu, lo(fp), MIPS_R_SP, stack - saved);
++
++ ctx->saved_size = saved;
++ ctx->stack_size = stack;
++}
++
++/* Build the program epilogue to restore the stack and registers */
++void build_epilogue(struct jit_context *ctx, int dest_reg)
++{
++ /* Restore callee-saved registers from stack */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0,
++ ctx->stack_size - ctx->saved_size);
++ /*
++ * A 32-bit return value is always passed in MIPS register v0,
++ * but on big-endian targets the low part of R0 is mapped to v1.
++ */
++#ifdef __BIG_ENDIAN
++ emit(ctx, move, MIPS_R_V0, MIPS_R_V1);
++#endif
++
++ /* Jump to the return address and adjust the stack pointer */
++ emit(ctx, jr, dest_reg);
++ emit(ctx, addiu, MIPS_R_SP, MIPS_R_SP, ctx->stack_size);
++}
++
++/* Build one eBPF instruction */
++int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
++{
++ const u8 *dst = bpf2mips32[insn->dst_reg];
++ const u8 *src = bpf2mips32[insn->src_reg];
++ const u8 *tmp = bpf2mips32[JIT_REG_TMP];
++ u8 code = insn->code;
++ s16 off = insn->off;
++ s32 imm = insn->imm;
++ s32 val, rel;
++ u8 alu, jmp;
++
++ switch (code) {
++ /* ALU operations */
++ /* dst = imm */
++ case BPF_ALU | BPF_MOV | BPF_K:
++ emit_mov_i(ctx, lo(dst), imm);
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = src */
++ case BPF_ALU | BPF_MOV | BPF_X:
++ if (imm == 1) {
++ /* Special mov32 for zext */
++ emit_mov_i(ctx, hi(dst), 0);
++ } else {
++ emit_mov_r(ctx, lo(dst), lo(src));
++ emit_zext_ver(ctx, dst);
++ }
++ break;
++ /* dst = -dst */
++ case BPF_ALU | BPF_NEG:
++ emit_alu_i(ctx, lo(dst), 0, BPF_NEG);
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst & imm */
++ /* dst = dst | imm */
++ /* dst = dst ^ imm */
++ /* dst = dst << imm */
++ /* dst = dst >> imm */
++ /* dst = dst >> imm (arithmetic) */
++ /* dst = dst + imm */
++ /* dst = dst - imm */
++ /* dst = dst * imm */
++ /* dst = dst / imm */
++ /* dst = dst % imm */
++ case BPF_ALU | BPF_OR | BPF_K:
++ case BPF_ALU | BPF_AND | BPF_K:
++ case BPF_ALU | BPF_XOR | BPF_K:
++ case BPF_ALU | BPF_LSH | BPF_K:
++ case BPF_ALU | BPF_RSH | BPF_K:
++ case BPF_ALU | BPF_ARSH | BPF_K:
++ case BPF_ALU | BPF_ADD | BPF_K:
++ case BPF_ALU | BPF_SUB | BPF_K:
++ case BPF_ALU | BPF_MUL | BPF_K:
++ case BPF_ALU | BPF_DIV | BPF_K:
++ case BPF_ALU | BPF_MOD | BPF_K:
++ if (!valid_alu_i(BPF_OP(code), imm)) {
++ emit_mov_i(ctx, MIPS_R_T6, imm);
++ emit_alu_r(ctx, lo(dst), MIPS_R_T6, BPF_OP(code));
++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
++ emit_alu_i(ctx, lo(dst), val, alu);
++ }
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst & src */
++ /* dst = dst | src */
++ /* dst = dst ^ src */
++ /* dst = dst << src */
++ /* dst = dst >> src */
++ /* dst = dst >> src (arithmetic) */
++ /* dst = dst + src */
++ /* dst = dst - src */
++ /* dst = dst * src */
++ /* dst = dst / src */
++ /* dst = dst % src */
++ case BPF_ALU | BPF_AND | BPF_X:
++ case BPF_ALU | BPF_OR | BPF_X:
++ case BPF_ALU | BPF_XOR | BPF_X:
++ case BPF_ALU | BPF_LSH | BPF_X:
++ case BPF_ALU | BPF_RSH | BPF_X:
++ case BPF_ALU | BPF_ARSH | BPF_X:
++ case BPF_ALU | BPF_ADD | BPF_X:
++ case BPF_ALU | BPF_SUB | BPF_X:
++ case BPF_ALU | BPF_MUL | BPF_X:
++ case BPF_ALU | BPF_DIV | BPF_X:
++ case BPF_ALU | BPF_MOD | BPF_X:
++ emit_alu_r(ctx, lo(dst), lo(src), BPF_OP(code));
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = imm (64-bit) */
++ case BPF_ALU64 | BPF_MOV | BPF_K:
++ emit_mov_se_i64(ctx, dst, imm);
++ break;
++ /* dst = src (64-bit) */
++ case BPF_ALU64 | BPF_MOV | BPF_X:
++ emit_mov_r(ctx, lo(dst), lo(src));
++ emit_mov_r(ctx, hi(dst), hi(src));
++ break;
++ /* dst = -dst (64-bit) */
++ case BPF_ALU64 | BPF_NEG:
++ emit_neg_i64(ctx, dst);
++ break;
++ /* dst = dst & imm (64-bit) */
++ case BPF_ALU64 | BPF_AND | BPF_K:
++ emit_alu_i64(ctx, dst, imm, BPF_OP(code));
++ break;
++ /* dst = dst | imm (64-bit) */
++ /* dst = dst ^ imm (64-bit) */
++ /* dst = dst + imm (64-bit) */
++ /* dst = dst - imm (64-bit) */
++ case BPF_ALU64 | BPF_OR | BPF_K:
++ case BPF_ALU64 | BPF_XOR | BPF_K:
++ case BPF_ALU64 | BPF_ADD | BPF_K:
++ case BPF_ALU64 | BPF_SUB | BPF_K:
++ if (imm)
++ emit_alu_i64(ctx, dst, imm, BPF_OP(code));
++ break;
++ /* dst = dst << imm (64-bit) */
++ /* dst = dst >> imm (64-bit) */
++ /* dst = dst >> imm (64-bit, arithmetic) */
++ case BPF_ALU64 | BPF_LSH | BPF_K:
++ case BPF_ALU64 | BPF_RSH | BPF_K:
++ case BPF_ALU64 | BPF_ARSH | BPF_K:
++ if (imm)
++ emit_shift_i64(ctx, dst, imm, BPF_OP(code));
++ break;
++ /* dst = dst * imm (64-bit) */
++ case BPF_ALU64 | BPF_MUL | BPF_K:
++ emit_mul_i64(ctx, dst, imm);
++ break;
++ /* dst = dst / imm (64-bit) */
++ /* dst = dst % imm (64-bit) */
++ case BPF_ALU64 | BPF_DIV | BPF_K:
++ case BPF_ALU64 | BPF_MOD | BPF_K:
++ /*
++ * Sign-extend the immediate value into a temporary register,
++ * and then do the operation on this register.
++ */
++ emit_mov_se_i64(ctx, tmp, imm);
++ emit_divmod_r64(ctx, dst, tmp, BPF_OP(code));
++ break;
++ /* dst = dst & src (64-bit) */
++ /* dst = dst | src (64-bit) */
++ /* dst = dst ^ src (64-bit) */
++ /* dst = dst + src (64-bit) */
++ /* dst = dst - src (64-bit) */
++ case BPF_ALU64 | BPF_AND | BPF_X:
++ case BPF_ALU64 | BPF_OR | BPF_X:
++ case BPF_ALU64 | BPF_XOR | BPF_X:
++ case BPF_ALU64 | BPF_ADD | BPF_X:
++ case BPF_ALU64 | BPF_SUB | BPF_X:
++ emit_alu_r64(ctx, dst, src, BPF_OP(code));
++ break;
++ /* dst = dst << src (64-bit) */
++ /* dst = dst >> src (64-bit) */
++ /* dst = dst >> src (64-bit, arithmetic) */
++ case BPF_ALU64 | BPF_LSH | BPF_X:
++ case BPF_ALU64 | BPF_RSH | BPF_X:
++ case BPF_ALU64 | BPF_ARSH | BPF_X:
++ emit_shift_r64(ctx, dst, lo(src), BPF_OP(code));
++ break;
++ /* dst = dst * src (64-bit) */
++ case BPF_ALU64 | BPF_MUL | BPF_X:
++ emit_mul_r64(ctx, dst, src);
++ break;
++ /* dst = dst / src (64-bit) */
++ /* dst = dst % src (64-bit) */
++ case BPF_ALU64 | BPF_DIV | BPF_X:
++ case BPF_ALU64 | BPF_MOD | BPF_X:
++ emit_divmod_r64(ctx, dst, src, BPF_OP(code));
++ break;
++ /* dst = htole(dst) */
++ /* dst = htobe(dst) */
++ case BPF_ALU | BPF_END | BPF_FROM_LE:
++ case BPF_ALU | BPF_END | BPF_FROM_BE:
++ if (BPF_SRC(code) ==
++#ifdef __BIG_ENDIAN
++ BPF_FROM_LE
++#else
++ BPF_FROM_BE
++#endif
++ )
++ emit_bswap_r64(ctx, dst, imm);
++ else
++ emit_trunc_r64(ctx, dst, imm);
++ break;
++ /* dst = imm64 */
++ case BPF_LD | BPF_IMM | BPF_DW:
++ emit_mov_i(ctx, lo(dst), imm);
++ emit_mov_i(ctx, hi(dst), insn[1].imm);
++ return 1;
++ /* LDX: dst = *(size *)(src + off) */
++ case BPF_LDX | BPF_MEM | BPF_W:
++ case BPF_LDX | BPF_MEM | BPF_H:
++ case BPF_LDX | BPF_MEM | BPF_B:
++ case BPF_LDX | BPF_MEM | BPF_DW:
++ emit_ldx(ctx, dst, lo(src), off, BPF_SIZE(code));
++ break;
++ /* ST: *(size *)(dst + off) = imm */
++ case BPF_ST | BPF_MEM | BPF_W:
++ case BPF_ST | BPF_MEM | BPF_H:
++ case BPF_ST | BPF_MEM | BPF_B:
++ case BPF_ST | BPF_MEM | BPF_DW:
++ switch (BPF_SIZE(code)) {
++ case BPF_DW:
++ /* Sign-extend immediate value into temporary reg */
++ emit_mov_se_i64(ctx, tmp, imm);
++ break;
++ case BPF_W:
++ case BPF_H:
++ case BPF_B:
++ emit_mov_i(ctx, lo(tmp), imm);
++ break;
++ }
++ emit_stx(ctx, lo(dst), tmp, off, BPF_SIZE(code));
++ break;
++ /* STX: *(size *)(dst + off) = src */
++ case BPF_STX | BPF_MEM | BPF_W:
++ case BPF_STX | BPF_MEM | BPF_H:
++ case BPF_STX | BPF_MEM | BPF_B:
++ case BPF_STX | BPF_MEM | BPF_DW:
++ emit_stx(ctx, lo(dst), src, off, BPF_SIZE(code));
++ break;
++ /* Speculation barrier */
++ case BPF_ST | BPF_NOSPEC:
++ break;
++ /* Atomics */
++ case BPF_STX | BPF_XADD | BPF_W:
++ switch (imm) {
++ case BPF_ADD:
++ case BPF_AND:
++ case BPF_OR:
++ case BPF_XOR:
++ if (cpu_has_llsc)
++ emit_atomic_r(ctx, lo(dst), lo(src), off, imm);
++ else /* Non-ll/sc fallback */
++ emit_atomic_r32(ctx, lo(dst), lo(src),
++ off, imm);
++ break;
++ default:
++ goto notyet;
++ }
++ break;
++ /* Atomics (64-bit) */
++ case BPF_STX | BPF_XADD | BPF_DW:
++ switch (imm) {
++ case BPF_ADD:
++ case BPF_AND:
++ case BPF_OR:
++ case BPF_XOR:
++ emit_atomic_r64(ctx, lo(dst), src, off, imm);
++ break;
++ default:
++ goto notyet;
++ }
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ /* PC += off if dst & src */
++ /* PC += off if dst > src */
++ /* PC += off if dst >= src */
++ /* PC += off if dst < src */
++ /* PC += off if dst <= src */
++ /* PC += off if dst > src (signed) */
++ /* PC += off if dst >= src (signed) */
++ /* PC += off if dst < src (signed) */
++ /* PC += off if dst <= src (signed) */
++ case BPF_JMP32 | BPF_JEQ | BPF_X:
++ case BPF_JMP32 | BPF_JNE | BPF_X:
++ case BPF_JMP32 | BPF_JSET | BPF_X:
++ case BPF_JMP32 | BPF_JGT | BPF_X:
++ case BPF_JMP32 | BPF_JGE | BPF_X:
++ case BPF_JMP32 | BPF_JLT | BPF_X:
++ case BPF_JMP32 | BPF_JLE | BPF_X:
++ case BPF_JMP32 | BPF_JSGT | BPF_X:
++ case BPF_JMP32 | BPF_JSGE | BPF_X:
++ case BPF_JMP32 | BPF_JSLT | BPF_X:
++ case BPF_JMP32 | BPF_JSLE | BPF_X:
++ if (off == 0)
++ break;
++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel);
++ emit_jmp_r(ctx, lo(dst), lo(src), rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ /* PC += off if dst & imm */
++ /* PC += off if dst > imm */
++ /* PC += off if dst >= imm */
++ /* PC += off if dst < imm */
++ /* PC += off if dst <= imm */
++ /* PC += off if dst > imm (signed) */
++ /* PC += off if dst >= imm (signed) */
++ /* PC += off if dst < imm (signed) */
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JMP32 | BPF_JEQ | BPF_K:
++ case BPF_JMP32 | BPF_JNE | BPF_K:
++ case BPF_JMP32 | BPF_JSET | BPF_K:
++ case BPF_JMP32 | BPF_JGT | BPF_K:
++ case BPF_JMP32 | BPF_JGE | BPF_K:
++ case BPF_JMP32 | BPF_JLT | BPF_K:
++ case BPF_JMP32 | BPF_JLE | BPF_K:
++ case BPF_JMP32 | BPF_JSGT | BPF_K:
++ case BPF_JMP32 | BPF_JSGE | BPF_K:
++ case BPF_JMP32 | BPF_JSLT | BPF_K:
++ case BPF_JMP32 | BPF_JSLE | BPF_K:
++ if (off == 0)
++ break;
++ setup_jmp_i(ctx, imm, 32, BPF_OP(code), off, &jmp, &rel);
++ if (valid_jmp_i(jmp, imm)) {
++ emit_jmp_i(ctx, lo(dst), imm, rel, jmp);
++ } else {
++ /* Move large immediate to register */
++ emit_mov_i(ctx, MIPS_R_T6, imm);
++ emit_jmp_r(ctx, lo(dst), MIPS_R_T6, rel, jmp);
++ }
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ /* PC += off if dst & src */
++ /* PC += off if dst > src */
++ /* PC += off if dst >= src */
++ /* PC += off if dst < src */
++ /* PC += off if dst <= src */
++ /* PC += off if dst > src (signed) */
++ /* PC += off if dst >= src (signed) */
++ /* PC += off if dst < src (signed) */
++ /* PC += off if dst <= src (signed) */
++ case BPF_JMP | BPF_JEQ | BPF_X:
++ case BPF_JMP | BPF_JNE | BPF_X:
++ case BPF_JMP | BPF_JSET | BPF_X:
++ case BPF_JMP | BPF_JGT | BPF_X:
++ case BPF_JMP | BPF_JGE | BPF_X:
++ case BPF_JMP | BPF_JLT | BPF_X:
++ case BPF_JMP | BPF_JLE | BPF_X:
++ case BPF_JMP | BPF_JSGT | BPF_X:
++ case BPF_JMP | BPF_JSGE | BPF_X:
++ case BPF_JMP | BPF_JSLT | BPF_X:
++ case BPF_JMP | BPF_JSLE | BPF_X:
++ if (off == 0)
++ break;
++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel);
++ emit_jmp_r64(ctx, dst, src, rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ /* PC += off if dst & imm */
++ /* PC += off if dst > imm */
++ /* PC += off if dst >= imm */
++ /* PC += off if dst < imm */
++ /* PC += off if dst <= imm */
++ /* PC += off if dst > imm (signed) */
++ /* PC += off if dst >= imm (signed) */
++ /* PC += off if dst < imm (signed) */
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JMP | BPF_JEQ | BPF_K:
++ case BPF_JMP | BPF_JNE | BPF_K:
++ case BPF_JMP | BPF_JSET | BPF_K:
++ case BPF_JMP | BPF_JGT | BPF_K:
++ case BPF_JMP | BPF_JGE | BPF_K:
++ case BPF_JMP | BPF_JLT | BPF_K:
++ case BPF_JMP | BPF_JLE | BPF_K:
++ case BPF_JMP | BPF_JSGT | BPF_K:
++ case BPF_JMP | BPF_JSGE | BPF_K:
++ case BPF_JMP | BPF_JSLT | BPF_K:
++ case BPF_JMP | BPF_JSLE | BPF_K:
++ if (off == 0)
++ break;
++ setup_jmp_i(ctx, imm, 64, BPF_OP(code), off, &jmp, &rel);
++ emit_jmp_i64(ctx, dst, imm, rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off */
++ case BPF_JMP | BPF_JA:
++ if (off == 0)
++ break;
++ if (emit_ja(ctx, off) < 0)
++ goto toofar;
++ break;
++ /* Tail call */
++ case BPF_JMP | BPF_TAIL_CALL:
++ if (emit_tail_call(ctx) < 0)
++ goto invalid;
++ break;
++ /* Function call */
++ case BPF_JMP | BPF_CALL:
++ if (emit_call(ctx, insn) < 0)
++ goto invalid;
++ break;
++ /* Function return */
++ case BPF_JMP | BPF_EXIT:
++ /*
++ * Optimization: when last instruction is EXIT
++ * simply continue to epilogue.
++ */
++ if (ctx->bpf_index == ctx->program->len - 1)
++ break;
++ if (emit_exit(ctx) < 0)
++ goto toofar;
++ break;
++
++ default:
++invalid:
++ pr_err_once("unknown opcode %02x\n", code);
++ return -EINVAL;
++notyet:
++ pr_info_once("*** NOT YET: opcode %02x ***\n", code);
++ return -EFAULT;
++toofar:
++ pr_info_once("*** TOO FAR: jump at %u opcode %02x ***\n",
++ ctx->bpf_index, code);
++ return -E2BIG;
++ }
++ return 0;
++}
diff --git a/target/linux/generic/backport-6.1/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch b/target/linux/generic/backport-6.1/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch
new file mode 100644
index 0000000000..38b46c0b76
--- /dev/null
+++ b/target/linux/generic/backport-6.1/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch
@@ -0,0 +1,1005 @@
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:05 +0200
+Subject: [PATCH] mips: bpf: Add new eBPF JIT for 64-bit MIPS
+
+This is an implementation on of an eBPF JIT for 64-bit MIPS III-V and
+MIPS64r1-r6. It uses the same framework introduced by the 32-bit JIT.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+ create mode 100644 arch/mips/net/bpf_jit_comp64.c
+
+--- /dev/null
++++ b/arch/mips/net/bpf_jit_comp64.c
+@@ -0,0 +1,991 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Just-In-Time compiler for eBPF bytecode on MIPS.
++ * Implementation of JIT functions for 64-bit CPUs.
++ *
++ * Copyright (c) 2021 Anyfi Networks AB.
++ * Author: Johan Almbladh <johan.almbladh@gmail.com>
++ *
++ * Based on code and ideas from
++ * Copyright (c) 2017 Cavium, Inc.
++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com>
++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
++ */
++
++#include <linux/errno.h>
++#include <linux/filter.h>
++#include <linux/bpf.h>
++#include <asm/cpu-features.h>
++#include <asm/isa-rev.h>
++#include <asm/uasm.h>
++
++#include "bpf_jit_comp.h"
++
++/* MIPS t0-t3 are not available in the n64 ABI */
++#undef MIPS_R_T0
++#undef MIPS_R_T1
++#undef MIPS_R_T2
++#undef MIPS_R_T3
++
++/* Stack is 16-byte aligned in n64 ABI */
++#define MIPS_STACK_ALIGNMENT 16
++
++/* Extra 64-bit eBPF registers used by JIT */
++#define JIT_REG_TC (MAX_BPF_JIT_REG + 0)
++#define JIT_REG_ZX (MAX_BPF_JIT_REG + 1)
++
++/* Number of prologue bytes to skip when doing a tail call */
++#define JIT_TCALL_SKIP 4
++
++/* Callee-saved CPU registers that the JIT must preserve */
++#define JIT_CALLEE_REGS \
++ (BIT(MIPS_R_S0) | \
++ BIT(MIPS_R_S1) | \
++ BIT(MIPS_R_S2) | \
++ BIT(MIPS_R_S3) | \
++ BIT(MIPS_R_S4) | \
++ BIT(MIPS_R_S5) | \
++ BIT(MIPS_R_S6) | \
++ BIT(MIPS_R_S7) | \
++ BIT(MIPS_R_GP) | \
++ BIT(MIPS_R_FP) | \
++ BIT(MIPS_R_RA))
++
++/* Caller-saved CPU registers available for JIT use */
++#define JIT_CALLER_REGS \
++ (BIT(MIPS_R_A5) | \
++ BIT(MIPS_R_A6) | \
++ BIT(MIPS_R_A7))
++/*
++ * Mapping of 64-bit eBPF registers to 64-bit native MIPS registers.
++ * MIPS registers t4 - t7 may be used by the JIT as temporary registers.
++ * MIPS registers t8 - t9 are reserved for single-register common functions.
++ */
++static const u8 bpf2mips64[] = {
++ /* Return value from in-kernel function, and exit value from eBPF */
++ [BPF_REG_0] = MIPS_R_V0,
++ /* Arguments from eBPF program to in-kernel function */
++ [BPF_REG_1] = MIPS_R_A0,
++ [BPF_REG_2] = MIPS_R_A1,
++ [BPF_REG_3] = MIPS_R_A2,
++ [BPF_REG_4] = MIPS_R_A3,
++ [BPF_REG_5] = MIPS_R_A4,
++ /* Callee-saved registers that in-kernel function will preserve */
++ [BPF_REG_6] = MIPS_R_S0,
++ [BPF_REG_7] = MIPS_R_S1,
++ [BPF_REG_8] = MIPS_R_S2,
++ [BPF_REG_9] = MIPS_R_S3,
++ /* Read-only frame pointer to access the eBPF stack */
++ [BPF_REG_FP] = MIPS_R_FP,
++ /* Temporary register for blinding constants */
++ [BPF_REG_AX] = MIPS_R_AT,
++ /* Tail call count register, caller-saved */
++ [JIT_REG_TC] = MIPS_R_A5,
++ /* Constant for register zero-extension */
++ [JIT_REG_ZX] = MIPS_R_V1,
++};
++
++/*
++ * MIPS 32-bit operations on 64-bit registers generate a sign-extended
++ * result. However, the eBPF ISA mandates zero-extension, so we rely on the
++ * verifier to add that for us (emit_zext_ver). In addition, ALU arithmetic
++ * operations, right shift and byte swap require properly sign-extended
++ * operands or the result is unpredictable. We emit explicit sign-extensions
++ * in those cases.
++ */
++
++/* Sign extension */
++static void emit_sext(struct jit_context *ctx, u8 dst, u8 src)
++{
++ emit(ctx, sll, dst, src, 0);
++ clobber_reg(ctx, dst);
++}
++
++/* Zero extension */
++static void emit_zext(struct jit_context *ctx, u8 dst)
++{
++ if (cpu_has_mips64r2 || cpu_has_mips64r6) {
++ emit(ctx, dinsu, dst, MIPS_R_ZERO, 32, 32);
++ } else {
++ emit(ctx, and, dst, dst, bpf2mips64[JIT_REG_ZX]);
++ access_reg(ctx, JIT_REG_ZX); /* We need the ZX register */
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Zero extension, if verifier does not do it for us */
++static void emit_zext_ver(struct jit_context *ctx, u8 dst)
++{
++ if (!ctx->program->aux->verifier_zext)
++ emit_zext(ctx, dst);
++}
++
++/* dst = imm (64-bit) */
++static void emit_mov_i64(struct jit_context *ctx, u8 dst, u64 imm64)
++{
++ if (imm64 >= 0xffffffffffff8000ULL || imm64 < 0x8000ULL) {
++ emit(ctx, daddiu, dst, MIPS_R_ZERO, (s16)imm64);
++ } else if (imm64 >= 0xffffffff80000000ULL ||
++ (imm64 < 0x80000000 && imm64 > 0xffff)) {
++ emit(ctx, lui, dst, (s16)(imm64 >> 16));
++ emit(ctx, ori, dst, dst, (u16)imm64 & 0xffff);
++ } else {
++ u8 acc = MIPS_R_ZERO;
++ int k;
++
++ for (k = 0; k < 4; k++) {
++ u16 half = imm64 >> (48 - 16 * k);
++
++ if (acc == dst)
++ emit(ctx, dsll, dst, dst, 16);
++
++ if (half) {
++ emit(ctx, ori, dst, acc, half);
++ acc = dst;
++ }
++ }
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* ALU immediate operation (64-bit) */
++static void emit_alu_i64(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst | imm */
++ case BPF_OR:
++ emit(ctx, ori, dst, dst, (u16)imm);
++ break;
++ /* dst = dst ^ imm */
++ case BPF_XOR:
++ emit(ctx, xori, dst, dst, (u16)imm);
++ break;
++ /* dst = -dst */
++ case BPF_NEG:
++ emit(ctx, dsubu, dst, MIPS_R_ZERO, dst);
++ break;
++ /* dst = dst << imm */
++ case BPF_LSH:
++ emit(ctx, dsll_safe, dst, dst, imm);
++ break;
++ /* dst = dst >> imm */
++ case BPF_RSH:
++ emit(ctx, dsrl_safe, dst, dst, imm);
++ break;
++ /* dst = dst >> imm (arithmetic) */
++ case BPF_ARSH:
++ emit(ctx, dsra_safe, dst, dst, imm);
++ break;
++ /* dst = dst + imm */
++ case BPF_ADD:
++ emit(ctx, daddiu, dst, dst, imm);
++ break;
++ /* dst = dst - imm */
++ case BPF_SUB:
++ emit(ctx, daddiu, dst, dst, -imm);
++ break;
++ default:
++ /* Width-generic operations */
++ emit_alu_i(ctx, dst, imm, op);
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* ALU register operation (64-bit) */
++static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op)
++{
++ switch (BPF_OP(op)) {
++ /* dst = dst << src */
++ case BPF_LSH:
++ emit(ctx, dsllv, dst, dst, src);
++ break;
++ /* dst = dst >> src */
++ case BPF_RSH:
++ emit(ctx, dsrlv, dst, dst, src);
++ break;
++ /* dst = dst >> src (arithmetic) */
++ case BPF_ARSH:
++ emit(ctx, dsrav, dst, dst, src);
++ break;
++ /* dst = dst + src */
++ case BPF_ADD:
++ emit(ctx, daddu, dst, dst, src);
++ break;
++ /* dst = dst - src */
++ case BPF_SUB:
++ emit(ctx, dsubu, dst, dst, src);
++ break;
++ /* dst = dst * src */
++ case BPF_MUL:
++ if (cpu_has_mips64r6) {
++ emit(ctx, dmulu, dst, dst, src);
++ } else {
++ emit(ctx, dmultu, dst, src);
++ emit(ctx, mflo, dst);
++ }
++ break;
++ /* dst = dst / src */
++ case BPF_DIV:
++ if (cpu_has_mips64r6) {
++ emit(ctx, ddivu_r6, dst, dst, src);
++ } else {
++ emit(ctx, ddivu, dst, src);
++ emit(ctx, mflo, dst);
++ }
++ break;
++ /* dst = dst % src */
++ case BPF_MOD:
++ if (cpu_has_mips64r6) {
++ emit(ctx, dmodu, dst, dst, src);
++ } else {
++ emit(ctx, ddivu, dst, src);
++ emit(ctx, mfhi, dst);
++ }
++ break;
++ default:
++ /* Width-generic operations */
++ emit_alu_r(ctx, dst, src, op);
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Swap sub words in a register double word */
++static void emit_swap_r64(struct jit_context *ctx, u8 dst, u8 mask, u32 bits)
++{
++ u8 tmp = MIPS_R_T9;
++
++ emit(ctx, and, tmp, dst, mask); /* tmp = dst & mask */
++ emit(ctx, dsll, tmp, tmp, bits); /* tmp = tmp << bits */
++ emit(ctx, dsrl, dst, dst, bits); /* dst = dst >> bits */
++ emit(ctx, and, dst, dst, mask); /* dst = dst & mask */
++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */
++}
++
++/* Swap bytes and truncate a register double word, word or half word */
++static void emit_bswap_r64(struct jit_context *ctx, u8 dst, u32 width)
++{
++ switch (width) {
++ /* Swap bytes in a double word */
++ case 64:
++ if (cpu_has_mips64r2 || cpu_has_mips64r6) {
++ emit(ctx, dsbh, dst, dst);
++ emit(ctx, dshd, dst, dst);
++ } else {
++ u8 t1 = MIPS_R_T6;
++ u8 t2 = MIPS_R_T7;
++
++ emit(ctx, dsll32, t2, dst, 0); /* t2 = dst << 32 */
++ emit(ctx, dsrl32, dst, dst, 0); /* dst = dst >> 32 */
++ emit(ctx, or, dst, dst, t2); /* dst = dst | t2 */
++
++ emit(ctx, ori, t2, MIPS_R_ZERO, 0xffff);
++ emit(ctx, dsll32, t1, t2, 0); /* t1 = t2 << 32 */
++ emit(ctx, or, t1, t1, t2); /* t1 = t1 | t2 */
++ emit_swap_r64(ctx, dst, t1, 16);/* dst = swap16(dst) */
++
++ emit(ctx, lui, t2, 0xff); /* t2 = 0x00ff0000 */
++ emit(ctx, ori, t2, t2, 0xff); /* t2 = t2 | 0x00ff */
++ emit(ctx, dsll32, t1, t2, 0); /* t1 = t2 << 32 */
++ emit(ctx, or, t1, t1, t2); /* t1 = t1 | t2 */
++ emit_swap_r64(ctx, dst, t1, 8); /* dst = swap8(dst) */
++ }
++ break;
++ /* Swap bytes in a half word */
++ /* Swap bytes in a word */
++ case 32:
++ case 16:
++ emit_sext(ctx, dst, dst);
++ emit_bswap_r(ctx, dst, width);
++ if (cpu_has_mips64r2 || cpu_has_mips64r6)
++ emit_zext(ctx, dst);
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Truncate a register double word, word or half word */
++static void emit_trunc_r64(struct jit_context *ctx, u8 dst, u32 width)
++{
++ switch (width) {
++ case 64:
++ break;
++ /* Zero-extend a word */
++ case 32:
++ emit_zext(ctx, dst);
++ break;
++ /* Zero-extend a half word */
++ case 16:
++ emit(ctx, andi, dst, dst, 0xffff);
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Load operation: dst = *(size*)(src + off) */
++static void emit_ldx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size)
++{
++ switch (size) {
++ /* Load a byte */
++ case BPF_B:
++ emit(ctx, lbu, dst, off, src);
++ break;
++ /* Load a half word */
++ case BPF_H:
++ emit(ctx, lhu, dst, off, src);
++ break;
++ /* Load a word */
++ case BPF_W:
++ emit(ctx, lwu, dst, off, src);
++ break;
++ /* Load a double word */
++ case BPF_DW:
++ emit(ctx, ld, dst, off, src);
++ break;
++ }
++ clobber_reg(ctx, dst);
++}
++
++/* Store operation: *(size *)(dst + off) = src */
++static void emit_stx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size)
++{
++ switch (size) {
++ /* Store a byte */
++ case BPF_B:
++ emit(ctx, sb, src, off, dst);
++ break;
++ /* Store a half word */
++ case BPF_H:
++ emit(ctx, sh, src, off, dst);
++ break;
++ /* Store a word */
++ case BPF_W:
++ emit(ctx, sw, src, off, dst);
++ break;
++ /* Store a double word */
++ case BPF_DW:
++ emit(ctx, sd, src, off, dst);
++ break;
++ }
++}
++
++/* Atomic read-modify-write */
++static void emit_atomic_r64(struct jit_context *ctx,
++ u8 dst, u8 src, s16 off, u8 code)
++{
++ u8 t1 = MIPS_R_T6;
++ u8 t2 = MIPS_R_T7;
++
++ emit(ctx, lld, t1, off, dst);
++ switch (code) {
++ case BPF_ADD:
++ emit(ctx, daddu, t2, t1, src);
++ break;
++ case BPF_AND:
++ emit(ctx, and, t2, t1, src);
++ break;
++ case BPF_OR:
++ emit(ctx, or, t2, t1, src);
++ break;
++ case BPF_XOR:
++ emit(ctx, xor, t2, t1, src);
++ break;
++ }
++ emit(ctx, scd, t2, off, dst);
++ emit(ctx, beqz, t2, -16);
++ emit(ctx, nop); /* Delay slot */
++}
++
++/* Function call */
++static int emit_call(struct jit_context *ctx, const struct bpf_insn *insn)
++{
++ u8 zx = bpf2mips64[JIT_REG_ZX];
++ u8 tmp = MIPS_R_T6;
++ bool fixed;
++ u64 addr;
++
++ /* Decode the call address */
++ if (bpf_jit_get_func_addr(ctx->program, insn, false,
++ &addr, &fixed) < 0)
++ return -1;
++ if (!fixed)
++ return -1;
++
++ /* Push caller-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0);
++
++ /* Emit function call */
++ emit_mov_i64(ctx, tmp, addr);
++ emit(ctx, jalr, MIPS_R_RA, tmp);
++ emit(ctx, nop); /* Delay slot */
++
++ /* Restore caller-saved registers */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0);
++
++ /* Re-initialize the JIT zero-extension register if accessed */
++ if (ctx->accessed & BIT(JIT_REG_ZX)) {
++ emit(ctx, daddiu, zx, MIPS_R_ZERO, -1);
++ emit(ctx, dsrl32, zx, zx, 0);
++ }
++
++ clobber_reg(ctx, MIPS_R_RA);
++ clobber_reg(ctx, MIPS_R_V0);
++ clobber_reg(ctx, MIPS_R_V1);
++ return 0;
++}
++
++/* Function tail call */
++static int emit_tail_call(struct jit_context *ctx)
++{
++ u8 ary = bpf2mips64[BPF_REG_2];
++ u8 ind = bpf2mips64[BPF_REG_3];
++ u8 tcc = bpf2mips64[JIT_REG_TC];
++ u8 tmp = MIPS_R_T6;
++ int off;
++
++ /*
++ * Tail call:
++ * eBPF R1 - function argument (context ptr), passed in a0-a1
++ * eBPF R2 - ptr to object with array of function entry points
++ * eBPF R3 - array index of function to be called
++ */
++
++ /* if (ind >= ary->map.max_entries) goto out */
++ off = offsetof(struct bpf_array, map.max_entries);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, lwu, tmp, off, ary); /* tmp = ary->map.max_entrs*/
++ emit(ctx, sltu, tmp, ind, tmp); /* tmp = ind < t1 */
++ emit(ctx, beqz, tmp, get_offset(ctx, 1)); /* PC += off(1) if tmp == 0*/
++
++ /* if (--TCC < 0) goto out */
++ emit(ctx, daddiu, tcc, tcc, -1); /* tcc-- (delay slot) */
++ emit(ctx, bltz, tcc, get_offset(ctx, 1)); /* PC += off(1) if tcc < 0 */
++ /* (next insn delay slot) */
++ /* prog = ary->ptrs[ind] */
++ off = offsetof(struct bpf_array, ptrs);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, dsll, tmp, ind, 3); /* tmp = ind << 3 */
++ emit(ctx, daddu, tmp, tmp, ary); /* tmp += ary */
++ emit(ctx, ld, tmp, off, tmp); /* tmp = *(tmp + off) */
++
++ /* if (prog == 0) goto out */
++ emit(ctx, beqz, tmp, get_offset(ctx, 1)); /* PC += off(1) if tmp == 0*/
++ emit(ctx, nop); /* Delay slot */
++
++ /* func = prog->bpf_func + 8 (prologue skip offset) */
++ off = offsetof(struct bpf_prog, bpf_func);
++ if (off > 0x7fff)
++ return -1;
++ emit(ctx, ld, tmp, off, tmp); /* tmp = *(tmp + off) */
++ emit(ctx, daddiu, tmp, tmp, JIT_TCALL_SKIP); /* tmp += skip (4) */
++
++ /* goto func */
++ build_epilogue(ctx, tmp);
++ access_reg(ctx, JIT_REG_TC);
++ return 0;
++}
++
++/*
++ * Stack frame layout for a JITed program (stack grows down).
++ *
++ * Higher address : Previous stack frame :
++ * +===========================+ <--- MIPS sp before call
++ * | Callee-saved registers, |
++ * | including RA and FP |
++ * +---------------------------+ <--- eBPF FP (MIPS fp)
++ * | Local eBPF variables |
++ * | allocated by program |
++ * +---------------------------+
++ * | Reserved for caller-saved |
++ * | registers |
++ * Lower address +===========================+ <--- MIPS sp
++ */
++
++/* Build program prologue to set up the stack and registers */
++void build_prologue(struct jit_context *ctx)
++{
++ u8 fp = bpf2mips64[BPF_REG_FP];
++ u8 tc = bpf2mips64[JIT_REG_TC];
++ u8 zx = bpf2mips64[JIT_REG_ZX];
++ int stack, saved, locals, reserved;
++
++ /*
++ * The first instruction initializes the tail call count register.
++ * On a tail call, the calling function jumps into the prologue
++ * after this instruction.
++ */
++ emit(ctx, addiu, tc, MIPS_R_ZERO, min(MAX_TAIL_CALL_CNT + 1, 0xffff));
++
++ /* === Entry-point for tail calls === */
++
++ /*
++ * If the eBPF frame pointer and tail call count registers were
++ * accessed they must be preserved. Mark them as clobbered here
++ * to save and restore them on the stack as needed.
++ */
++ if (ctx->accessed & BIT(BPF_REG_FP))
++ clobber_reg(ctx, fp);
++ if (ctx->accessed & BIT(JIT_REG_TC))
++ clobber_reg(ctx, tc);
++ if (ctx->accessed & BIT(JIT_REG_ZX))
++ clobber_reg(ctx, zx);
++
++ /* Compute the stack space needed for callee-saved registers */
++ saved = hweight32(ctx->clobbered & JIT_CALLEE_REGS) * sizeof(u64);
++ saved = ALIGN(saved, MIPS_STACK_ALIGNMENT);
++
++ /* Stack space used by eBPF program local data */
++ locals = ALIGN(ctx->program->aux->stack_depth, MIPS_STACK_ALIGNMENT);
++
++ /*
++ * If we are emitting function calls, reserve extra stack space for
++ * caller-saved registers needed by the JIT. The required space is
++ * computed automatically during resource usage discovery (pass 1).
++ */
++ reserved = ctx->stack_used;
++
++ /* Allocate the stack frame */
++ stack = ALIGN(saved + locals + reserved, MIPS_STACK_ALIGNMENT);
++ if (stack)
++ emit(ctx, daddiu, MIPS_R_SP, MIPS_R_SP, -stack);
++
++ /* Store callee-saved registers on stack */
++ push_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0, stack - saved);
++
++ /* Initialize the eBPF frame pointer if accessed */
++ if (ctx->accessed & BIT(BPF_REG_FP))
++ emit(ctx, daddiu, fp, MIPS_R_SP, stack - saved);
++
++ /* Initialize the ePF JIT zero-extension register if accessed */
++ if (ctx->accessed & BIT(JIT_REG_ZX)) {
++ emit(ctx, daddiu, zx, MIPS_R_ZERO, -1);
++ emit(ctx, dsrl32, zx, zx, 0);
++ }
++
++ ctx->saved_size = saved;
++ ctx->stack_size = stack;
++}
++
++/* Build the program epilogue to restore the stack and registers */
++void build_epilogue(struct jit_context *ctx, int dest_reg)
++{
++ /* Restore callee-saved registers from stack */
++ pop_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0,
++ ctx->stack_size - ctx->saved_size);
++
++ /* Release the stack frame */
++ if (ctx->stack_size)
++ emit(ctx, daddiu, MIPS_R_SP, MIPS_R_SP, ctx->stack_size);
++
++ /* Jump to return address and sign-extend the 32-bit return value */
++ emit(ctx, jr, dest_reg);
++ emit(ctx, sll, MIPS_R_V0, MIPS_R_V0, 0); /* Delay slot */
++}
++
++/* Build one eBPF instruction */
++int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
++{
++ u8 dst = bpf2mips64[insn->dst_reg];
++ u8 src = bpf2mips64[insn->src_reg];
++ u8 code = insn->code;
++ s16 off = insn->off;
++ s32 imm = insn->imm;
++ s32 val, rel;
++ u8 alu, jmp;
++
++ switch (code) {
++ /* ALU operations */
++ /* dst = imm */
++ case BPF_ALU | BPF_MOV | BPF_K:
++ emit_mov_i(ctx, dst, imm);
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = src */
++ case BPF_ALU | BPF_MOV | BPF_X:
++ if (imm == 1) {
++ /* Special mov32 for zext */
++ emit_zext(ctx, dst);
++ } else {
++ emit_mov_r(ctx, dst, src);
++ emit_zext_ver(ctx, dst);
++ }
++ break;
++ /* dst = -dst */
++ case BPF_ALU | BPF_NEG:
++ emit_sext(ctx, dst, dst);
++ emit_alu_i(ctx, dst, 0, BPF_NEG);
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst & imm */
++ /* dst = dst | imm */
++ /* dst = dst ^ imm */
++ /* dst = dst << imm */
++ case BPF_ALU | BPF_OR | BPF_K:
++ case BPF_ALU | BPF_AND | BPF_K:
++ case BPF_ALU | BPF_XOR | BPF_K:
++ case BPF_ALU | BPF_LSH | BPF_K:
++ if (!valid_alu_i(BPF_OP(code), imm)) {
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
++ emit_alu_i(ctx, dst, val, alu);
++ }
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst >> imm */
++ /* dst = dst >> imm (arithmetic) */
++ /* dst = dst + imm */
++ /* dst = dst - imm */
++ /* dst = dst * imm */
++ /* dst = dst / imm */
++ /* dst = dst % imm */
++ case BPF_ALU | BPF_RSH | BPF_K:
++ case BPF_ALU | BPF_ARSH | BPF_K:
++ case BPF_ALU | BPF_ADD | BPF_K:
++ case BPF_ALU | BPF_SUB | BPF_K:
++ case BPF_ALU | BPF_MUL | BPF_K:
++ case BPF_ALU | BPF_DIV | BPF_K:
++ case BPF_ALU | BPF_MOD | BPF_K:
++ if (!valid_alu_i(BPF_OP(code), imm)) {
++ emit_sext(ctx, dst, dst);
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
++ emit_sext(ctx, dst, dst);
++ emit_alu_i(ctx, dst, val, alu);
++ }
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst & src */
++ /* dst = dst | src */
++ /* dst = dst ^ src */
++ /* dst = dst << src */
++ case BPF_ALU | BPF_AND | BPF_X:
++ case BPF_ALU | BPF_OR | BPF_X:
++ case BPF_ALU | BPF_XOR | BPF_X:
++ case BPF_ALU | BPF_LSH | BPF_X:
++ emit_alu_r(ctx, dst, src, BPF_OP(code));
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = dst >> src */
++ /* dst = dst >> src (arithmetic) */
++ /* dst = dst + src */
++ /* dst = dst - src */
++ /* dst = dst * src */
++ /* dst = dst / src */
++ /* dst = dst % src */
++ case BPF_ALU | BPF_RSH | BPF_X:
++ case BPF_ALU | BPF_ARSH | BPF_X:
++ case BPF_ALU | BPF_ADD | BPF_X:
++ case BPF_ALU | BPF_SUB | BPF_X:
++ case BPF_ALU | BPF_MUL | BPF_X:
++ case BPF_ALU | BPF_DIV | BPF_X:
++ case BPF_ALU | BPF_MOD | BPF_X:
++ emit_sext(ctx, dst, dst);
++ emit_sext(ctx, MIPS_R_T4, src);
++ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
++ emit_zext_ver(ctx, dst);
++ break;
++ /* dst = imm (64-bit) */
++ case BPF_ALU64 | BPF_MOV | BPF_K:
++ emit_mov_i(ctx, dst, imm);
++ break;
++ /* dst = src (64-bit) */
++ case BPF_ALU64 | BPF_MOV | BPF_X:
++ emit_mov_r(ctx, dst, src);
++ break;
++ /* dst = -dst (64-bit) */
++ case BPF_ALU64 | BPF_NEG:
++ emit_alu_i64(ctx, dst, 0, BPF_NEG);
++ break;
++ /* dst = dst & imm (64-bit) */
++ /* dst = dst | imm (64-bit) */
++ /* dst = dst ^ imm (64-bit) */
++ /* dst = dst << imm (64-bit) */
++ /* dst = dst >> imm (64-bit) */
++ /* dst = dst >> imm ((64-bit, arithmetic) */
++ /* dst = dst + imm (64-bit) */
++ /* dst = dst - imm (64-bit) */
++ /* dst = dst * imm (64-bit) */
++ /* dst = dst / imm (64-bit) */
++ /* dst = dst % imm (64-bit) */
++ case BPF_ALU64 | BPF_AND | BPF_K:
++ case BPF_ALU64 | BPF_OR | BPF_K:
++ case BPF_ALU64 | BPF_XOR | BPF_K:
++ case BPF_ALU64 | BPF_LSH | BPF_K:
++ case BPF_ALU64 | BPF_RSH | BPF_K:
++ case BPF_ALU64 | BPF_ARSH | BPF_K:
++ case BPF_ALU64 | BPF_ADD | BPF_K:
++ case BPF_ALU64 | BPF_SUB | BPF_K:
++ case BPF_ALU64 | BPF_MUL | BPF_K:
++ case BPF_ALU64 | BPF_DIV | BPF_K:
++ case BPF_ALU64 | BPF_MOD | BPF_K:
++ if (!valid_alu_i(BPF_OP(code), imm)) {
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_alu_r64(ctx, dst, MIPS_R_T4, BPF_OP(code));
++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
++ emit_alu_i64(ctx, dst, val, alu);
++ }
++ break;
++ /* dst = dst & src (64-bit) */
++ /* dst = dst | src (64-bit) */
++ /* dst = dst ^ src (64-bit) */
++ /* dst = dst << src (64-bit) */
++ /* dst = dst >> src (64-bit) */
++ /* dst = dst >> src (64-bit, arithmetic) */
++ /* dst = dst + src (64-bit) */
++ /* dst = dst - src (64-bit) */
++ /* dst = dst * src (64-bit) */
++ /* dst = dst / src (64-bit) */
++ /* dst = dst % src (64-bit) */
++ case BPF_ALU64 | BPF_AND | BPF_X:
++ case BPF_ALU64 | BPF_OR | BPF_X:
++ case BPF_ALU64 | BPF_XOR | BPF_X:
++ case BPF_ALU64 | BPF_LSH | BPF_X:
++ case BPF_ALU64 | BPF_RSH | BPF_X:
++ case BPF_ALU64 | BPF_ARSH | BPF_X:
++ case BPF_ALU64 | BPF_ADD | BPF_X:
++ case BPF_ALU64 | BPF_SUB | BPF_X:
++ case BPF_ALU64 | BPF_MUL | BPF_X:
++ case BPF_ALU64 | BPF_DIV | BPF_X:
++ case BPF_ALU64 | BPF_MOD | BPF_X:
++ emit_alu_r64(ctx, dst, src, BPF_OP(code));
++ break;
++ /* dst = htole(dst) */
++ /* dst = htobe(dst) */
++ case BPF_ALU | BPF_END | BPF_FROM_LE:
++ case BPF_ALU | BPF_END | BPF_FROM_BE:
++ if (BPF_SRC(code) ==
++#ifdef __BIG_ENDIAN
++ BPF_FROM_LE
++#else
++ BPF_FROM_BE
++#endif
++ )
++ emit_bswap_r64(ctx, dst, imm);
++ else
++ emit_trunc_r64(ctx, dst, imm);
++ break;
++ /* dst = imm64 */
++ case BPF_LD | BPF_IMM | BPF_DW:
++ emit_mov_i64(ctx, dst, (u32)imm | ((u64)insn[1].imm << 32));
++ return 1;
++ /* LDX: dst = *(size *)(src + off) */
++ case BPF_LDX | BPF_MEM | BPF_W:
++ case BPF_LDX | BPF_MEM | BPF_H:
++ case BPF_LDX | BPF_MEM | BPF_B:
++ case BPF_LDX | BPF_MEM | BPF_DW:
++ emit_ldx(ctx, dst, src, off, BPF_SIZE(code));
++ break;
++ /* ST: *(size *)(dst + off) = imm */
++ case BPF_ST | BPF_MEM | BPF_W:
++ case BPF_ST | BPF_MEM | BPF_H:
++ case BPF_ST | BPF_MEM | BPF_B:
++ case BPF_ST | BPF_MEM | BPF_DW:
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_stx(ctx, dst, MIPS_R_T4, off, BPF_SIZE(code));
++ break;
++ /* STX: *(size *)(dst + off) = src */
++ case BPF_STX | BPF_MEM | BPF_W:
++ case BPF_STX | BPF_MEM | BPF_H:
++ case BPF_STX | BPF_MEM | BPF_B:
++ case BPF_STX | BPF_MEM | BPF_DW:
++ emit_stx(ctx, dst, src, off, BPF_SIZE(code));
++ break;
++ /* Speculation barrier */
++ case BPF_ST | BPF_NOSPEC:
++ break;
++ /* Atomics */
++ case BPF_STX | BPF_XADD | BPF_W:
++ case BPF_STX | BPF_XADD | BPF_DW:
++ switch (imm) {
++ case BPF_ADD:
++ case BPF_AND:
++ case BPF_OR:
++ case BPF_XOR:
++ if (BPF_SIZE(code) == BPF_DW) {
++ emit_atomic_r64(ctx, dst, src, off, imm);
++ } else { /* 32-bit, no fetch */
++ emit_sext(ctx, MIPS_R_T4, src);
++ emit_atomic_r(ctx, dst, MIPS_R_T4, off, imm);
++ }
++ break;
++ default:
++ goto notyet;
++ }
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ /* PC += off if dst & src */
++ /* PC += off if dst > src */
++ /* PC += off if dst >= src */
++ /* PC += off if dst < src */
++ /* PC += off if dst <= src */
++ /* PC += off if dst > src (signed) */
++ /* PC += off if dst >= src (signed) */
++ /* PC += off if dst < src (signed) */
++ /* PC += off if dst <= src (signed) */
++ case BPF_JMP32 | BPF_JEQ | BPF_X:
++ case BPF_JMP32 | BPF_JNE | BPF_X:
++ case BPF_JMP32 | BPF_JSET | BPF_X:
++ case BPF_JMP32 | BPF_JGT | BPF_X:
++ case BPF_JMP32 | BPF_JGE | BPF_X:
++ case BPF_JMP32 | BPF_JLT | BPF_X:
++ case BPF_JMP32 | BPF_JLE | BPF_X:
++ case BPF_JMP32 | BPF_JSGT | BPF_X:
++ case BPF_JMP32 | BPF_JSGE | BPF_X:
++ case BPF_JMP32 | BPF_JSLT | BPF_X:
++ case BPF_JMP32 | BPF_JSLE | BPF_X:
++ if (off == 0)
++ break;
++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel);
++ emit_sext(ctx, MIPS_R_T4, dst); /* Sign-extended dst */
++ emit_sext(ctx, MIPS_R_T5, src); /* Sign-extended src */
++ emit_jmp_r(ctx, MIPS_R_T4, MIPS_R_T5, rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ /* PC += off if dst & imm */
++ /* PC += off if dst > imm */
++ /* PC += off if dst >= imm */
++ /* PC += off if dst < imm */
++ /* PC += off if dst <= imm */
++ /* PC += off if dst > imm (signed) */
++ /* PC += off if dst >= imm (signed) */
++ /* PC += off if dst < imm (signed) */
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JMP32 | BPF_JEQ | BPF_K:
++ case BPF_JMP32 | BPF_JNE | BPF_K:
++ case BPF_JMP32 | BPF_JSET | BPF_K:
++ case BPF_JMP32 | BPF_JGT | BPF_K:
++ case BPF_JMP32 | BPF_JGE | BPF_K:
++ case BPF_JMP32 | BPF_JLT | BPF_K:
++ case BPF_JMP32 | BPF_JLE | BPF_K:
++ case BPF_JMP32 | BPF_JSGT | BPF_K:
++ case BPF_JMP32 | BPF_JSGE | BPF_K:
++ case BPF_JMP32 | BPF_JSLT | BPF_K:
++ case BPF_JMP32 | BPF_JSLE | BPF_K:
++ if (off == 0)
++ break;
++ setup_jmp_i(ctx, imm, 32, BPF_OP(code), off, &jmp, &rel);
++ emit_sext(ctx, MIPS_R_T4, dst); /* Sign-extended dst */
++ if (valid_jmp_i(jmp, imm)) {
++ emit_jmp_i(ctx, MIPS_R_T4, imm, rel, jmp);
++ } else {
++ /* Move large immediate to register, sign-extended */
++ emit_mov_i(ctx, MIPS_R_T5, imm);
++ emit_jmp_r(ctx, MIPS_R_T4, MIPS_R_T5, rel, jmp);
++ }
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == src */
++ /* PC += off if dst != src */
++ /* PC += off if dst & src */
++ /* PC += off if dst > src */
++ /* PC += off if dst >= src */
++ /* PC += off if dst < src */
++ /* PC += off if dst <= src */
++ /* PC += off if dst > src (signed) */
++ /* PC += off if dst >= src (signed) */
++ /* PC += off if dst < src (signed) */
++ /* PC += off if dst <= src (signed) */
++ case BPF_JMP | BPF_JEQ | BPF_X:
++ case BPF_JMP | BPF_JNE | BPF_X:
++ case BPF_JMP | BPF_JSET | BPF_X:
++ case BPF_JMP | BPF_JGT | BPF_X:
++ case BPF_JMP | BPF_JGE | BPF_X:
++ case BPF_JMP | BPF_JLT | BPF_X:
++ case BPF_JMP | BPF_JLE | BPF_X:
++ case BPF_JMP | BPF_JSGT | BPF_X:
++ case BPF_JMP | BPF_JSGE | BPF_X:
++ case BPF_JMP | BPF_JSLT | BPF_X:
++ case BPF_JMP | BPF_JSLE | BPF_X:
++ if (off == 0)
++ break;
++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel);
++ emit_jmp_r(ctx, dst, src, rel, jmp);
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off if dst == imm */
++ /* PC += off if dst != imm */
++ /* PC += off if dst & imm */
++ /* PC += off if dst > imm */
++ /* PC += off if dst >= imm */
++ /* PC += off if dst < imm */
++ /* PC += off if dst <= imm */
++ /* PC += off if dst > imm (signed) */
++ /* PC += off if dst >= imm (signed) */
++ /* PC += off if dst < imm (signed) */
++ /* PC += off if dst <= imm (signed) */
++ case BPF_JMP | BPF_JEQ | BPF_K:
++ case BPF_JMP | BPF_JNE | BPF_K:
++ case BPF_JMP | BPF_JSET | BPF_K:
++ case BPF_JMP | BPF_JGT | BPF_K:
++ case BPF_JMP | BPF_JGE | BPF_K:
++ case BPF_JMP | BPF_JLT | BPF_K:
++ case BPF_JMP | BPF_JLE | BPF_K:
++ case BPF_JMP | BPF_JSGT | BPF_K:
++ case BPF_JMP | BPF_JSGE | BPF_K:
++ case BPF_JMP | BPF_JSLT | BPF_K:
++ case BPF_JMP | BPF_JSLE | BPF_K:
++ if (off == 0)
++ break;
++ setup_jmp_i(ctx, imm, 64, BPF_OP(code), off, &jmp, &rel);
++ if (valid_jmp_i(jmp, imm)) {
++ emit_jmp_i(ctx, dst, imm, rel, jmp);
++ } else {
++ /* Move large immediate to register */
++ emit_mov_i(ctx, MIPS_R_T4, imm);
++ emit_jmp_r(ctx, dst, MIPS_R_T4, rel, jmp);
++ }
++ if (finish_jmp(ctx, jmp, off) < 0)
++ goto toofar;
++ break;
++ /* PC += off */
++ case BPF_JMP | BPF_JA:
++ if (off == 0)
++ break;
++ if (emit_ja(ctx, off) < 0)
++ goto toofar;
++ break;
++ /* Tail call */
++ case BPF_JMP | BPF_TAIL_CALL:
++ if (emit_tail_call(ctx) < 0)
++ goto invalid;
++ break;
++ /* Function call */
++ case BPF_JMP | BPF_CALL:
++ if (emit_call(ctx, insn) < 0)
++ goto invalid;
++ break;
++ /* Function return */
++ case BPF_JMP | BPF_EXIT:
++ /*
++ * Optimization: when last instruction is EXIT
++ * simply continue to epilogue.
++ */
++ if (ctx->bpf_index == ctx->program->len - 1)
++ break;
++ if (emit_exit(ctx) < 0)
++ goto toofar;
++ break;
++
++ default:
++invalid:
++ pr_err_once("unknown opcode %02x\n", code);
++ return -EINVAL;
++notyet:
++ pr_info_once("*** NOT YET: opcode %02x ***\n", code);
++ return -EFAULT;
++toofar:
++ pr_info_once("*** TOO FAR: jump at %u opcode %02x ***\n",
++ ctx->bpf_index, code);
++ return -E2BIG;
++ }
++ return 0;
++}
diff --git a/target/linux/generic/backport-6.1/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch b/target/linux/generic/backport-6.1/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch
new file mode 100644
index 0000000000..63553ebe58
--- /dev/null
+++ b/target/linux/generic/backport-6.1/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch
@@ -0,0 +1,120 @@
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:06 +0200
+Subject: [PATCH] mips: bpf: Add JIT workarounds for CPU errata
+
+This patch adds workarounds for the following CPU errata to the MIPS
+eBPF JIT, if enabled in the kernel configuration.
+
+ - R10000 ll/sc weak ordering
+ - Loongson-3 ll/sc weak ordering
+ - Loongson-2F jump hang
+
+The Loongson-2F nop errata is implemented in uasm, which the JIT uses,
+so no additional mitigations are needed for that.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
+---
+
+--- a/arch/mips/net/bpf_jit_comp.c
++++ b/arch/mips/net/bpf_jit_comp.c
+@@ -404,6 +404,7 @@ void emit_alu_r(struct jit_context *ctx,
+ /* Atomic read-modify-write (32-bit) */
+ void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code)
+ {
++ LLSC_sync(ctx);
+ emit(ctx, ll, MIPS_R_T9, off, dst);
+ switch (code) {
+ case BPF_ADD:
+@@ -420,18 +421,19 @@ void emit_atomic_r(struct jit_context *c
+ break;
+ }
+ emit(ctx, sc, MIPS_R_T8, off, dst);
+- emit(ctx, beqz, MIPS_R_T8, -16);
++ emit(ctx, LLSC_beqz, MIPS_R_T8, -16 - LLSC_offset);
+ emit(ctx, nop); /* Delay slot */
+ }
+
+ /* Atomic compare-and-exchange (32-bit) */
+ void emit_cmpxchg_r(struct jit_context *ctx, u8 dst, u8 src, u8 res, s16 off)
+ {
++ LLSC_sync(ctx);
+ emit(ctx, ll, MIPS_R_T9, off, dst);
+ emit(ctx, bne, MIPS_R_T9, res, 12);
+ emit(ctx, move, MIPS_R_T8, src); /* Delay slot */
+ emit(ctx, sc, MIPS_R_T8, off, dst);
+- emit(ctx, beqz, MIPS_R_T8, -20);
++ emit(ctx, LLSC_beqz, MIPS_R_T8, -20 - LLSC_offset);
+ emit(ctx, move, res, MIPS_R_T9); /* Delay slot */
+ clobber_reg(ctx, res);
+ }
+--- a/arch/mips/net/bpf_jit_comp.h
++++ b/arch/mips/net/bpf_jit_comp.h
+@@ -87,7 +87,7 @@ struct jit_context {
+ };
+
+ /* Emit the instruction if the JIT memory space has been allocated */
+-#define emit(ctx, func, ...) \
++#define __emit(ctx, func, ...) \
+ do { \
+ if ((ctx)->target != NULL) { \
+ u32 *p = &(ctx)->target[ctx->jit_index]; \
+@@ -95,6 +95,30 @@ do { \
+ } \
+ (ctx)->jit_index++; \
+ } while (0)
++#define emit(...) __emit(__VA_ARGS__)
++
++/* Workaround for R10000 ll/sc errata */
++#ifdef CONFIG_WAR_R10000
++#define LLSC_beqz beqzl
++#else
++#define LLSC_beqz beqz
++#endif
++
++/* Workaround for Loongson-3 ll/sc errata */
++#ifdef CONFIG_CPU_LOONGSON3_WORKAROUNDS
++#define LLSC_sync(ctx) emit(ctx, sync, 0)
++#define LLSC_offset 4
++#else
++#define LLSC_sync(ctx)
++#define LLSC_offset 0
++#endif
++
++/* Workaround for Loongson-2F jump errata */
++#ifdef CONFIG_CPU_JUMP_WORKAROUNDS
++#define JALR_MASK 0xffffffffcfffffffULL
++#else
++#define JALR_MASK (~0ULL)
++#endif
+
+ /*
+ * Mark a BPF register as accessed, it needs to be
+--- a/arch/mips/net/bpf_jit_comp64.c
++++ b/arch/mips/net/bpf_jit_comp64.c
+@@ -375,6 +375,7 @@ static void emit_atomic_r64(struct jit_c
+ u8 t1 = MIPS_R_T6;
+ u8 t2 = MIPS_R_T7;
+
++ LLSC_sync(ctx);
+ emit(ctx, lld, t1, off, dst);
+ switch (code) {
+ case BPF_ADD:
+@@ -391,7 +392,7 @@ static void emit_atomic_r64(struct jit_c
+ break;
+ }
+ emit(ctx, scd, t2, off, dst);
+- emit(ctx, beqz, t2, -16);
++ emit(ctx, LLSC_beqz, t2, -16 - LLSC_offset);
+ emit(ctx, nop); /* Delay slot */
+ }
+
+@@ -414,7 +415,7 @@ static int emit_call(struct jit_context
+ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0);
+
+ /* Emit function call */
+- emit_mov_i64(ctx, tmp, addr);
++ emit_mov_i64(ctx, tmp, addr & JALR_MASK);
+ emit(ctx, jalr, MIPS_R_RA, tmp);
+ emit(ctx, nop); /* Delay slot */
+
diff --git a/target/linux/generic/backport-6.1/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch b/target/linux/generic/backport-6.1/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch
new file mode 100644
index 0000000000..14763c1211
--- /dev/null
+++ b/target/linux/generic/backport-6.1/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch
@@ -0,0 +1,61 @@
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:07 +0200
+Subject: [PATCH] mips: bpf: Enable eBPF JITs
+
+This patch enables the new eBPF JITs for 32-bit and 64-bit MIPS. It also
+disables the old cBPF JIT to so cBPF programs are converted to use the
+new JIT.
+
+Workarounds for R4000 CPU errata are not implemented by the JIT, so the
+JIT is disabled if any of those workarounds are configured.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -3431,6 +3431,7 @@ S: Supported
+ F: arch/arm64/net/
+
+ BPF JIT for MIPS (32-BIT AND 64-BIT)
++M: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+ M: Paul Burton <paulburton@kernel.org>
+ L: netdev@vger.kernel.org
+ L: bpf@vger.kernel.org
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -57,7 +57,6 @@ config MIPS
+ select HAVE_ARCH_TRACEHOOK
+ select HAVE_ARCH_TRANSPARENT_HUGEPAGE if CPU_SUPPORTS_HUGEPAGES
+ select HAVE_ASM_MODVERSIONS
+- select HAVE_CBPF_JIT if !64BIT && !CPU_MICROMIPS
+ select HAVE_CONTEXT_TRACKING
+ select HAVE_TIF_NOHZ
+ select HAVE_C_RECORDMCOUNT
+@@ -65,7 +64,10 @@ config MIPS
+ select HAVE_DEBUG_STACKOVERFLOW
+ select HAVE_DMA_CONTIGUOUS
+ select HAVE_DYNAMIC_FTRACE
+- select HAVE_EBPF_JIT if 64BIT && !CPU_MICROMIPS && TARGET_ISA_REV >= 2
++ select HAVE_EBPF_JIT if !CPU_MICROMIPS && \
++ !CPU_DADDI_WORKAROUNDS && \
++ !CPU_R4000_WORKAROUNDS && \
++ !CPU_R4400_WORKAROUNDS
+ select HAVE_EXIT_THREAD
+ select HAVE_FAST_GUP
+ select HAVE_FTRACE_MCOUNT_RECORD
+--- a/arch/mips/net/Makefile
++++ b/arch/mips/net/Makefile
+@@ -2,9 +2,10 @@
+ # MIPS networking code
+
+ obj-$(CONFIG_MIPS_CBPF_JIT) += bpf_jit.o bpf_jit_asm.o
++obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp.o
+
+ ifeq ($(CONFIG_32BIT),y)
+- obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp.o bpf_jit_comp32.o
++ obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp32.o
+ else
+- obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o
++ obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp64.o
+ endif
diff --git a/target/linux/generic/backport-6.1/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch b/target/linux/generic/backport-6.1/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch
new file mode 100644
index 0000000000..e25c336831
--- /dev/null
+++ b/target/linux/generic/backport-6.1/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch
@@ -0,0 +1,387 @@
+From: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+Date: Tue, 5 Oct 2021 18:54:08 +0200
+Subject: [PATCH] mips: bpf: Remove old BPF JIT implementations
+
+This patch removes the old 32-bit cBPF and 64-bit eBPF JIT implementations.
+They are replaced by a new eBPF implementation that supports both 32-bit
+and 64-bit MIPS CPUs.
+
+Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com>
+---
+ delete mode 100644 arch/mips/net/bpf_jit.c
+ delete mode 100644 arch/mips/net/bpf_jit.h
+ delete mode 100644 arch/mips/net/bpf_jit_asm.S
+ delete mode 100644 arch/mips/net/ebpf_jit.c
+
+--- a/arch/mips/net/bpf_jit.h
++++ /dev/null
+@@ -1,81 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-only */
+-/*
+- * Just-In-Time compiler for BPF filters on MIPS
+- *
+- * Copyright (c) 2014 Imagination Technologies Ltd.
+- * Author: Markos Chandras <markos.chandras@imgtec.com>
+- */
+-
+-#ifndef BPF_JIT_MIPS_OP_H
+-#define BPF_JIT_MIPS_OP_H
+-
+-/* Registers used by JIT */
+-#define MIPS_R_ZERO 0
+-#define MIPS_R_V0 2
+-#define MIPS_R_A0 4
+-#define MIPS_R_A1 5
+-#define MIPS_R_T4 12
+-#define MIPS_R_T5 13
+-#define MIPS_R_T6 14
+-#define MIPS_R_T7 15
+-#define MIPS_R_S0 16
+-#define MIPS_R_S1 17
+-#define MIPS_R_S2 18
+-#define MIPS_R_S3 19
+-#define MIPS_R_S4 20
+-#define MIPS_R_S5 21
+-#define MIPS_R_S6 22
+-#define MIPS_R_S7 23
+-#define MIPS_R_SP 29
+-#define MIPS_R_RA 31
+-
+-/* Conditional codes */
+-#define MIPS_COND_EQ 0x1
+-#define MIPS_COND_GE (0x1 << 1)
+-#define MIPS_COND_GT (0x1 << 2)
+-#define MIPS_COND_NE (0x1 << 3)
+-#define MIPS_COND_ALL (0x1 << 4)
+-/* Conditionals on X register or K immediate */
+-#define MIPS_COND_X (0x1 << 5)
+-#define MIPS_COND_K (0x1 << 6)
+-
+-#define r_ret MIPS_R_V0
+-
+-/*
+- * Use 2 scratch registers to avoid pipeline interlocks.
+- * There is no overhead during epilogue and prologue since
+- * any of the $s0-$s6 registers will only be preserved if
+- * they are going to actually be used.
+- */
+-#define r_skb_hl MIPS_R_S0 /* skb header length */
+-#define r_skb_data MIPS_R_S1 /* skb actual data */
+-#define r_off MIPS_R_S2
+-#define r_A MIPS_R_S3
+-#define r_X MIPS_R_S4
+-#define r_skb MIPS_R_S5
+-#define r_M MIPS_R_S6
+-#define r_skb_len MIPS_R_S7
+-#define r_s0 MIPS_R_T4 /* scratch reg 1 */
+-#define r_s1 MIPS_R_T5 /* scratch reg 2 */
+-#define r_tmp_imm MIPS_R_T6 /* No need to preserve this */
+-#define r_tmp MIPS_R_T7 /* No need to preserve this */
+-#define r_zero MIPS_R_ZERO
+-#define r_sp MIPS_R_SP
+-#define r_ra MIPS_R_RA
+-
+-#ifndef __ASSEMBLY__
+-
+-/* Declare ASM helpers */
+-
+-#define DECLARE_LOAD_FUNC(func) \
+- extern u8 func(unsigned long *skb, int offset); \
+- extern u8 func##_negative(unsigned long *skb, int offset); \
+- extern u8 func##_positive(unsigned long *skb, int offset)
+-
+-DECLARE_LOAD_FUNC(sk_load_word);
+-DECLARE_LOAD_FUNC(sk_load_half);
+-DECLARE_LOAD_FUNC(sk_load_byte);
+-
+-#endif
+-
+-#endif /* BPF_JIT_MIPS_OP_H */
+--- a/arch/mips/net/bpf_jit_asm.S
++++ /dev/null
+@@ -1,285 +0,0 @@
+-/*
+- * bpf_jib_asm.S: Packet/header access helper functions for MIPS/MIPS64 BPF
+- * compiler.
+- *
+- * Copyright (C) 2015 Imagination Technologies Ltd.
+- * Author: Markos Chandras <markos.chandras@imgtec.com>
+- *
+- * This program is free software; you can redistribute it and/or modify it
+- * under the terms of the GNU General Public License as published by the
+- * Free Software Foundation; version 2 of the License.
+- */
+-
+-#include <asm/asm.h>
+-#include <asm/isa-rev.h>
+-#include <asm/regdef.h>
+-#include "bpf_jit.h"
+-
+-/* ABI
+- *
+- * r_skb_hl skb header length
+- * r_skb_data skb data
+- * r_off(a1) offset register
+- * r_A BPF register A
+- * r_X PF register X
+- * r_skb(a0) *skb
+- * r_M *scratch memory
+- * r_skb_le skb length
+- * r_s0 Scratch register 0
+- * r_s1 Scratch register 1
+- *
+- * On entry:
+- * a0: *skb
+- * a1: offset (imm or imm + X)
+- *
+- * All non-BPF-ABI registers are free for use. On return, we only
+- * care about r_ret. The BPF-ABI registers are assumed to remain
+- * unmodified during the entire filter operation.
+- */
+-
+-#define skb a0
+-#define offset a1
+-#define SKF_LL_OFF (-0x200000) /* Can't include linux/filter.h in assembly */
+-
+- /* We know better :) so prevent assembler reordering etc */
+- .set noreorder
+-
+-#define is_offset_negative(TYPE) \
+- /* If offset is negative we have more work to do */ \
+- slti t0, offset, 0; \
+- bgtz t0, bpf_slow_path_##TYPE##_neg; \
+- /* Be careful what follows in DS. */
+-
+-#define is_offset_in_header(SIZE, TYPE) \
+- /* Reading from header? */ \
+- addiu $r_s0, $r_skb_hl, -SIZE; \
+- slt t0, $r_s0, offset; \
+- bgtz t0, bpf_slow_path_##TYPE; \
+-
+-LEAF(sk_load_word)
+- is_offset_negative(word)
+-FEXPORT(sk_load_word_positive)
+- is_offset_in_header(4, word)
+- /* Offset within header boundaries */
+- PTR_ADDU t1, $r_skb_data, offset
+- .set reorder
+- lw $r_A, 0(t1)
+- .set noreorder
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-# if MIPS_ISA_REV >= 2
+- wsbh t0, $r_A
+- rotr $r_A, t0, 16
+-# else
+- sll t0, $r_A, 24
+- srl t1, $r_A, 24
+- srl t2, $r_A, 8
+- or t0, t0, t1
+- andi t2, t2, 0xff00
+- andi t1, $r_A, 0xff00
+- or t0, t0, t2
+- sll t1, t1, 8
+- or $r_A, t0, t1
+-# endif
+-#endif
+- jr $r_ra
+- move $r_ret, zero
+- END(sk_load_word)
+-
+-LEAF(sk_load_half)
+- is_offset_negative(half)
+-FEXPORT(sk_load_half_positive)
+- is_offset_in_header(2, half)
+- /* Offset within header boundaries */
+- PTR_ADDU t1, $r_skb_data, offset
+- lhu $r_A, 0(t1)
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-# if MIPS_ISA_REV >= 2
+- wsbh $r_A, $r_A
+-# else
+- sll t0, $r_A, 8
+- srl t1, $r_A, 8
+- andi t0, t0, 0xff00
+- or $r_A, t0, t1
+-# endif
+-#endif
+- jr $r_ra
+- move $r_ret, zero
+- END(sk_load_half)
+-
+-LEAF(sk_load_byte)
+- is_offset_negative(byte)
+-FEXPORT(sk_load_byte_positive)
+- is_offset_in_header(1, byte)
+- /* Offset within header boundaries */
+- PTR_ADDU t1, $r_skb_data, offset
+- lbu $r_A, 0(t1)
+- jr $r_ra
+- move $r_ret, zero
+- END(sk_load_byte)
+-
+-/*
+- * call skb_copy_bits:
+- * (prototype in linux/skbuff.h)
+- *
+- * int skb_copy_bits(sk_buff *skb, int offset, void *to, int len)
+- *
+- * o32 mandates we leave 4 spaces for argument registers in case
+- * the callee needs to use them. Even though we don't care about
+- * the argument registers ourselves, we need to allocate that space
+- * to remain ABI compliant since the callee may want to use that space.
+- * We also allocate 2 more spaces for $r_ra and our return register (*to).
+- *
+- * n64 is a bit different. The *caller* will allocate the space to preserve
+- * the arguments. So in 64-bit kernels, we allocate the 4-arg space for no
+- * good reason but it does not matter that much really.
+- *
+- * (void *to) is returned in r_s0
+- *
+- */
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-#define DS_OFFSET(SIZE) (4 * SZREG)
+-#else
+-#define DS_OFFSET(SIZE) ((4 * SZREG) + (4 - SIZE))
+-#endif
+-#define bpf_slow_path_common(SIZE) \
+- /* Quick check. Are we within reasonable boundaries? */ \
+- LONG_ADDIU $r_s1, $r_skb_len, -SIZE; \
+- sltu $r_s0, offset, $r_s1; \
+- beqz $r_s0, fault; \
+- /* Load 4th argument in DS */ \
+- LONG_ADDIU a3, zero, SIZE; \
+- PTR_ADDIU $r_sp, $r_sp, -(6 * SZREG); \
+- PTR_LA t0, skb_copy_bits; \
+- PTR_S $r_ra, (5 * SZREG)($r_sp); \
+- /* Assign low slot to a2 */ \
+- PTR_ADDIU a2, $r_sp, DS_OFFSET(SIZE); \
+- jalr t0; \
+- /* Reset our destination slot (DS but it's ok) */ \
+- INT_S zero, (4 * SZREG)($r_sp); \
+- /* \
+- * skb_copy_bits returns 0 on success and -EFAULT \
+- * on error. Our data live in a2. Do not bother with \
+- * our data if an error has been returned. \
+- */ \
+- /* Restore our frame */ \
+- PTR_L $r_ra, (5 * SZREG)($r_sp); \
+- INT_L $r_s0, (4 * SZREG)($r_sp); \
+- bltz v0, fault; \
+- PTR_ADDIU $r_sp, $r_sp, 6 * SZREG; \
+- move $r_ret, zero; \
+-
+-NESTED(bpf_slow_path_word, (6 * SZREG), $r_sp)
+- bpf_slow_path_common(4)
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-# if MIPS_ISA_REV >= 2
+- wsbh t0, $r_s0
+- jr $r_ra
+- rotr $r_A, t0, 16
+-# else
+- sll t0, $r_s0, 24
+- srl t1, $r_s0, 24
+- srl t2, $r_s0, 8
+- or t0, t0, t1
+- andi t2, t2, 0xff00
+- andi t1, $r_s0, 0xff00
+- or t0, t0, t2
+- sll t1, t1, 8
+- jr $r_ra
+- or $r_A, t0, t1
+-# endif
+-#else
+- jr $r_ra
+- move $r_A, $r_s0
+-#endif
+-
+- END(bpf_slow_path_word)
+-
+-NESTED(bpf_slow_path_half, (6 * SZREG), $r_sp)
+- bpf_slow_path_common(2)
+-#ifdef CONFIG_CPU_LITTLE_ENDIAN
+-# if MIPS_ISA_REV >= 2
+- jr $r_ra
+- wsbh $r_A, $r_s0
+-# else
+- sll t0, $r_s0, 8
+- andi t1, $r_s0, 0xff00
+- andi t0, t0, 0xff00
+- srl t1, t1, 8
+- jr $r_ra
+- or $r_A, t0, t1
+-# endif
+-#else
+- jr $r_ra
+- move $r_A, $r_s0
+-#endif
+-
+- END(bpf_slow_path_half)
+-
+-NESTED(bpf_slow_path_byte, (6 * SZREG), $r_sp)
+- bpf_slow_path_common(1)
+- jr $r_ra
+- move $r_A, $r_s0
+-
+- END(bpf_slow_path_byte)
+-
+-/*
+- * Negative entry points
+- */
+- .macro bpf_is_end_of_data
+- li t0, SKF_LL_OFF
+- /* Reading link layer data? */
+- slt t1, offset, t0
+- bgtz t1, fault
+- /* Be careful what follows in DS. */
+- .endm
+-/*
+- * call skb_copy_bits:
+- * (prototype in linux/filter.h)
+- *
+- * void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb,
+- * int k, unsigned int size)
+- *
+- * see above (bpf_slow_path_common) for ABI restrictions
+- */
+-#define bpf_negative_common(SIZE) \
+- PTR_ADDIU $r_sp, $r_sp, -(6 * SZREG); \
+- PTR_LA t0, bpf_internal_load_pointer_neg_helper; \
+- PTR_S $r_ra, (5 * SZREG)($r_sp); \
+- jalr t0; \
+- li a2, SIZE; \
+- PTR_L $r_ra, (5 * SZREG)($r_sp); \
+- /* Check return pointer */ \
+- beqz v0, fault; \
+- PTR_ADDIU $r_sp, $r_sp, 6 * SZREG; \
+- /* Preserve our pointer */ \
+- move $r_s0, v0; \
+- /* Set return value */ \
+- move $r_ret, zero; \
+-
+-bpf_slow_path_word_neg:
+- bpf_is_end_of_data
+-NESTED(sk_load_word_negative, (6 * SZREG), $r_sp)
+- bpf_negative_common(4)
+- jr $r_ra
+- lw $r_A, 0($r_s0)
+- END(sk_load_word_negative)
+-
+-bpf_slow_path_half_neg:
+- bpf_is_end_of_data
+-NESTED(sk_load_half_negative, (6 * SZREG), $r_sp)
+- bpf_negative_common(2)
+- jr $r_ra
+- lhu $r_A, 0($r_s0)
+- END(sk_load_half_negative)
+-
+-bpf_slow_path_byte_neg:
+- bpf_is_end_of_data
+-NESTED(sk_load_byte_negative, (6 * SZREG), $r_sp)
+- bpf_negative_common(1)
+- jr $r_ra
+- lbu $r_A, 0($r_s0)
+- END(sk_load_byte_negative)
+-
+-fault:
+- jr $r_ra
+- addiu $r_ret, zero, 1
diff --git a/target/linux/generic/backport-6.1/080-v5.17-clk-gate-Add-devm_clk_hw_register_gate.patch b/target/linux/generic/backport-6.1/080-v5.17-clk-gate-Add-devm_clk_hw_register_gate.patch
new file mode 100644
index 0000000000..51c23b6e34
--- /dev/null
+++ b/target/linux/generic/backport-6.1/080-v5.17-clk-gate-Add-devm_clk_hw_register_gate.patch
@@ -0,0 +1,105 @@
+From 815f0e738a8d5663a02350e2580706829144a722 Mon Sep 17 00:00:00 2001
+From: Horatiu Vultur <horatiu.vultur@microchip.com>
+Date: Wed, 3 Nov 2021 09:50:59 +0100
+Subject: [PATCH] clk: gate: Add devm_clk_hw_register_gate()
+
+Add devm_clk_hw_register_gate() - devres-managed version of
+clk_hw_register_gate()
+
+Suggested-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
+Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
+Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
+Link: https://lore.kernel.org/r/20211103085102.1656081-2-horatiu.vultur@microchip.com
+---
+ drivers/clk/clk-gate.c | 35 +++++++++++++++++++++++++++++++++++
+ include/linux/clk-provider.h | 23 +++++++++++++++++++++++
+ 2 files changed, 58 insertions(+)
+
+--- a/drivers/clk/clk-gate.c
++++ b/drivers/clk/clk-gate.c
+@@ -7,6 +7,7 @@
+ */
+
+ #include <linux/clk-provider.h>
++#include <linux/device.h>
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/io.h>
+@@ -222,3 +223,37 @@ void clk_hw_unregister_gate(struct clk_h
+ kfree(gate);
+ }
+ EXPORT_SYMBOL_GPL(clk_hw_unregister_gate);
++
++static void devm_clk_hw_release_gate(struct device *dev, void *res)
++{
++ clk_hw_unregister_gate(*(struct clk_hw **)res);
++}
++
++struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
++ struct device_node *np, const char *name,
++ const char *parent_name, const struct clk_hw *parent_hw,
++ const struct clk_parent_data *parent_data,
++ unsigned long flags,
++ void __iomem *reg, u8 bit_idx,
++ u8 clk_gate_flags, spinlock_t *lock)
++{
++ struct clk_hw **ptr, *hw;
++
++ ptr = devres_alloc(devm_clk_hw_release_gate, sizeof(*ptr), GFP_KERNEL);
++ if (!ptr)
++ return ERR_PTR(-ENOMEM);
++
++ hw = __clk_hw_register_gate(dev, np, name, parent_name, parent_hw,
++ parent_data, flags, reg, bit_idx,
++ clk_gate_flags, lock);
++
++ if (!IS_ERR(hw)) {
++ *ptr = hw;
++ devres_add(dev, ptr);
++ } else {
++ devres_free(ptr);
++ }
++
++ return hw;
++}
++EXPORT_SYMBOL_GPL(__devm_clk_hw_register_gate);
+--- a/include/linux/clk-provider.h
++++ b/include/linux/clk-provider.h
+@@ -490,6 +490,13 @@ struct clk_hw *__clk_hw_register_gate(st
+ unsigned long flags,
+ void __iomem *reg, u8 bit_idx,
+ u8 clk_gate_flags, spinlock_t *lock);
++struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
++ struct device_node *np, const char *name,
++ const char *parent_name, const struct clk_hw *parent_hw,
++ const struct clk_parent_data *parent_data,
++ unsigned long flags,
++ void __iomem *reg, u8 bit_idx,
++ u8 clk_gate_flags, spinlock_t *lock);
+ struct clk *clk_register_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 bit_idx,
+@@ -544,6 +551,22 @@ struct clk *clk_register_gate(struct dev
+ __clk_hw_register_gate((dev), NULL, (name), NULL, NULL, (parent_data), \
+ (flags), (reg), (bit_idx), \
+ (clk_gate_flags), (lock))
++/**
++ * devm_clk_hw_register_gate - register a gate clock with the clock framework
++ * @dev: device that is registering this clock
++ * @name: name of this clock
++ * @parent_name: name of this clock's parent
++ * @flags: framework-specific flags for this clock
++ * @reg: register address to control gating of this clock
++ * @bit_idx: which bit in the register controls gating of this clock
++ * @clk_gate_flags: gate-specific flags for this clock
++ * @lock: shared register lock for this clock
++ */
++#define devm_clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx,\
++ clk_gate_flags, lock) \
++ __devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \
++ NULL, (flags), (reg), (bit_idx), \
++ (clk_gate_flags), (lock))
+ void clk_unregister_gate(struct clk *clk);
+ void clk_hw_unregister_gate(struct clk_hw *hw);
+ int clk_gate_is_enabled(struct clk_hw *hw);
diff --git a/target/linux/generic/backport-6.1/081-v5.17-regmap-allow-to-define-reg_update_bits-for-no-bus.patch b/target/linux/generic/backport-6.1/081-v5.17-regmap-allow-to-define-reg_update_bits-for-no-bus.patch
new file mode 100644
index 0000000000..e4c0833ae7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/081-v5.17-regmap-allow-to-define-reg_update_bits-for-no-bus.patch
@@ -0,0 +1,52 @@
+From 02d6fdecb9c38de19065f6bed8d5214556fd061d Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 4 Nov 2021 16:00:40 +0100
+Subject: regmap: allow to define reg_update_bits for no bus configuration
+
+Some device requires a special handling for reg_update_bits and can't use
+the normal regmap read write logic. An example is when locking is
+handled by the device and rmw operations requires to do atomic operations.
+Allow to declare a dedicated function in regmap_config for
+reg_update_bits in no bus configuration.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Link: https://lore.kernel.org/r/20211104150040.1260-1-ansuelsmth@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/base/regmap/regmap.c | 1 +
+ include/linux/regmap.h | 7 +++++++
+ 2 files changed, 8 insertions(+)
+
+--- a/drivers/base/regmap/regmap.c
++++ b/drivers/base/regmap/regmap.c
+@@ -877,6 +877,7 @@ struct regmap *__regmap_init(struct devi
+ if (!bus) {
+ map->reg_read = config->reg_read;
+ map->reg_write = config->reg_write;
++ map->reg_update_bits = config->reg_update_bits;
+
+ map->defer_caching = false;
+ goto skip_format_initialization;
+--- a/include/linux/regmap.h
++++ b/include/linux/regmap.h
+@@ -290,6 +290,11 @@ typedef void (*regmap_unlock)(void *);
+ * read operation on a bus such as SPI, I2C, etc. Most of the
+ * devices do not need this.
+ * @reg_write: Same as above for writing.
++ * @reg_update_bits: Optional callback that if filled will be used to perform
++ * all the update_bits(rmw) operation. Should only be provided
++ * if the function require special handling with lock and reg
++ * handling and the operation cannot be represented as a simple
++ * update_bits operation on a bus such as SPI, I2C, etc.
+ * @fast_io: Register IO is fast. Use a spinlock instead of a mutex
+ * to perform locking. This field is ignored if custom lock/unlock
+ * functions are used (see fields lock/unlock of struct regmap_config).
+@@ -372,6 +377,8 @@ struct regmap_config {
+
+ int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
+ int (*reg_write)(void *context, unsigned int reg, unsigned int val);
++ int (*reg_update_bits)(void *context, unsigned int reg,
++ unsigned int mask, unsigned int val);
+
+ bool fast_io;
+
diff --git a/target/linux/generic/backport-6.1/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch b/target/linux/generic/backport-6.1/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch
new file mode 100644
index 0000000000..7de3cbbda0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch
@@ -0,0 +1,37 @@
+From 0dc0da881b4574d1e04a079ab2ea75da61f5ad2e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 11 Mar 2022 10:32:33 +0100
+Subject: [PATCH] tty: serial: bcm63xx: use more precise Kconfig symbol
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Patches lowering SERIAL_BCM63XX dependencies led to a discussion and
+documentation change regarding "depends" usage. Adjust Kconfig entry to
+match current guidelines. Make this symbol available for relevant
+architectures only.
+
+Cc: Geert Uytterhoeven <geert@linux-m68k.org>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Ref: f35a07f92616 ("tty: serial: bcm63xx: lower driver dependencies")
+Ref: 18084e435ff6 ("Documentation/kbuild: Document platform dependency practises")
+Link: https://lore.kernel.org/r/20220311093233.10012-1-zajec5@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/Kconfig | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/tty/serial/Kconfig
++++ b/drivers/tty/serial/Kconfig
+@@ -1098,7 +1098,8 @@ config SERIAL_TIMBERDALE
+ config SERIAL_BCM63XX
+ tristate "Broadcom BCM63xx/BCM33xx UART support"
+ select SERIAL_CORE
+- depends on COMMON_CLK
++ depends on ARCH_BCM4908 || ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC || COMPILE_TEST
++ default ARCH_BCM4908 || ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC
+ help
+ This enables the driver for the onchip UART core found on
+ the following chipsets:
diff --git a/target/linux/generic/backport-6.1/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch b/target/linux/generic/backport-6.1/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch
new file mode 100644
index 0000000000..caec8db5d6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch
@@ -0,0 +1,49 @@
+From cdbc4e3399ed8cdcf234a85f7a2482b622379e82 Mon Sep 17 00:00:00 2001
+From: Connor O'Brien <connoro@google.com>
+Date: Wed, 12 Jan 2022 00:25:03 +0000
+Subject: [PATCH] tools/resolve_btfids: Build with host flags
+
+resolve_btfids is built using $(HOSTCC) and $(HOSTLD) but does not
+pick up the corresponding flags. As a result, host-specific settings
+(such as a sysroot specified via HOSTCFLAGS=--sysroot=..., or a linker
+specified via HOSTLDFLAGS=-fuse-ld=...) will not be respected.
+
+Fix this by setting CFLAGS to KBUILD_HOSTCFLAGS and LDFLAGS to
+KBUILD_HOSTLDFLAGS.
+
+Also pass the cflags through to libbpf via EXTRA_CFLAGS to ensure that
+the host libbpf is built with flags consistent with resolve_btfids.
+
+Signed-off-by: Connor O'Brien <connoro@google.com>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Acked-by: Song Liu <songliubraving@fb.com>
+Link: https://lore.kernel.org/bpf/20220112002503.115968-1-connoro@google.com
+(cherry picked from commit 0e3a1c902ffb56e9fe4416f0cd382c97b09ecbf6)
+Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
+---
+ tools/bpf/resolve_btfids/Makefile | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/tools/bpf/resolve_btfids/Makefile
++++ b/tools/bpf/resolve_btfids/Makefile
+@@ -23,6 +23,8 @@ CC = $(HOSTCC)
+ LD = $(HOSTLD)
+ ARCH = $(HOSTARCH)
+ RM ?= rm
++CFLAGS := $(KBUILD_HOSTCFLAGS)
++LDFLAGS := $(KBUILD_HOSTLDFLAGS)
+
+ OUTPUT ?= $(srctree)/tools/bpf/resolve_btfids/
+
+@@ -45,9 +47,9 @@ $(SUBCMDOBJ): fixdep FORCE | $(OUTPUT)/l
+ $(Q)$(MAKE) -C $(SUBCMD_SRC) OUTPUT=$(abspath $(dir $@))/ $(abspath $@)
+
+ $(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf
+- $(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) OUTPUT=$(abspath $(dir $@))/ $(abspath $@)
++ $(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) OUTPUT=$(abspath $(dir $@))/ EXTRA_CFLAGS="$(CFLAGS)" $(abspath $@)
+
+-CFLAGS := -g \
++CFLAGS += -g \
+ -I$(srctree)/tools/include \
+ -I$(srctree)/tools/include/uapi \
+ -I$(LIBBPF_SRC) \
diff --git a/target/linux/generic/backport-6.1/201-v5.16-scripts-dtc-Update-to-upstream-version-v1.6.1-19-g0a.patch b/target/linux/generic/backport-6.1/201-v5.16-scripts-dtc-Update-to-upstream-version-v1.6.1-19-g0a.patch
new file mode 100644
index 0000000000..d1bef74f32
--- /dev/null
+++ b/target/linux/generic/backport-6.1/201-v5.16-scripts-dtc-Update-to-upstream-version-v1.6.1-19-g0a.patch
@@ -0,0 +1,997 @@
+From a77725a9a3c5924e2fd4cd5b3557dd92a8e46f87 Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Mon, 25 Oct 2021 11:05:45 -0500
+Subject: [PATCH 1/1] scripts/dtc: Update to upstream version
+ v1.6.1-19-g0a3a9d3449c8
+
+This adds the following commits from upstream:
+
+0a3a9d3449c8 checks: Add an interrupt-map check
+8fd24744e361 checks: Ensure '#interrupt-cells' only exists in interrupt providers
+d8d1a9a77863 checks: Drop interrupt provider '#address-cells' check
+52a16fd72824 checks: Make interrupt_provider check dependent on interrupts_extended_is_cell
+37fd700685da treesource: Maintain phandle label/path on output
+e33ce1d6a8c7 flattree: Use '\n', not ';' to separate asm pseudo-ops
+d24cc189dca6 asm: Use assembler macros instead of cpp macros
+ff3a30c115ad asm: Use .asciz and .ascii instead of .string
+5eb5927d81ee fdtdump: fix -Werror=int-to-pointer-cast
+0869f8269161 libfdt: Add ALIGNMENT error string
+69595a167f06 checks: Fix bus-range check
+72d09e2682a4 Makefile: add -Wsign-compare to warning options
+b587787ef388 checks: Fix signedness comparisons warnings
+69bed6c2418f dtc: Wrap phandle validity check
+910221185560 fdtget: Fix signedness comparisons warnings
+d966f08fcd21 tests: Fix signedness comparisons warnings
+ecfb438c07fa dtc: Fix signedness comparisons warnings: pointer diff
+5bec74a6d135 dtc: Fix signedness comparisons warnings: reservednum
+24e7f511fd4a fdtdump: Fix signedness comparisons warnings
+b6910bec1161 Bump version to v1.6.1
+21d61d18f968 Fix CID 1461557
+4c2ef8f4d14c checks: Introduce is_multiple_of()
+e59ca36fb70e Make handling of cpp line information more tolerant
+0c3fd9b6aceb checks: Drop interrupt_cells_is_cell check
+6b3081abc4ac checks: Add check_is_cell() for all phandle+arg properties
+2dffc192a77f yamltree: Remove marker ordering dependency
+61e513439e40 pylibfdt: Rework "avoid unused variable warning" lines
+c8bddd106095 tests: add a positive gpio test case
+ad4abfadb687 checks: replace strstr and strrchr with strends
+09c6a6e88718 dtc.h: add strends for suffix matching
+9bb9b8d0b4a0 checks: tigthen up nr-gpios prop exception
+b07b62ee3342 libfdt: Add FDT alignment check to fdt_check_header()
+a2def5479950 libfdt: Check that the root-node name is empty
+4ca61f84dc21 libfdt: Check that there is only one root node
+34d708249a91 dtc: Remove -O dtbo support
+8e7ff260f755 libfdt: Fix a possible "unchecked return value" warning
+88875268c05c checks: Warn on node-name and property name being the same
+9d2279e7e6ee checks: Change node-name check to match devicetree spec
+f527c867a8c6 util: limit gnu_printf format attribute to gcc >= 4.4.0
+
+Reviewed-by: Frank Rowand <frank.rowand@sony.com>
+Tested-by: Frank Rowand <frank.rowand@sony.com>
+Signed-off-by: Rob Herring <robh@kernel.org>
+---
+ scripts/dtc/checks.c | 222 ++++++++++++++++++++++--------
+ scripts/dtc/dtc-lexer.l | 2 +-
+ scripts/dtc/dtc.c | 6 +-
+ scripts/dtc/dtc.h | 40 +++++-
+ scripts/dtc/flattree.c | 11 +-
+ scripts/dtc/libfdt/fdt.c | 4 +
+ scripts/dtc/libfdt/fdt_rw.c | 18 ++-
+ scripts/dtc/libfdt/fdt_strerror.c | 1 +
+ scripts/dtc/libfdt/libfdt.h | 7 +
+ scripts/dtc/livetree.c | 6 +-
+ scripts/dtc/treesource.c | 48 +++----
+ scripts/dtc/util.h | 6 +-
+ scripts/dtc/version_gen.h | 2 +-
+ scripts/dtc/yamltree.c | 16 ++-
+ 14 files changed, 275 insertions(+), 114 deletions(-)
+
+--- a/scripts/dtc/checks.c
++++ b/scripts/dtc/checks.c
+@@ -143,6 +143,14 @@ static void check_nodes_props(struct che
+ check_nodes_props(c, dti, child);
+ }
+
++static bool is_multiple_of(int multiple, int divisor)
++{
++ if (divisor == 0)
++ return multiple == 0;
++ else
++ return (multiple % divisor) == 0;
++}
++
+ static bool run_check(struct check *c, struct dt_info *dti)
+ {
+ struct node *dt = dti->dt;
+@@ -297,19 +305,20 @@ ERROR(duplicate_property_names, check_du
+ #define LOWERCASE "abcdefghijklmnopqrstuvwxyz"
+ #define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ #define DIGITS "0123456789"
+-#define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-"
++#define NODECHARS LOWERCASE UPPERCASE DIGITS ",._+-@"
++#define PROPCHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-"
+ #define PROPNODECHARSSTRICT LOWERCASE UPPERCASE DIGITS ",-"
+
+ static void check_node_name_chars(struct check *c, struct dt_info *dti,
+ struct node *node)
+ {
+- int n = strspn(node->name, c->data);
++ size_t n = strspn(node->name, c->data);
+
+ if (n < strlen(node->name))
+ FAIL(c, dti, node, "Bad character '%c' in node name",
+ node->name[n]);
+ }
+-ERROR(node_name_chars, check_node_name_chars, PROPNODECHARS "@");
++ERROR(node_name_chars, check_node_name_chars, NODECHARS);
+
+ static void check_node_name_chars_strict(struct check *c, struct dt_info *dti,
+ struct node *node)
+@@ -330,6 +339,20 @@ static void check_node_name_format(struc
+ }
+ ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars);
+
++static void check_node_name_vs_property_name(struct check *c,
++ struct dt_info *dti,
++ struct node *node)
++{
++ if (!node->parent)
++ return;
++
++ if (get_property(node->parent, node->name)) {
++ FAIL(c, dti, node, "node name and property name conflict");
++ }
++}
++WARNING(node_name_vs_property_name, check_node_name_vs_property_name,
++ NULL, &node_name_chars);
++
+ static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti,
+ struct node *node)
+ {
+@@ -363,14 +386,14 @@ static void check_property_name_chars(st
+ struct property *prop;
+
+ for_each_property(node, prop) {
+- int n = strspn(prop->name, c->data);
++ size_t n = strspn(prop->name, c->data);
+
+ if (n < strlen(prop->name))
+ FAIL_PROP(c, dti, node, prop, "Bad character '%c' in property name",
+ prop->name[n]);
+ }
+ }
+-ERROR(property_name_chars, check_property_name_chars, PROPNODECHARS);
++ERROR(property_name_chars, check_property_name_chars, PROPCHARS);
+
+ static void check_property_name_chars_strict(struct check *c,
+ struct dt_info *dti,
+@@ -380,7 +403,7 @@ static void check_property_name_chars_st
+
+ for_each_property(node, prop) {
+ const char *name = prop->name;
+- int n = strspn(name, c->data);
++ size_t n = strspn(name, c->data);
+
+ if (n == strlen(prop->name))
+ continue;
+@@ -497,7 +520,7 @@ static cell_t check_phandle_prop(struct
+
+ phandle = propval_cell(prop);
+
+- if ((phandle == 0) || (phandle == -1)) {
++ if (!phandle_is_valid(phandle)) {
+ FAIL_PROP(c, dti, node, prop, "bad value (0x%x) in %s property",
+ phandle, prop->name);
+ return 0;
+@@ -556,7 +579,7 @@ static void check_name_properties(struct
+ if (!prop)
+ return; /* No name property, that's fine */
+
+- if ((prop->val.len != node->basenamelen+1)
++ if ((prop->val.len != node->basenamelen + 1U)
+ || (memcmp(prop->val.val, node->name, node->basenamelen) != 0)) {
+ FAIL(c, dti, node, "\"name\" property is incorrect (\"%s\" instead"
+ " of base node name)", prop->val.val);
+@@ -657,7 +680,6 @@ ERROR(omit_unused_nodes, fixup_omit_unus
+ */
+ WARNING_IF_NOT_CELL(address_cells_is_cell, "#address-cells");
+ WARNING_IF_NOT_CELL(size_cells_is_cell, "#size-cells");
+-WARNING_IF_NOT_CELL(interrupt_cells_is_cell, "#interrupt-cells");
+
+ WARNING_IF_NOT_STRING(device_type_is_string, "device_type");
+ WARNING_IF_NOT_STRING(model_is_string, "model");
+@@ -672,8 +694,7 @@ static void check_names_is_string_list(s
+ struct property *prop;
+
+ for_each_property(node, prop) {
+- const char *s = strrchr(prop->name, '-');
+- if (!s || !streq(s, "-names"))
++ if (!strends(prop->name, "-names"))
+ continue;
+
+ c->data = prop->name;
+@@ -753,7 +774,7 @@ static void check_reg_format(struct chec
+ size_cells = node_size_cells(node->parent);
+ entrylen = (addr_cells + size_cells) * sizeof(cell_t);
+
+- if (!entrylen || (prop->val.len % entrylen) != 0)
++ if (!is_multiple_of(prop->val.len, entrylen))
+ FAIL_PROP(c, dti, node, prop, "property has invalid length (%d bytes) "
+ "(#address-cells == %d, #size-cells == %d)",
+ prop->val.len, addr_cells, size_cells);
+@@ -794,7 +815,7 @@ static void check_ranges_format(struct c
+ "#size-cells (%d) differs from %s (%d)",
+ ranges, c_size_cells, node->parent->fullpath,
+ p_size_cells);
+- } else if ((prop->val.len % entrylen) != 0) {
++ } else if (!is_multiple_of(prop->val.len, entrylen)) {
+ FAIL_PROP(c, dti, node, prop, "\"%s\" property has invalid length (%d bytes) "
+ "(parent #address-cells == %d, child #address-cells == %d, "
+ "#size-cells == %d)", ranges, prop->val.len,
+@@ -871,7 +892,7 @@ static void check_pci_device_bus_num(str
+ } else {
+ cells = (cell_t *)prop->val.val;
+ min_bus = fdt32_to_cpu(cells[0]);
+- max_bus = fdt32_to_cpu(cells[0]);
++ max_bus = fdt32_to_cpu(cells[1]);
+ }
+ if ((bus_num < min_bus) || (bus_num > max_bus))
+ FAIL_PROP(c, dti, node, prop, "PCI bus number %d out of range, expected (%d - %d)",
+@@ -1367,9 +1388,9 @@ static void check_property_phandle_args(
+ const struct provider *provider)
+ {
+ struct node *root = dti->dt;
+- int cell, cellsize = 0;
++ unsigned int cell, cellsize = 0;
+
+- if (prop->val.len % sizeof(cell_t)) {
++ if (!is_multiple_of(prop->val.len, sizeof(cell_t))) {
+ FAIL_PROP(c, dti, node, prop,
+ "property size (%d) is invalid, expected multiple of %zu",
+ prop->val.len, sizeof(cell_t));
+@@ -1379,14 +1400,14 @@ static void check_property_phandle_args(
+ for (cell = 0; cell < prop->val.len / sizeof(cell_t); cell += cellsize + 1) {
+ struct node *provider_node;
+ struct property *cellprop;
+- int phandle;
++ cell_t phandle;
+
+ phandle = propval_cell_n(prop, cell);
+ /*
+ * Some bindings use a cell value 0 or -1 to skip over optional
+ * entries when each index position has a specific definition.
+ */
+- if (phandle == 0 || phandle == -1) {
++ if (!phandle_is_valid(phandle)) {
+ /* Give up if this is an overlay with external references */
+ if (dti->dtsflags & DTSF_PLUGIN)
+ break;
+@@ -1452,7 +1473,8 @@ static void check_provider_cells_propert
+ }
+ #define WARNING_PROPERTY_PHANDLE_CELLS(nm, propname, cells_name, ...) \
+ static struct provider nm##_provider = { (propname), (cells_name), __VA_ARGS__ }; \
+- WARNING(nm##_property, check_provider_cells_property, &nm##_provider, &phandle_references);
++ WARNING_IF_NOT_CELL(nm##_is_cell, cells_name); \
++ WARNING(nm##_property, check_provider_cells_property, &nm##_provider, &nm##_is_cell, &phandle_references);
+
+ WARNING_PROPERTY_PHANDLE_CELLS(clocks, "clocks", "#clock-cells");
+ WARNING_PROPERTY_PHANDLE_CELLS(cooling_device, "cooling-device", "#cooling-cells");
+@@ -1473,24 +1495,17 @@ WARNING_PROPERTY_PHANDLE_CELLS(thermal_s
+
+ static bool prop_is_gpio(struct property *prop)
+ {
+- char *str;
+-
+ /*
+ * *-gpios and *-gpio can appear in property names,
+ * so skip over any false matches (only one known ATM)
+ */
+- if (strstr(prop->name, "nr-gpio"))
++ if (strends(prop->name, ",nr-gpios"))
+ return false;
+
+- str = strrchr(prop->name, '-');
+- if (str)
+- str++;
+- else
+- str = prop->name;
+- if (!(streq(str, "gpios") || streq(str, "gpio")))
+- return false;
+-
+- return true;
++ return strends(prop->name, "-gpios") ||
++ streq(prop->name, "gpios") ||
++ strends(prop->name, "-gpio") ||
++ streq(prop->name, "gpio");
+ }
+
+ static void check_gpios_property(struct check *c,
+@@ -1525,13 +1540,10 @@ static void check_deprecated_gpio_proper
+ struct property *prop;
+
+ for_each_property(node, prop) {
+- char *str;
+-
+ if (!prop_is_gpio(prop))
+ continue;
+
+- str = strstr(prop->name, "gpio");
+- if (!streq(str, "gpio"))
++ if (!strends(prop->name, "gpio"))
+ continue;
+
+ FAIL_PROP(c, dti, node, prop,
+@@ -1561,21 +1573,106 @@ static void check_interrupt_provider(str
+ struct node *node)
+ {
+ struct property *prop;
++ bool irq_provider = node_is_interrupt_provider(node);
+
+- if (!node_is_interrupt_provider(node))
++ prop = get_property(node, "#interrupt-cells");
++ if (irq_provider && !prop) {
++ FAIL(c, dti, node,
++ "Missing '#interrupt-cells' in interrupt provider");
+ return;
++ }
+
+- prop = get_property(node, "#interrupt-cells");
+- if (!prop)
++ if (!irq_provider && prop) {
+ FAIL(c, dti, node,
+- "Missing #interrupt-cells in interrupt provider");
++ "'#interrupt-cells' found, but node is not an interrupt provider");
++ return;
++ }
++}
++WARNING(interrupt_provider, check_interrupt_provider, NULL, &interrupts_extended_is_cell);
+
+- prop = get_property(node, "#address-cells");
+- if (!prop)
++static void check_interrupt_map(struct check *c,
++ struct dt_info *dti,
++ struct node *node)
++{
++ struct node *root = dti->dt;
++ struct property *prop, *irq_map_prop;
++ size_t cellsize, cell, map_cells;
++
++ irq_map_prop = get_property(node, "interrupt-map");
++ if (!irq_map_prop)
++ return;
++
++ if (node->addr_cells < 0) {
+ FAIL(c, dti, node,
+- "Missing #address-cells in interrupt provider");
++ "Missing '#address-cells' in interrupt-map provider");
++ return;
++ }
++ cellsize = node_addr_cells(node);
++ cellsize += propval_cell(get_property(node, "#interrupt-cells"));
++
++ prop = get_property(node, "interrupt-map-mask");
++ if (prop && (prop->val.len != (cellsize * sizeof(cell_t))))
++ FAIL_PROP(c, dti, node, prop,
++ "property size (%d) is invalid, expected %zu",
++ prop->val.len, cellsize * sizeof(cell_t));
++
++ if (!is_multiple_of(irq_map_prop->val.len, sizeof(cell_t))) {
++ FAIL_PROP(c, dti, node, irq_map_prop,
++ "property size (%d) is invalid, expected multiple of %zu",
++ irq_map_prop->val.len, sizeof(cell_t));
++ return;
++ }
++
++ map_cells = irq_map_prop->val.len / sizeof(cell_t);
++ for (cell = 0; cell < map_cells; ) {
++ struct node *provider_node;
++ struct property *cellprop;
++ int phandle;
++ size_t parent_cellsize;
++
++ if ((cell + cellsize) >= map_cells) {
++ FAIL_PROP(c, dti, node, irq_map_prop,
++ "property size (%d) too small, expected > %zu",
++ irq_map_prop->val.len, (cell + cellsize) * sizeof(cell_t));
++ break;
++ }
++ cell += cellsize;
++
++ phandle = propval_cell_n(irq_map_prop, cell);
++ if (!phandle_is_valid(phandle)) {
++ /* Give up if this is an overlay with external references */
++ if (!(dti->dtsflags & DTSF_PLUGIN))
++ FAIL_PROP(c, dti, node, irq_map_prop,
++ "Cell %zu is not a phandle(%d)",
++ cell, phandle);
++ break;
++ }
++
++ provider_node = get_node_by_phandle(root, phandle);
++ if (!provider_node) {
++ FAIL_PROP(c, dti, node, irq_map_prop,
++ "Could not get phandle(%d) node for (cell %zu)",
++ phandle, cell);
++ break;
++ }
++
++ cellprop = get_property(provider_node, "#interrupt-cells");
++ if (cellprop) {
++ parent_cellsize = propval_cell(cellprop);
++ } else {
++ FAIL(c, dti, node, "Missing property '#interrupt-cells' in node %s or bad phandle (referred from interrupt-map[%zu])",
++ provider_node->fullpath, cell);
++ break;
++ }
++
++ cellprop = get_property(provider_node, "#address-cells");
++ if (cellprop)
++ parent_cellsize += propval_cell(cellprop);
++
++ cell += 1 + parent_cellsize;
++ }
+ }
+-WARNING(interrupt_provider, check_interrupt_provider, NULL);
++WARNING(interrupt_map, check_interrupt_map, NULL, &phandle_references, &addr_size_cells, &interrupt_provider);
+
+ static void check_interrupts_property(struct check *c,
+ struct dt_info *dti,
+@@ -1584,13 +1681,13 @@ static void check_interrupts_property(st
+ struct node *root = dti->dt;
+ struct node *irq_node = NULL, *parent = node;
+ struct property *irq_prop, *prop = NULL;
+- int irq_cells, phandle;
++ cell_t irq_cells, phandle;
+
+ irq_prop = get_property(node, "interrupts");
+ if (!irq_prop)
+ return;
+
+- if (irq_prop->val.len % sizeof(cell_t))
++ if (!is_multiple_of(irq_prop->val.len, sizeof(cell_t)))
+ FAIL_PROP(c, dti, node, irq_prop, "size (%d) is invalid, expected multiple of %zu",
+ irq_prop->val.len, sizeof(cell_t));
+
+@@ -1603,7 +1700,7 @@ static void check_interrupts_property(st
+ prop = get_property(parent, "interrupt-parent");
+ if (prop) {
+ phandle = propval_cell(prop);
+- if ((phandle == 0) || (phandle == -1)) {
++ if (!phandle_is_valid(phandle)) {
+ /* Give up if this is an overlay with
+ * external references */
+ if (dti->dtsflags & DTSF_PLUGIN)
+@@ -1639,7 +1736,7 @@ static void check_interrupts_property(st
+ }
+
+ irq_cells = propval_cell(prop);
+- if (irq_prop->val.len % (irq_cells * sizeof(cell_t))) {
++ if (!is_multiple_of(irq_prop->val.len, irq_cells * sizeof(cell_t))) {
+ FAIL_PROP(c, dti, node, prop,
+ "size is (%d), expected multiple of %d",
+ irq_prop->val.len, (int)(irq_cells * sizeof(cell_t)));
+@@ -1750,7 +1847,7 @@ WARNING(graph_port, check_graph_port, NU
+ static struct node *get_remote_endpoint(struct check *c, struct dt_info *dti,
+ struct node *endpoint)
+ {
+- int phandle;
++ cell_t phandle;
+ struct node *node;
+ struct property *prop;
+
+@@ -1760,7 +1857,7 @@ static struct node *get_remote_endpoint(
+
+ phandle = propval_cell(prop);
+ /* Give up if this is an overlay with external references */
+- if (phandle == 0 || phandle == -1)
++ if (!phandle_is_valid(phandle))
+ return NULL;
+
+ node = get_node_by_phandle(dti->dt, phandle);
+@@ -1796,7 +1893,7 @@ WARNING(graph_endpoint, check_graph_endp
+ static struct check *check_table[] = {
+ &duplicate_node_names, &duplicate_property_names,
+ &node_name_chars, &node_name_format, &property_name_chars,
+- &name_is_string, &name_properties,
++ &name_is_string, &name_properties, &node_name_vs_property_name,
+
+ &duplicate_label,
+
+@@ -1804,7 +1901,7 @@ static struct check *check_table[] = {
+ &phandle_references, &path_references,
+ &omit_unused_nodes,
+
+- &address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell,
++ &address_cells_is_cell, &size_cells_is_cell,
+ &device_type_is_string, &model_is_string, &status_is_string,
+ &label_is_string,
+
+@@ -1839,26 +1936,43 @@ static struct check *check_table[] = {
+ &chosen_node_is_root, &chosen_node_bootargs, &chosen_node_stdout_path,
+
+ &clocks_property,
++ &clocks_is_cell,
+ &cooling_device_property,
++ &cooling_device_is_cell,
+ &dmas_property,
++ &dmas_is_cell,
+ &hwlocks_property,
++ &hwlocks_is_cell,
+ &interrupts_extended_property,
++ &interrupts_extended_is_cell,
+ &io_channels_property,
++ &io_channels_is_cell,
+ &iommus_property,
++ &iommus_is_cell,
+ &mboxes_property,
++ &mboxes_is_cell,
+ &msi_parent_property,
++ &msi_parent_is_cell,
+ &mux_controls_property,
++ &mux_controls_is_cell,
+ &phys_property,
++ &phys_is_cell,
+ &power_domains_property,
++ &power_domains_is_cell,
+ &pwms_property,
++ &pwms_is_cell,
+ &resets_property,
++ &resets_is_cell,
+ &sound_dai_property,
++ &sound_dai_is_cell,
+ &thermal_sensors_property,
++ &thermal_sensors_is_cell,
+
+ &deprecated_gpio_property,
+ &gpios_property,
+ &interrupts_property,
+ &interrupt_provider,
++ &interrupt_map,
+
+ &alias_paths,
+
+@@ -1882,7 +1996,7 @@ static void enable_warning_error(struct
+
+ static void disable_warning_error(struct check *c, bool warn, bool error)
+ {
+- int i;
++ unsigned int i;
+
+ /* Lowering level, also lower it for things this is the prereq
+ * for */
+@@ -1903,7 +2017,7 @@ static void disable_warning_error(struct
+
+ void parse_checks_option(bool warn, bool error, const char *arg)
+ {
+- int i;
++ unsigned int i;
+ const char *name = arg;
+ bool enable = true;
+
+@@ -1930,7 +2044,7 @@ void parse_checks_option(bool warn, bool
+
+ void process_checks(bool force, struct dt_info *dti)
+ {
+- int i;
++ unsigned int i;
+ int error = 0;
+
+ for (i = 0; i < ARRAY_SIZE(check_table); i++) {
+--- a/scripts/dtc/dtc-lexer.l
++++ b/scripts/dtc/dtc-lexer.l
+@@ -57,7 +57,7 @@ static void PRINTF(1, 2) lexical_error(c
+ push_input_file(name);
+ }
+
+-<*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)? {
++<*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)* {
+ char *line, *fnstart, *fnend;
+ struct data fn;
+ /* skip text before line # */
+--- a/scripts/dtc/dtc.c
++++ b/scripts/dtc/dtc.c
+@@ -12,7 +12,7 @@
+ * Command line options
+ */
+ int quiet; /* Level of quietness */
+-int reservenum; /* Number of memory reservation slots */
++unsigned int reservenum;/* Number of memory reservation slots */
+ int minsize; /* Minimum blob size */
+ int padsize; /* Additional padding to blob */
+ int alignsize; /* Additional padding to blob accroding to the alignsize */
+@@ -197,7 +197,7 @@ int main(int argc, char *argv[])
+ depname = optarg;
+ break;
+ case 'R':
+- reservenum = strtol(optarg, NULL, 0);
++ reservenum = strtoul(optarg, NULL, 0);
+ break;
+ case 'S':
+ minsize = strtol(optarg, NULL, 0);
+@@ -359,8 +359,6 @@ int main(int argc, char *argv[])
+ #endif
+ } else if (streq(outform, "dtb")) {
+ dt_to_blob(outf, dti, outversion);
+- } else if (streq(outform, "dtbo")) {
+- dt_to_blob(outf, dti, outversion);
+ } else if (streq(outform, "asm")) {
+ dt_to_asm(outf, dti, outversion);
+ } else if (streq(outform, "null")) {
+--- a/scripts/dtc/dtc.h
++++ b/scripts/dtc/dtc.h
+@@ -35,7 +35,7 @@
+ * Command line options
+ */
+ extern int quiet; /* Level of quietness */
+-extern int reservenum; /* Number of memory reservation slots */
++extern unsigned int reservenum; /* Number of memory reservation slots */
+ extern int minsize; /* Minimum blob size */
+ extern int padsize; /* Additional padding to blob */
+ extern int alignsize; /* Additional padding to blob accroding to the alignsize */
+@@ -51,6 +51,11 @@ extern int annotate; /* annotate .dts w
+
+ typedef uint32_t cell_t;
+
++static inline bool phandle_is_valid(cell_t phandle)
++{
++ return phandle != 0 && phandle != ~0U;
++}
++
+ static inline uint16_t dtb_ld16(const void *p)
+ {
+ const uint8_t *bp = (const uint8_t *)p;
+@@ -86,6 +91,16 @@ static inline uint64_t dtb_ld64(const vo
+ #define streq(a, b) (strcmp((a), (b)) == 0)
+ #define strstarts(s, prefix) (strncmp((s), (prefix), strlen(prefix)) == 0)
+ #define strprefixeq(a, n, b) (strlen(b) == (n) && (memcmp(a, b, n) == 0))
++static inline bool strends(const char *str, const char *suffix)
++{
++ unsigned int len, suffix_len;
++
++ len = strlen(str);
++ suffix_len = strlen(suffix);
++ if (len < suffix_len)
++ return false;
++ return streq(str + len - suffix_len, suffix);
++}
+
+ #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+
+@@ -101,6 +116,12 @@ enum markertype {
+ TYPE_UINT64,
+ TYPE_STRING,
+ };
++
++static inline bool is_type_marker(enum markertype type)
++{
++ return type >= TYPE_UINT8;
++}
++
+ extern const char *markername(enum markertype markertype);
+
+ struct marker {
+@@ -125,7 +146,22 @@ struct data {
+ for_each_marker(m) \
+ if ((m)->type == (t))
+
+-size_t type_marker_length(struct marker *m);
++static inline struct marker *next_type_marker(struct marker *m)
++{
++ for_each_marker(m)
++ if (is_type_marker(m->type))
++ break;
++ return m;
++}
++
++static inline size_t type_marker_length(struct marker *m)
++{
++ struct marker *next = next_type_marker(m->next);
++
++ if (next)
++ return next->offset - m->offset;
++ return 0;
++}
+
+ void data_free(struct data d);
+
+--- a/scripts/dtc/flattree.c
++++ b/scripts/dtc/flattree.c
+@@ -124,7 +124,8 @@ static void asm_emit_cell(void *e, cell_
+ {
+ FILE *f = e;
+
+- fprintf(f, "\t.byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x\n",
++ fprintf(f, "\t.byte\t0x%02x\n" "\t.byte\t0x%02x\n"
++ "\t.byte\t0x%02x\n" "\t.byte\t0x%02x\n",
+ (val >> 24) & 0xff, (val >> 16) & 0xff,
+ (val >> 8) & 0xff, val & 0xff);
+ }
+@@ -134,9 +135,9 @@ static void asm_emit_string(void *e, con
+ FILE *f = e;
+
+ if (len != 0)
+- fprintf(f, "\t.string\t\"%.*s\"\n", len, str);
++ fprintf(f, "\t.asciz\t\"%.*s\"\n", len, str);
+ else
+- fprintf(f, "\t.string\t\"%s\"\n", str);
++ fprintf(f, "\t.asciz\t\"%s\"\n", str);
+ }
+
+ static void asm_emit_align(void *e, int a)
+@@ -295,7 +296,7 @@ static struct data flatten_reserve_list(
+ {
+ struct reserve_info *re;
+ struct data d = empty_data;
+- int j;
++ unsigned int j;
+
+ for (re = reservelist; re; re = re->next) {
+ d = data_append_re(d, re->address, re->size);
+@@ -438,7 +439,7 @@ static void dump_stringtable_asm(FILE *f
+
+ while (p < (strbuf.val + strbuf.len)) {
+ len = strlen(p);
+- fprintf(f, "\t.string \"%s\"\n", p);
++ fprintf(f, "\t.asciz \"%s\"\n", p);
+ p += len+1;
+ }
+ }
+--- a/scripts/dtc/libfdt/fdt.c
++++ b/scripts/dtc/libfdt/fdt.c
+@@ -90,6 +90,10 @@ int fdt_check_header(const void *fdt)
+ {
+ size_t hdrsize;
+
++ /* The device tree must be at an 8-byte aligned address */
++ if ((uintptr_t)fdt & 7)
++ return -FDT_ERR_ALIGNMENT;
++
+ if (fdt_magic(fdt) != FDT_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ if (!can_assume(LATEST)) {
+--- a/scripts/dtc/libfdt/fdt_rw.c
++++ b/scripts/dtc/libfdt/fdt_rw.c
+@@ -349,7 +349,10 @@ int fdt_add_subnode_namelen(void *fdt, i
+ return offset;
+
+ /* Try to place the new node after the parent's properties */
+- fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
++ tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
++ /* the fdt_subnode_offset_namelen() should ensure this never hits */
++ if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
++ return -FDT_ERR_INTERNAL;
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+@@ -391,7 +394,9 @@ int fdt_del_node(void *fdt, int nodeoffs
+ }
+
+ static void fdt_packblocks_(const char *old, char *new,
+- int mem_rsv_size, int struct_size)
++ int mem_rsv_size,
++ int struct_size,
++ int strings_size)
+ {
+ int mem_rsv_off, struct_off, strings_off;
+
+@@ -406,8 +411,7 @@ static void fdt_packblocks_(const char *
+ fdt_set_off_dt_struct(new, struct_off);
+ fdt_set_size_dt_struct(new, struct_size);
+
+- memmove(new + strings_off, old + fdt_off_dt_strings(old),
+- fdt_size_dt_strings(old));
++ memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
+ fdt_set_off_dt_strings(new, strings_off);
+ fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
+ }
+@@ -467,7 +471,8 @@ int fdt_open_into(const void *fdt, void
+ return -FDT_ERR_NOSPACE;
+ }
+
+- fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size);
++ fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
++ fdt_size_dt_strings(fdt));
+ memmove(buf, tmp, newsize);
+
+ fdt_set_magic(buf, FDT_MAGIC);
+@@ -487,7 +492,8 @@ int fdt_pack(void *fdt)
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+- fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
++ fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
++ fdt_size_dt_strings(fdt));
+ fdt_set_totalsize(fdt, fdt_data_size_(fdt));
+
+ return 0;
+--- a/scripts/dtc/libfdt/fdt_strerror.c
++++ b/scripts/dtc/libfdt/fdt_strerror.c
+@@ -39,6 +39,7 @@ static struct fdt_errtabent fdt_errtable
+ FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
+ FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
+ FDT_ERRTABENT(FDT_ERR_BADFLAGS),
++ FDT_ERRTABENT(FDT_ERR_ALIGNMENT),
+ };
+ #define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0])))
+
+--- a/scripts/dtc/libfdt/libfdt.h
++++ b/scripts/dtc/libfdt/libfdt.h
+@@ -131,6 +131,13 @@ uint32_t fdt_next_tag(const void *fdt, i
+ * to work even with unaligned pointers on platforms (such as ARMv5) that don't
+ * like unaligned loads and stores.
+ */
++static inline uint16_t fdt16_ld(const fdt16_t *p)
++{
++ const uint8_t *bp = (const uint8_t *)p;
++
++ return ((uint16_t)bp[0] << 8) | bp[1];
++}
++
+ static inline uint32_t fdt32_ld(const fdt32_t *p)
+ {
+ const uint8_t *bp = (const uint8_t *)p;
+--- a/scripts/dtc/livetree.c
++++ b/scripts/dtc/livetree.c
+@@ -526,7 +526,7 @@ struct node *get_node_by_path(struct nod
+ p = strchr(path, '/');
+
+ for_each_child(tree, child) {
+- if (p && strprefixeq(path, p - path, child->name))
++ if (p && strprefixeq(path, (size_t)(p - path), child->name))
+ return get_node_by_path(child, p+1);
+ else if (!p && streq(path, child->name))
+ return child;
+@@ -559,7 +559,7 @@ struct node *get_node_by_phandle(struct
+ {
+ struct node *child, *node;
+
+- if ((phandle == 0) || (phandle == -1)) {
++ if (!phandle_is_valid(phandle)) {
+ assert(generate_fixups);
+ return NULL;
+ }
+@@ -594,7 +594,7 @@ cell_t get_node_phandle(struct node *roo
+ static cell_t phandle = 1; /* FIXME: ick, static local */
+ struct data d = empty_data;
+
+- if ((node->phandle != 0) && (node->phandle != -1))
++ if (phandle_is_valid(node->phandle))
+ return node->phandle;
+
+ while (get_node_by_phandle(root, phandle))
+--- a/scripts/dtc/treesource.c
++++ b/scripts/dtc/treesource.c
+@@ -124,27 +124,6 @@ static void write_propval_int(FILE *f, c
+ }
+ }
+
+-static bool has_data_type_information(struct marker *m)
+-{
+- return m->type >= TYPE_UINT8;
+-}
+-
+-static struct marker *next_type_marker(struct marker *m)
+-{
+- while (m && !has_data_type_information(m))
+- m = m->next;
+- return m;
+-}
+-
+-size_t type_marker_length(struct marker *m)
+-{
+- struct marker *next = next_type_marker(m->next);
+-
+- if (next)
+- return next->offset - m->offset;
+- return 0;
+-}
+-
+ static const char *delim_start[] = {
+ [TYPE_UINT8] = "[",
+ [TYPE_UINT16] = "/bits/ 16 <",
+@@ -229,26 +208,39 @@ static void write_propval(FILE *f, struc
+ size_t chunk_len = (m->next ? m->next->offset : len) - m->offset;
+ size_t data_len = type_marker_length(m) ? : len - m->offset;
+ const char *p = &prop->val.val[m->offset];
++ struct marker *m_phandle;
+
+- if (has_data_type_information(m)) {
++ if (is_type_marker(m->type)) {
+ emit_type = m->type;
+ fprintf(f, " %s", delim_start[emit_type]);
+ } else if (m->type == LABEL)
+ fprintf(f, " %s:", m->ref);
+- else if (m->offset)
+- fputc(' ', f);
+
+- if (emit_type == TYPE_NONE) {
+- assert(chunk_len == 0);
++ if (emit_type == TYPE_NONE || chunk_len == 0)
+ continue;
+- }
+
+ switch(emit_type) {
+ case TYPE_UINT16:
+ write_propval_int(f, p, chunk_len, 2);
+ break;
+ case TYPE_UINT32:
+- write_propval_int(f, p, chunk_len, 4);
++ m_phandle = prop->val.markers;
++ for_each_marker_of_type(m_phandle, REF_PHANDLE)
++ if (m->offset == m_phandle->offset)
++ break;
++
++ if (m_phandle) {
++ if (m_phandle->ref[0] == '/')
++ fprintf(f, "&{%s}", m_phandle->ref);
++ else
++ fprintf(f, "&%s", m_phandle->ref);
++ if (chunk_len > 4) {
++ fputc(' ', f);
++ write_propval_int(f, p + 4, chunk_len - 4, 4);
++ }
++ } else {
++ write_propval_int(f, p, chunk_len, 4);
++ }
+ break;
+ case TYPE_UINT64:
+ write_propval_int(f, p, chunk_len, 8);
+--- a/scripts/dtc/util.h
++++ b/scripts/dtc/util.h
+@@ -13,10 +13,10 @@
+ */
+
+ #ifdef __GNUC__
+-#ifdef __clang__
+-#define PRINTF(i, j) __attribute__((format (printf, i, j)))
+-#else
++#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
+ #define PRINTF(i, j) __attribute__((format (gnu_printf, i, j)))
++#else
++#define PRINTF(i, j) __attribute__((format (printf, i, j)))
+ #endif
+ #define NORETURN __attribute__((noreturn))
+ #else
+--- a/scripts/dtc/version_gen.h
++++ b/scripts/dtc/version_gen.h
+@@ -1 +1 @@
+-#define DTC_VERSION "DTC 1.6.0-g183df9e9"
++#define DTC_VERSION "DTC 1.6.1-g0a3a9d34"
+--- a/scripts/dtc/yamltree.c
++++ b/scripts/dtc/yamltree.c
+@@ -29,11 +29,12 @@ char *yaml_error_name[] = {
+ (emitter)->problem, __func__, __LINE__); \
+ })
+
+-static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, char *data, unsigned int len, int width)
++static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers,
++ char *data, unsigned int seq_offset, unsigned int len, int width)
+ {
+ yaml_event_t event;
+ void *tag;
+- unsigned int off, start_offset = markers->offset;
++ unsigned int off;
+
+ switch(width) {
+ case 1: tag = "!u8"; break;
+@@ -66,7 +67,7 @@ static void yaml_propval_int(yaml_emitte
+ m = markers;
+ is_phandle = false;
+ for_each_marker_of_type(m, REF_PHANDLE) {
+- if (m->offset == (start_offset + off)) {
++ if (m->offset == (seq_offset + off)) {
+ is_phandle = true;
+ break;
+ }
+@@ -114,6 +115,7 @@ static void yaml_propval(yaml_emitter_t
+ yaml_event_t event;
+ unsigned int len = prop->val.len;
+ struct marker *m = prop->val.markers;
++ struct marker *markers = prop->val.markers;
+
+ /* Emit the property name */
+ yaml_scalar_event_initialize(&event, NULL,
+@@ -151,19 +153,19 @@ static void yaml_propval(yaml_emitter_t
+
+ switch(m->type) {
+ case TYPE_UINT16:
+- yaml_propval_int(emitter, m, data, chunk_len, 2);
++ yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 2);
+ break;
+ case TYPE_UINT32:
+- yaml_propval_int(emitter, m, data, chunk_len, 4);
++ yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 4);
+ break;
+ case TYPE_UINT64:
+- yaml_propval_int(emitter, m, data, chunk_len, 8);
++ yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 8);
+ break;
+ case TYPE_STRING:
+ yaml_propval_string(emitter, data, chunk_len);
+ break;
+ default:
+- yaml_propval_int(emitter, m, data, chunk_len, 1);
++ yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 1);
+ break;
+ }
+ }
diff --git a/target/linux/generic/backport-6.1/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch b/target/linux/generic/backport-6.1/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch
new file mode 100644
index 0000000000..18a8752a18
--- /dev/null
+++ b/target/linux/generic/backport-6.1/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch
@@ -0,0 +1,48 @@
+From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+To: linus.walleij@linaro.org
+Cc: bjorn.andersson@linaro.org, dianders@chromium.org,
+ linux-arm-msm@vger.kernel.org, linux-gpio@vger.kernel.org,
+ linux-kernel@vger.kernel.org,
+ Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Subject: [PATCH] pinctrl: qcom: Return -EINVAL for setting affinity if no IRQ
+ parent
+Date: Thu, 13 Jan 2022 21:56:17 +0530
+Message-Id: <20220113162617.131697-1-manivannan.sadhasivam@linaro.org>
+
+The MSM GPIO IRQ controller relies on the parent IRQ controller to set the
+CPU affinity for the IRQ. And this is only valid if there is any wakeup
+parent available and defined in DT.
+
+For the case of no parent IRQ controller defined in DT,
+msm_gpio_irq_set_affinity() and msm_gpio_irq_set_vcpu_affinity() should
+return -EINVAL instead of 0 as the affinity can't be set.
+
+Otherwise, below warning will be printed by genirq:
+
+genirq: irq_chip msmgpio did not update eff. affinity mask of irq 70
+
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+---
+ drivers/pinctrl/qcom/pinctrl-msm.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/pinctrl/qcom/pinctrl-msm.c
++++ b/drivers/pinctrl/qcom/pinctrl-msm.c
+@@ -1157,7 +1157,7 @@ static int msm_gpio_irq_set_affinity(str
+ if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs))
+ return irq_chip_set_affinity_parent(d, dest, force);
+
+- return 0;
++ return -EINVAL;
+ }
+
+ static int msm_gpio_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
+@@ -1168,7 +1168,7 @@ static int msm_gpio_irq_set_vcpu_affinit
+ if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs))
+ return irq_chip_set_vcpu_affinity_parent(d, vcpu_info);
+
+- return 0;
++ return -EINVAL;
+ }
+
+ static void msm_gpio_irq_handler(struct irq_desc *desc)
diff --git a/target/linux/generic/backport-6.1/301-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch b/target/linux/generic/backport-6.1/301-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch
new file mode 100644
index 0000000000..ee0bf9309f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/301-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch
@@ -0,0 +1,166 @@
+From b5af64fceb04dc298c5e69c517b4d83893ff060b Mon Sep 17 00:00:00 2001
+From: Bjorn Andersson <bjorn.andersson@linaro.org>
+Date: Thu, 30 Sep 2021 11:21:10 -0700
+Subject: [PATCH 1/1] soc: qcom: smem: Support reserved-memory description
+
+Practically all modern Qualcomm platforms has a single reserved-memory
+region for SMEM. So rather than having to describe SMEM in the form of a
+node with a reference to a reserved-memory node, allow the SMEM device
+to be instantiated directly from the reserved-memory node.
+
+The current means of falling back to dereferencing the "memory-region"
+is kept as a fallback, if it's determined that the SMEM node is a
+reserved-memory node.
+
+The "qcom,smem" compatible is added to the reserved_mem_matches list, to
+allow the reserved-memory device to be probed.
+
+In order to retain the readability of the code, the resolution of
+resources is split from the actual ioremapping.
+
+Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
+Acked-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
+Link: https://lore.kernel.org/r/20210930182111.57353-4-bjorn.andersson@linaro.org
+---
+ drivers/of/platform.c | 1 +
+ drivers/soc/qcom/smem.c | 57 ++++++++++++++++++++++++++++-------------
+ 2 files changed, 40 insertions(+), 18 deletions(-)
+
+--- a/drivers/of/platform.c
++++ b/drivers/of/platform.c
+@@ -509,6 +509,7 @@ EXPORT_SYMBOL_GPL(of_platform_default_po
+ static const struct of_device_id reserved_mem_matches[] = {
+ { .compatible = "qcom,rmtfs-mem" },
+ { .compatible = "qcom,cmd-db" },
++ { .compatible = "qcom,smem" },
+ { .compatible = "ramoops" },
+ { .compatible = "nvmem-rmem" },
+ {}
+--- a/drivers/soc/qcom/smem.c
++++ b/drivers/soc/qcom/smem.c
+@@ -9,6 +9,7 @@
+ #include <linux/module.h>
+ #include <linux/of.h>
+ #include <linux/of_address.h>
++#include <linux/of_reserved_mem.h>
+ #include <linux/platform_device.h>
+ #include <linux/sizes.h>
+ #include <linux/slab.h>
+@@ -240,7 +241,7 @@ static const u8 SMEM_INFO_MAGIC[] = { 0x
+ * @size: size of the memory region
+ */
+ struct smem_region {
+- u32 aux_base;
++ phys_addr_t aux_base;
+ void __iomem *virt_base;
+ size_t size;
+ };
+@@ -499,7 +500,7 @@ static void *qcom_smem_get_global(struct
+ for (i = 0; i < smem->num_regions; i++) {
+ region = &smem->regions[i];
+
+- if (region->aux_base == aux_base || !aux_base) {
++ if ((u32)region->aux_base == aux_base || !aux_base) {
+ if (size != NULL)
+ *size = le32_to_cpu(entry->size);
+ return region->virt_base + le32_to_cpu(entry->offset);
+@@ -664,7 +665,7 @@ phys_addr_t qcom_smem_virt_to_phys(void
+ if (p < region->virt_base + region->size) {
+ u64 offset = p - region->virt_base;
+
+- return (phys_addr_t)region->aux_base + offset;
++ return region->aux_base + offset;
+ }
+ }
+
+@@ -863,12 +864,12 @@ qcom_smem_enumerate_partitions(struct qc
+ return 0;
+ }
+
+-static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
+- const char *name, int i)
++static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name,
++ struct smem_region *region)
+ {
++ struct device *dev = smem->dev;
+ struct device_node *np;
+ struct resource r;
+- resource_size_t size;
+ int ret;
+
+ np = of_parse_phandle(dev->of_node, name, 0);
+@@ -881,13 +882,9 @@ static int qcom_smem_map_memory(struct q
+ of_node_put(np);
+ if (ret)
+ return ret;
+- size = resource_size(&r);
+
+- smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, size);
+- if (!smem->regions[i].virt_base)
+- return -ENOMEM;
+- smem->regions[i].aux_base = (u32)r.start;
+- smem->regions[i].size = size;
++ region->aux_base = r.start;
++ region->size = resource_size(&r);
+
+ return 0;
+ }
+@@ -895,12 +892,14 @@ static int qcom_smem_map_memory(struct q
+ static int qcom_smem_probe(struct platform_device *pdev)
+ {
+ struct smem_header *header;
++ struct reserved_mem *rmem;
+ struct qcom_smem *smem;
+ size_t array_size;
+ int num_regions;
+ int hwlock_id;
+ u32 version;
+ int ret;
++ int i;
+
+ num_regions = 1;
+ if (of_find_property(pdev->dev.of_node, "qcom,rpm-msg-ram", NULL))
+@@ -914,13 +913,35 @@ static int qcom_smem_probe(struct platfo
+ smem->dev = &pdev->dev;
+ smem->num_regions = num_regions;
+
+- ret = qcom_smem_map_memory(smem, &pdev->dev, "memory-region", 0);
+- if (ret)
+- return ret;
+-
+- if (num_regions > 1 && (ret = qcom_smem_map_memory(smem, &pdev->dev,
+- "qcom,rpm-msg-ram", 1)))
+- return ret;
++ rmem = of_reserved_mem_lookup(pdev->dev.of_node);
++ if (rmem) {
++ smem->regions[0].aux_base = rmem->base;
++ smem->regions[0].size = rmem->size;
++ } else {
++ /*
++ * Fall back to the memory-region reference, if we're not a
++ * reserved-memory node.
++ */
++ ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]);
++ if (ret)
++ return ret;
++ }
++
++ if (num_regions > 1) {
++ ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]);
++ if (ret)
++ return ret;
++ }
++
++ for (i = 0; i < num_regions; i++) {
++ smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev,
++ smem->regions[i].aux_base,
++ smem->regions[i].size);
++ if (!smem->regions[i].virt_base) {
++ dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base);
++ return -ENOMEM;
++ }
++ }
+
+ header = smem->regions[0].virt_base;
+ if (le32_to_cpu(header->initialized) != 1 ||
diff --git a/target/linux/generic/backport-6.1/302-v5.16-watchdog-bcm63xx_wdt-fix-fallthrough-warning.patch b/target/linux/generic/backport-6.1/302-v5.16-watchdog-bcm63xx_wdt-fix-fallthrough-warning.patch
new file mode 100644
index 0000000000..84ae5a7fc7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/302-v5.16-watchdog-bcm63xx_wdt-fix-fallthrough-warning.patch
@@ -0,0 +1,33 @@
+From ee1a0696934a8b77a6a2098f92832c46d34ec5da Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 27 Oct 2021 14:31:35 +0200
+Subject: [PATCH] watchdog: bcm63xx_wdt: fix fallthrough warning
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This fixes:
+drivers/watchdog/bcm63xx_wdt.c: In function 'bcm63xx_wdt_ioctl':
+drivers/watchdog/bcm63xx_wdt.c:208:17: warning: this statement may fall through [-Wimplicit-fallthrough=]
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/r/20211027123135.27458-1-zajec5@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
+---
+ drivers/watchdog/bcm63xx_wdt.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/watchdog/bcm63xx_wdt.c
++++ b/drivers/watchdog/bcm63xx_wdt.c
+@@ -207,6 +207,8 @@ static long bcm63xx_wdt_ioctl(struct fil
+
+ bcm63xx_wdt_pet();
+
++ fallthrough;
++
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdt_time, p);
+
diff --git a/target/linux/generic/backport-6.1/330-v5.16-01-MIPS-kernel-proc-add-CPU-option-reporting.patch b/target/linux/generic/backport-6.1/330-v5.16-01-MIPS-kernel-proc-add-CPU-option-reporting.patch
new file mode 100644
index 0000000000..c66a3f11b4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/330-v5.16-01-MIPS-kernel-proc-add-CPU-option-reporting.patch
@@ -0,0 +1,162 @@
+From 626bfa03729959ea9917181fb3d8ffaa1594d02a Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Wed, 13 Oct 2021 22:40:18 -0700
+Subject: [PATCH 1/1] MIPS: kernel: proc: add CPU option reporting
+
+Many MIPS CPUs have optional CPU features which are not activated for
+all CPU cores. Print the CPU options, which are implemented in the core,
+in /proc/cpuinfo. This makes it possible to see which 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>
+
+Changes from original patch[0]:
+- Remove cpu_has_6k_cache and cpu_has_8k_cache due to commit 6ce91ba8589a
+ ("MIPS: Remove cpu_has_6k_cache and cpu_has_8k_cache in cpu_cache_init()")
+- Add new options: mac2008_only, ftlbparex, gsexcex, mmid, mm_sysad,
+ mm_full
+- Use seq_puts instead of seq_printf as suggested by checkpatch
+- Minor commit message reword
+
+[0]: https://lore.kernel.org/linux-mips/20181223225224.23042-1-hauke@hauke-m.de/
+
+Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
+Acked-by: Hauke Mehrtens <hauke@hauke-m.de>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ arch/mips/kernel/proc.c | 122 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 122 insertions(+)
+
+--- a/arch/mips/kernel/proc.c
++++ b/arch/mips/kernel/proc.c
+@@ -138,6 +138,128 @@ 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_puts(m, "Options implemented\t:");
++ if (cpu_has_tlb)
++ seq_puts(m, " tlb");
++ if (cpu_has_ftlb)
++ seq_puts(m, " ftlb");
++ if (cpu_has_tlbinv)
++ seq_puts(m, " tlbinv");
++ if (cpu_has_segments)
++ seq_puts(m, " segments");
++ if (cpu_has_rixiex)
++ seq_puts(m, " rixiex");
++ if (cpu_has_ldpte)
++ seq_puts(m, " ldpte");
++ if (cpu_has_maar)
++ seq_puts(m, " maar");
++ if (cpu_has_rw_llb)
++ seq_puts(m, " rw_llb");
++ if (cpu_has_4kex)
++ seq_puts(m, " 4kex");
++ if (cpu_has_3k_cache)
++ seq_puts(m, " 3k_cache");
++ if (cpu_has_4k_cache)
++ seq_puts(m, " 4k_cache");
++ if (cpu_has_tx39_cache)
++ seq_puts(m, " tx39_cache");
++ if (cpu_has_octeon_cache)
++ seq_puts(m, " octeon_cache");
++ if (cpu_has_fpu)
++ seq_puts(m, " fpu");
++ if (cpu_has_32fpr)
++ seq_puts(m, " 32fpr");
++ if (cpu_has_cache_cdex_p)
++ seq_puts(m, " cache_cdex_p");
++ if (cpu_has_cache_cdex_s)
++ seq_puts(m, " cache_cdex_s");
++ if (cpu_has_prefetch)
++ seq_puts(m, " prefetch");
++ if (cpu_has_mcheck)
++ seq_puts(m, " mcheck");
++ if (cpu_has_ejtag)
++ seq_puts(m, " ejtag");
++ if (cpu_has_llsc)
++ seq_puts(m, " llsc");
++ if (cpu_has_guestctl0ext)
++ seq_puts(m, " guestctl0ext");
++ if (cpu_has_guestctl1)
++ seq_puts(m, " guestctl1");
++ if (cpu_has_guestctl2)
++ seq_puts(m, " guestctl2");
++ if (cpu_has_guestid)
++ seq_puts(m, " guestid");
++ if (cpu_has_drg)
++ seq_puts(m, " drg");
++ if (cpu_has_rixi)
++ seq_puts(m, " rixi");
++ if (cpu_has_lpa)
++ seq_puts(m, " lpa");
++ if (cpu_has_mvh)
++ seq_puts(m, " mvh");
++ if (cpu_has_vtag_icache)
++ seq_puts(m, " vtag_icache");
++ if (cpu_has_dc_aliases)
++ seq_puts(m, " dc_aliases");
++ if (cpu_has_ic_fills_f_dc)
++ seq_puts(m, " ic_fills_f_dc");
++ if (cpu_has_pindexed_dcache)
++ seq_puts(m, " pindexed_dcache");
++ if (cpu_has_userlocal)
++ seq_puts(m, " userlocal");
++ if (cpu_has_nofpuex)
++ seq_puts(m, " nofpuex");
++ if (cpu_has_vint)
++ seq_puts(m, " vint");
++ if (cpu_has_veic)
++ seq_puts(m, " veic");
++ if (cpu_has_inclusive_pcaches)
++ seq_puts(m, " inclusive_pcaches");
++ if (cpu_has_perf_cntr_intr_bit)
++ seq_puts(m, " perf_cntr_intr_bit");
++ if (cpu_has_ufr)
++ seq_puts(m, " ufr");
++ if (cpu_has_fre)
++ seq_puts(m, " fre");
++ if (cpu_has_cdmm)
++ seq_puts(m, " cdmm");
++ if (cpu_has_small_pages)
++ seq_puts(m, " small_pages");
++ if (cpu_has_nan_legacy)
++ seq_puts(m, " nan_legacy");
++ if (cpu_has_nan_2008)
++ seq_puts(m, " nan_2008");
++ if (cpu_has_ebase_wg)
++ seq_puts(m, " ebase_wg");
++ if (cpu_has_badinstr)
++ seq_puts(m, " badinstr");
++ if (cpu_has_badinstrp)
++ seq_puts(m, " badinstrp");
++ if (cpu_has_contextconfig)
++ seq_puts(m, " contextconfig");
++ if (cpu_has_perf)
++ seq_puts(m, " perf");
++ if (cpu_has_mac2008_only)
++ seq_puts(m, " mac2008_only");
++ if (cpu_has_ftlbparex)
++ seq_puts(m, " ftlbparex");
++ if (cpu_has_gsexcex)
++ seq_puts(m, " gsexcex");
++ if (cpu_has_shared_ftlb_ram)
++ seq_puts(m, " shared_ftlb_ram");
++ if (cpu_has_shared_ftlb_entries)
++ seq_puts(m, " shared_ftlb_entries");
++ if (cpu_has_mipsmt_pertccounters)
++ seq_puts(m, " mipsmt_pertccounters");
++ if (cpu_has_mmid)
++ seq_puts(m, " mmid");
++ if (cpu_has_mm_sysad)
++ seq_puts(m, " mm_sysad");
++ if (cpu_has_mm_full)
++ seq_puts(m, " mm_full");
++ seq_puts(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/backport-6.1/330-v5.16-02-MIPS-Fix-using-smp_processor_id-in-preemptible-in-sh.patch b/target/linux/generic/backport-6.1/330-v5.16-02-MIPS-Fix-using-smp_processor_id-in-preemptible-in-sh.patch
new file mode 100644
index 0000000000..6caf7d06d4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/330-v5.16-02-MIPS-Fix-using-smp_processor_id-in-preemptible-in-sh.patch
@@ -0,0 +1,62 @@
+From 1cab5bd69eb1f995ced2d7576cb15f8a8941fd85 Mon Sep 17 00:00:00 2001
+From: Tiezhu Yang <yangtiezhu@loongson.cn>
+Date: Thu, 25 Nov 2021 19:39:32 +0800
+Subject: [PATCH 1/1] MIPS: Fix using smp_processor_id() in preemptible in
+ show_cpuinfo()
+
+There exists the following issue under DEBUG_PREEMPT:
+
+ BUG: using smp_processor_id() in preemptible [00000000] code: systemd/1
+ caller is show_cpuinfo+0x460/0xea0
+ ...
+ Call Trace:
+ [<ffffffff8020f0dc>] show_stack+0x94/0x128
+ [<ffffffff80e6cab4>] dump_stack_lvl+0x94/0xd8
+ [<ffffffff80e74c5c>] check_preemption_disabled+0x104/0x110
+ [<ffffffff802209c8>] show_cpuinfo+0x460/0xea0
+ [<ffffffff80539d54>] seq_read_iter+0xfc/0x4f8
+ [<ffffffff804fcc10>] new_sync_read+0x110/0x1b8
+ [<ffffffff804ff57c>] vfs_read+0x1b4/0x1d0
+ [<ffffffff804ffb18>] ksys_read+0xd0/0x110
+ [<ffffffff8021c090>] syscall_common+0x34/0x58
+
+We can see the following call trace:
+ show_cpuinfo()
+ cpu_has_fpu
+ current_cpu_data
+ smp_processor_id()
+
+ $ addr2line -f -e vmlinux 0xffffffff802209c8
+ show_cpuinfo
+ arch/mips/kernel/proc.c:188
+
+ $ head -188 arch/mips/kernel/proc.c | tail -1
+ if (cpu_has_fpu)
+
+ arch/mips/include/asm/cpu-features.h
+ # define cpu_has_fpu (current_cpu_data.options & MIPS_CPU_FPU)
+
+ arch/mips/include/asm/cpu-info.h
+ #define current_cpu_data cpu_data[smp_processor_id()]
+
+Based on the above analysis, fix the issue by using raw_cpu_has_fpu
+which calls raw_smp_processor_id() in show_cpuinfo().
+
+Fixes: 626bfa037299 ("MIPS: kernel: proc: add CPU option reporting")
+Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ arch/mips/kernel/proc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/mips/kernel/proc.c
++++ b/arch/mips/kernel/proc.c
+@@ -166,7 +166,7 @@ static int show_cpuinfo(struct seq_file
+ seq_puts(m, " tx39_cache");
+ if (cpu_has_octeon_cache)
+ seq_puts(m, " octeon_cache");
+- if (cpu_has_fpu)
++ if (raw_cpu_has_fpu)
+ seq_puts(m, " fpu");
+ if (cpu_has_32fpr)
+ seq_puts(m, " 32fpr");
diff --git a/target/linux/generic/backport-6.1/331-v5.19-mtd-spinand-Add-support-for-XTX-XT26G0xA.patch b/target/linux/generic/backport-6.1/331-v5.19-mtd-spinand-Add-support-for-XTX-XT26G0xA.patch
new file mode 100644
index 0000000000..541d247542
--- /dev/null
+++ b/target/linux/generic/backport-6.1/331-v5.19-mtd-spinand-Add-support-for-XTX-XT26G0xA.patch
@@ -0,0 +1,186 @@
+From f4c5c7f9d2e5ab005d57826b740b694b042a737c Mon Sep 17 00:00:00 2001
+From: Felix Matouschek <felix@matouschek.org>
+Date: Mon, 18 Apr 2022 15:28:03 +0200
+Subject: [PATCH 1/1] 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.
+
+Links:
+ - 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>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220418132803.664103-1-felix@matouschek.org
+---
+ drivers/mtd/nand/spi/Makefile | 2 +-
+ drivers/mtd/nand/spi/core.c | 1 +
+ drivers/mtd/nand/spi/xtx.c | 129 ++++++++++++++++++++++++++++++++++
+ include/linux/mtd/spinand.h | 1 +
+ 4 files changed, 132 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,129 @@
++// 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)
++{
++ status = status & XT26G0XA_STATUS_ECC_MASK;
++
++ switch (status) {
++ 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:
++ break;
++ }
++
++ /* At this point values greater than (2 << 4) are invalid */
++ if (status > XT26G0XA_STATUS_ECC_UNCOR_ERROR)
++ return -EINVAL;
++
++ /* (1 << 2) through (7 << 2) are 1-7 corrected errors */
++ return status >> 2;
++}
++
++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/backport-6.1/344-v5.18-01-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch b/target/linux/generic/backport-6.1/344-v5.18-01-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch
new file mode 100644
index 0000000000..d6c4b4fd1e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/344-v5.18-01-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch
@@ -0,0 +1,219 @@
+From 4bf18d5a2dd02db8c5b16a2cfae513510506df5b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 3 Feb 2022 22:44:40 +0100
+Subject: [PATCH 1/2] 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>
+Link: https://lore.kernel.org/r/20220203214444.1508-2-kabel@kernel.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ 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),
+ };
+
+@@ -98,7 +93,6 @@ struct mvebu_a3700_comphy_lane {
+ unsigned int id;
+ enum phy_mode mode;
+ int submode;
+- int port;
+ };
+
+ static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
+@@ -120,7 +114,7 @@ static int mvebu_a3700_comphy_smc(unsign
+ }
+ }
+
+-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
+
+ 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;
+
+- 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;
+
+- 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;
+
+ lane = phy_get_drvdata(phy);
+- lane->port = args->args[0];
++
++ 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);
+ }
+
diff --git a/target/linux/generic/backport-6.1/344-v5.18-02-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch b/target/linux/generic/backport-6.1/344-v5.18-02-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch
new file mode 100644
index 0000000000..4593d14bfe
--- /dev/null
+++ b/target/linux/generic/backport-6.1/344-v5.18-02-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch
@@ -0,0 +1,1552 @@
+From 934337080c6c59b75db76b180b509f218640ad48 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 3 Feb 2022 22:44:41 +0100
+Subject: [PATCH 2/2] 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
+
+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.
+
+[ 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: Pali Rohár <pali@kernel.org>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Link: https://lore.kernel.org/r/20220203214444.1508-3-kabel@kernel.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 1332 ++++++++++++++++--
+ 1 file changed, 1215 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,1118 @@
+ #include <linux/phy.h>
+ #include <linux/phy/phy.h>
+ #include <linux/platform_device.h>
++#include <linux/spinlock.h>
+
+-#define MVEBU_A3700_COMPHY_LANES 3
++#define PLL_SET_DELAY_US 600
++#define COMPHY_PLL_SLEEP 1000
++#define COMPHY_PLL_TIMEOUT 150000
++
++/* Comphy lane2 indirect access register offset */
++#define COMPHY_LANE2_INDIR_ADDR 0x0
++#define COMPHY_LANE2_INDIR_DATA 0x4
++
++/* SATA and USB3 PHY offset compared to SATA PHY */
++#define COMPHY_LANE2_REGS_BASE 0x200
++
++/*
++ * 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)
++
++/*
++ * 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)
+
+-/* 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))
++
++/*
++ * 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)
++
++/*
++ * 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, \
+ }
+
+-#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;
+ };
+
+ 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 }
+ };
+
+-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 */
++};
++
++static inline void comphy_reg_set(void __iomem *addr, u32 data, u32 mask)
+ {
+- struct arm_smccc_res res;
+- s32 ret;
++ u32 val;
++
++ val = readl(addr);
++ val = (val & ~mask) | (data & mask);
++ writel(val, addr);
++}
+
+- arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
+- ret = res.a0;
++static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask)
++{
++ u16 val;
+
+- switch (ret) {
+- case SMCCC_RET_SUCCESS:
+- return 0;
+- case SMCCC_RET_NOT_SUPPORTED:
+- return -EOPNOTSUPP;
++ val = readw(addr);
++ val = (val & ~mask) | (data & mask);
++ writew(val, addr);
++}
++
++/* 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);
++}
++
++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);
++ } else {
++ void __iomem *base = lane->id == 1 ?
++ lane->priv->lane1_phy_regs :
++ lane->priv->lane0_phy_regs;
++
++ comphy_reg_set16(base + COMPHY_LANE_REG_DIRECT(reg),
++ data, mask);
++ }
++}
++
++static int comphy_lane_reg_poll(struct mvebu_a3700_comphy_lane *lane,
++ u16 reg, u16 bits,
++ ulong sleep_us, ulong timeout_us)
++{
++ int ret;
++
++ if (lane->id == 2) {
++ u32 data;
++
++ /* lane 2 PHY registers are accessed indirectly */
++ writel(reg + COMPHY_LANE2_REGS_BASE,
++ lane->priv->lane2_phy_indirect +
++ COMPHY_LANE2_INDIR_ADDR);
++
++ 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);
++ }
++
++ return ret;
++}
++
++static void comphy_periph_reg_set(struct mvebu_a3700_comphy_lane *lane,
++ u8 reg, u32 data, u32 mask)
++{
++ comphy_reg_set(lane->priv->comphy_regs + COMPHY_PHY_REG(lane->id, reg),
++ data, mask);
++}
++
++static int comphy_periph_reg_poll(struct mvebu_a3700_comphy_lane *lane,
++ u8 reg, u32 bits,
++ ulong sleep_us, ulong timeout_us)
++{
++ u32 data;
++
++ return readl_poll_timeout(lane->priv->comphy_regs +
++ COMPHY_PHY_REG(lane->id, reg),
++ data, (data & bits) == bits,
++ sleep_us, timeout_us);
++}
++
++/* PHY selector configures with corresponding modes */
++static int
++mvebu_a3700_comphy_set_phy_selector(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 old, new, clr = 0, set = 0;
++ unsigned long flags;
++
++ 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;
++
++ 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;
++ 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;
++ break;
++
++ case PHY_MODE_PCIE:
++ /* PCIE must be in Lane1 */
++ if (lane->id == 1)
++ set = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT;
++ else
++ goto error;
++ break;
++
++ default:
++ goto error;
++ }
++
++ 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;
++}
++
++static int
++mvebu_a3700_comphy_sata_power_on(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 mask, data, ref_clk;
++ int ret;
++
++ /* Configure phy selector for SATA */
++ ret = mvebu_a3700_comphy_set_phy_selector(lane);
++ if (ret)
++ return ret;
++
++ /* 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));
++ */
++
++ /* Wait for > 55 us to allow PLL be enabled */
++ udelay(PLL_SET_DELAY_US);
++
++ /* 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;
++}
++
++static void comphy_gbe_phy_init(struct mvebu_a3700_comphy_lane *lane,
++ bool is_1gbps)
++{
++ int addr, fix_idx;
++ u16 val;
++
++ 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];
++ }
++
++ comphy_lane_reg_set(lane, addr, val, 0xFFFF);
++ }
++}
++
++static int
++mvebu_a3700_comphy_ethernet_power_on(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 mask, data, speed_sel;
++ int ret;
++
++ /* Set selector */
++ ret = mvebu_a3700_comphy_set_phy_selector(lane);
++ if (ret)
++ return ret;
++
++ /*
++ * 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);
++
++ /* 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);
++
++ /*
++ * 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);
++
++ /*
++ * 6. Wait 10mS for bandgap and reference clocks to stabilize; then
++ * start SW programming.
++ */
++ mdelay(10);
++
++ /* 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);
++
++ /*
++ * 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);
++
++ /*
++ * 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;
++
++ mask = REF_FREF_SEL_MASK;
++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask);
++
++ /*
++ * 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
++ */
++
++ /*
++ * 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);
++
++ /*
++ * 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 value of the DFE_UPDATE_EN already is 0x3F, because it is the
++ * default value after reset of the PHY.
++ */
++
++ /*
++ * 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);
++
++ /*
++ * 14. 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);
++
++ /*
++ * 15. 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);
++
++ /*
++ * 16. 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;
++ }
++
++ /*
++ * 17. Set COMPHY input port PIN_TX_IDLE=0
++ */
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT);
++
++ /*
++ * 18. 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;
++ }
++
++ 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);
++
++ return ret;
+ }
+
+-static int mvebu_a3700_comphy_get_fw_mode(int lane,
++static int
++mvebu_a3700_comphy_usb3_power_on(struct mvebu_a3700_comphy_lane *lane)
++{
++ u32 mask, data, cfg, ref_clk;
++ int ret;
++
++ /* Set phy seclector */
++ ret = mvebu_a3700_comphy_set_phy_selector(lane);
++ if (ret)
++ return ret;
++
++ /*
++ * 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.
++ */
++
++ /*
++ * 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);
++
++ /*
++ * 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);
++
++ /*
++ * 3. Set Spread Spectrum Clock Enabled
++ */
++ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG4,
++ SPREAD_SPECTRUM_CLK_EN, SPREAD_SPECTRUM_CLK_EN);
++
++ /*
++ * 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);
++
++ /*
++ * 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);
++
++ /*
++ * 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);
++
++ /*
++ * 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);
++
++ /*
++ * 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;
++ }
++
++ 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);
++
++ 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);
++
++ /*
++ * 9. Enable idle sync
++ */
++ comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN,
++ IDLE_SYNC_EN, IDLE_SYNC_EN);
++
++ /*
++ * 10. Enable the output of 500M clock
++ */
++ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, CLK500M_EN, CLK500M_EN);
++
++ /*
++ * 11. Set 20-bit data width
++ */
++ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN,
++ DATA_WIDTH_20BIT, 0xFFFF);
++
++ /*
++ * 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);
++
++ /*
++ * 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);
++
++ /*
++ * 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);
++
++ /*
++ * 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);
++
++ /*
++ * 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);
++
++ /* 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 USB3 PLL\n");
++
++ return ret;
++}
++
++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);
++
++ /* 2. Select 20 bit SERDES interface. */
++ comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO,
++ CFG_SEL_20B, CFG_SEL_20B);
++
++ /* 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);
++
++ /* 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);
++
++ /* 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);
++
++ /*
++ * 7. Enable TX, PCIE global register, 0xd0074814, it is done in
++ * PCI-E driver
++ */
++
++ /*
++ * 8. Check crystal jumper setting and program the Power and PLL
++ * Control accordingly
++ */
++
++ 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 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);
++
++ /* 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 void
++mvebu_a3700_comphy_ethernet_power_off(struct mvebu_a3700_comphy_lane *lane)
++{
++ 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);
++}
++
++static void
++mvebu_a3700_comphy_pcie_power_off(struct mvebu_a3700_comphy_lane *lane)
++{
++ /* 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 int mvebu_a3700_comphy_reset(struct phy *phy)
++{
++ 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;
++}
++
++static bool mvebu_a3700_comphy_check_mode(int lane,
+ enum phy_mode mode,
+ int submode)
+ {
+@@ -122,7 +1141,7 @@ static int mvebu_a3700_comphy_get_fw_mod
+
+ /* Unused PHY mux value is 0x0 */
+ if (mode == PHY_MODE_INVALID)
+- return -EINVAL;
++ return false;
+
+ for (i = 0; i < n; i++) {
+ if (mvebu_a3700_comphy_modes[i].lane == lane &&
+@@ -132,27 +1151,30 @@ static int mvebu_a3700_comphy_get_fw_mod
+ }
+
+ if (i == n)
+- return -EINVAL;
++ return false;
+
+- return mvebu_a3700_comphy_modes[i].fw_mode;
++ return true;
+ }
+
+ 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;
+ }
+
++ /* Mode cannot be changed while the PHY is powered on */
++ if (phy->power_count &&
++ (lane->mode != mode || lane->submode != submode))
++ return -EBUSY;
++
++ /* If changing mode, ensure reset is called */
++ if (lane->mode != PHY_MODE_INVALID && lane->mode != mode)
++ lane->needs_reset = true;
++
+ /* Just remember the mode, ->power_on() will do the real setup */
+ lane->mode = mode;
+ lane->submode = submode;
+@@ -163,76 +1185,77 @@ 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;
+
+- 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;
++ }
++
++ if (lane->needs_reset) {
++ ret = mvebu_a3700_comphy_reset(phy);
++ if (ret)
++ return ret;
++
++ 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;
+ }
+-
+- 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);
+
+- return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
++ switch (lane->mode) {
++ case PHY_MODE_USB_HOST_SS:
++ /*
++ * The USB3 MAC sets the USB3 PHY to low state, so we do not
++ * need to power off USB3 PHY again.
++ */
++ break;
++
++ case PHY_MODE_SATA:
++ mvebu_a3700_comphy_sata_power_off(lane);
++ break;
++
++ case PHY_MODE_ETHERNET:
++ mvebu_a3700_comphy_ethernet_power_off(lane);
++ break;
++
++ case PHY_MODE_PCIE:
++ mvebu_a3700_comphy_pcie_power_off(lane);
++ break;
++
++ default:
++ dev_err(lane->dev, "invalid COMPHY mode\n");
++ return -EINVAL;
++ }
++
++ return 0;
+ }
+
+ 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 +1279,75 @@ static struct phy *mvebu_a3700_comphy_xl
+ return ERR_PTR(-EINVAL);
+ }
+
++ lane->invert_tx = args->args[1] & BIT(0);
++ lane->invert_rx = args->args[1] & BIT(1);
++
+ return phy;
+ }
+
+ 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);
++
++ 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);
++
++ /*
++ * 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);
++ }
++ 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 +1362,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,15 +1380,26 @@ 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,
+ mvebu_a3700_comphy_xlate);
++
+ return PTR_ERR_OR_ZERO(provider);
+ }
+
+@@ -323,5 +1419,7 @@ static struct platform_driver mvebu_a370
+ module_platform_driver(mvebu_a3700_comphy_driver);
+
+ 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/backport-6.1/345-v5.17-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch b/target/linux/generic/backport-6.1/345-v5.17-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch
new file mode 100644
index 0000000000..03b6a5754d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/345-v5.17-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch
@@ -0,0 +1,32 @@
+From 73a78b6130d9e13daca22b86ad52f063b9403e03 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 8 Dec 2021 03:40:35 +0100
+Subject: [PATCH 1/1] arm64: dts: marvell: armada-37xx: Add xtal clock to
+ comphy node
+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).
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.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/backport-6.1/346-v5.18-01-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch b/target/linux/generic/backport-6.1/346-v5.18-01-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch
new file mode 100644
index 0000000000..b254e7c0b3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/346-v5.18-01-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch
@@ -0,0 +1,64 @@
+From ee995101fde67f85a3cd4c74f4f92fc4592e726b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 3 Feb 2022 22:44:42 +0100
+Subject: [PATCH 1/3] 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.
+
+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>
+Acked-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+Link: https://lore.kernel.org/r/20220203214444.1508-4-kabel@kernel.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ 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
+ }
+
+ 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/backport-6.1/346-v5.18-02-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch b/target/linux/generic/backport-6.1/346-v5.18-02-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch
new file mode 100644
index 0000000000..1e8afb7bbf
--- /dev/null
+++ b/target/linux/generic/backport-6.1/346-v5.18-02-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch
@@ -0,0 +1,166 @@
+From 8e10548f7f4814e530857d2049d6af6bc78add53 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 3 Feb 2022 22:44:43 +0100
+Subject: [PATCH 2/3] Revert "usb: host: xhci: mvebu: make USB 3.0 PHY optional
+ for Armada 3720"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit 3241929b67d28c83945d3191c6816a3271fd6b85.
+
+Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return
+-EOPNOTSUPP from phy_power_on() callback anymore.
+
+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.
+
+xhci plat_setup callback is not used by any other xhci plat driver, so
+remove this callback completely.
+
+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>
+Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Link: https://lore.kernel.org/r/20220203214444.1508-5-kabel@kernel.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ 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;
+ }
+-
+-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)
+ {
+--- 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);
+ }
+
+-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_
+ };
+
+ static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
+- .plat_setup = xhci_mvebu_a3700_plat_setup,
+ .init_quirk = xhci_mvebu_a3700_init_quirk,
+ };
+
+@@ -341,14 +330,7 @@ static int xhci_plat_probe(struct platfo
+
+ hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
+ xhci->shared_hcd->tpl_support = hcd->tpl_support;
+-
+- if (priv) {
+- ret = xhci_priv_plat_setup(hcd);
+- if (ret)
+- goto disable_usb_phy;
+- }
+-
+- 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/backport-6.1/346-v5.18-03-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch b/target/linux/generic/backport-6.1/346-v5.18-03-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch
new file mode 100644
index 0000000000..fcfb02d35a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/346-v5.18-03-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch
@@ -0,0 +1,39 @@
+From 9a4556dad7bd0a6b8339cb72e169f5c76f2af6f1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 3 Feb 2022 22:44:44 +0100
+Subject: [PATCH 3/3] 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>
+Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Link: https://lore.kernel.org/r/20220203214444.1508-6-kabel@kernel.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ 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
+@@ -1642,9 +1642,7 @@ static int advk_pcie_enable_phy(struct a
+ }
+
+ 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;
+ }
diff --git a/target/linux/generic/backport-6.1/347-v6.0-phy-marvell-phy-mvebu-a3700-comphy-Remove-broken-res.patch b/target/linux/generic/backport-6.1/347-v6.0-phy-marvell-phy-mvebu-a3700-comphy-Remove-broken-res.patch
new file mode 100644
index 0000000000..a2c897b7a9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/347-v6.0-phy-marvell-phy-mvebu-a3700-comphy-Remove-broken-res.patch
@@ -0,0 +1,194 @@
+From 0a6fc70d76bddf98278af2ac000379c82aec8f11 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 29 Aug 2022 10:30:46 +0200
+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Remove broken reset
+ support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reset support for SATA PHY is somehow broken and after calling it, kernel
+is not able to detect and initialize SATA disk Samsung SSD 850 EMT0 [1].
+
+Reset support was introduced in commit 934337080c6c ("phy: marvell:
+phy-mvebu-a3700-comphy: Add native kernel implementation") as part of
+complete rewrite of this driver. v1 patch series of that commit [2] did
+not contain reset support and was tested that is working fine with
+Ethernet, SATA and USB PHYs without issues too.
+
+So for now remove broken reset support and change implementation of
+power_off callback to power off all functions on specified lane (and not
+only selected function) because during startup kernel does not know which
+function was selected and configured by bootloader. Same logic was used
+also in v1 patch series of that commit.
+
+This change fixes issues with initialization of SATA disk Samsung SSD 850
+and disk is working again, like before mentioned commit.
+
+Once problem with PHY reset callback is solved its functionality could be
+re-introduced. But for now it is unknown why it does not work.
+
+[1] - https://lore.kernel.org/r/20220531124159.3e4lgn2v462irbtz@shindev/
+[2] - https://lore.kernel.org/r/20211028184242.22105-1-kabel@kernel.org/
+
+Reported-by: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
+Fixes: 934337080c6c ("phy: marvell: phy-mvebu-a3700-comphy: Add native kernel implementation")
+Cc: stable@vger.kernel.org # v5.18+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Tested-by: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
+Link: https://lore.kernel.org/r/20220829083046.15082-1-pali@kernel.org
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 87 ++++----------------
+ 1 file changed, 17 insertions(+), 70 deletions(-)
+
+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+@@ -274,7 +274,6 @@ struct mvebu_a3700_comphy_lane {
+ int submode;
+ bool invert_tx;
+ bool invert_rx;
+- bool needs_reset;
+ };
+
+ struct gbe_phy_init_data_fix {
+@@ -1097,40 +1096,12 @@ mvebu_a3700_comphy_pcie_power_off(struct
+ 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT);
+ }
+
+-static int mvebu_a3700_comphy_reset(struct phy *phy)
++static void mvebu_a3700_comphy_usb3_power_off(struct mvebu_a3700_comphy_lane *lane)
+ {
+- 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;
++ /*
++ * The USB3 MAC sets the USB3 PHY to low state, so we do not
++ * need to power off USB3 PHY again.
++ */
+ }
+
+ static bool mvebu_a3700_comphy_check_mode(int lane,
+@@ -1171,10 +1142,6 @@ static int mvebu_a3700_comphy_set_mode(s
+ (lane->mode != mode || lane->submode != submode))
+ return -EBUSY;
+
+- /* If changing mode, ensure reset is called */
+- if (lane->mode != PHY_MODE_INVALID && lane->mode != mode)
+- lane->needs_reset = true;
+-
+ /* Just remember the mode, ->power_on() will do the real setup */
+ lane->mode = mode;
+ lane->submode = submode;
+@@ -1185,7 +1152,6 @@ 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);
+- int ret;
+
+ if (!mvebu_a3700_comphy_check_mode(lane->id, lane->mode,
+ lane->submode)) {
+@@ -1193,14 +1159,6 @@ static int mvebu_a3700_comphy_power_on(s
+ return -EINVAL;
+ }
+
+- if (lane->needs_reset) {
+- ret = mvebu_a3700_comphy_reset(phy);
+- if (ret)
+- return ret;
+-
+- 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);
+@@ -1224,38 +1182,28 @@ static int mvebu_a3700_comphy_power_off(
+ {
+ struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
+
+- switch (lane->mode) {
+- case PHY_MODE_USB_HOST_SS:
+- /*
+- * The USB3 MAC sets the USB3 PHY to low state, so we do not
+- * need to power off USB3 PHY again.
+- */
+- break;
+-
+- case PHY_MODE_SATA:
+- mvebu_a3700_comphy_sata_power_off(lane);
+- break;
+-
+- case PHY_MODE_ETHERNET:
++ switch (lane->id) {
++ case 0:
++ mvebu_a3700_comphy_usb3_power_off(lane);
+ mvebu_a3700_comphy_ethernet_power_off(lane);
+- break;
+-
+- case PHY_MODE_PCIE:
++ return 0;
++ case 1:
+ mvebu_a3700_comphy_pcie_power_off(lane);
+- break;
+-
++ mvebu_a3700_comphy_ethernet_power_off(lane);
++ return 0;
++ case 2:
++ mvebu_a3700_comphy_usb3_power_off(lane);
++ mvebu_a3700_comphy_sata_power_off(lane);
++ return 0;
+ default:
+ dev_err(lane->dev, "invalid COMPHY mode\n");
+ return -EINVAL;
+ }
+-
+- return 0;
+ }
+
+ 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,
+ };
+@@ -1393,8 +1341,7 @@ static int mvebu_a3700_comphy_probe(stru
+ * To avoid relying on the bootloader/firmware configuration,
+ * power off all comphys.
+ */
+- mvebu_a3700_comphy_reset(phy);
+- lane->needs_reset = false;
++ mvebu_a3700_comphy_power_off(phy);
+ }
+
+ provider = devm_of_phy_provider_register(&pdev->dev,
diff --git a/target/linux/generic/backport-6.1/350-v5.18-regmap-add-configurable-downshift-for-addresses.patch b/target/linux/generic/backport-6.1/350-v5.18-regmap-add-configurable-downshift-for-addresses.patch
new file mode 100644
index 0000000000..99cd89ea00
--- /dev/null
+++ b/target/linux/generic/backport-6.1/350-v5.18-regmap-add-configurable-downshift-for-addresses.patch
@@ -0,0 +1,90 @@
+From 86fc59ef818beb0e1945d17f8e734898baba7e4e Mon Sep 17 00:00:00 2001
+From: Colin Foster <colin.foster@in-advantage.com>
+Date: Sun, 13 Mar 2022 15:45:23 -0700
+Subject: [PATCH 1/2] regmap: add configurable downshift for addresses
+
+Add an additional reg_downshift to be applied to register addresses before
+any register accesses. An example of a device that uses this is a VSC7514
+chip, which require each register address to be downshifted by two if the
+access is performed over a SPI bus.
+
+Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
+Link: https://lore.kernel.org/r/20220313224524.399947-2-colin.foster@in-advantage.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/base/regmap/internal.h | 1 +
+ drivers/base/regmap/regmap.c | 5 +++++
+ include/linux/regmap.h | 3 +++
+ 3 files changed, 9 insertions(+)
+
+--- a/drivers/base/regmap/internal.h
++++ b/drivers/base/regmap/internal.h
+@@ -31,6 +31,7 @@ struct regmap_format {
+ size_t buf_size;
+ size_t reg_bytes;
+ size_t pad_bytes;
++ size_t reg_downshift;
+ size_t val_bytes;
+ void (*format_write)(struct regmap *map,
+ unsigned int reg, unsigned int val);
+--- a/drivers/base/regmap/regmap.c
++++ b/drivers/base/regmap/regmap.c
+@@ -823,6 +823,7 @@ struct regmap *__regmap_init(struct devi
+
+ map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
+ map->format.pad_bytes = config->pad_bits / 8;
++ map->format.reg_downshift = config->reg_downshift;
+ map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
+ map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
+ config->val_bits + config->pad_bits, 8);
+@@ -1735,6 +1736,7 @@ static int _regmap_raw_write_impl(struct
+ return ret;
+ }
+
++ reg >>= map->format.reg_downshift;
+ map->format.format_reg(map->work_buf, reg, map->reg_shift);
+ regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+ map->write_flag_mask);
+@@ -1905,6 +1907,7 @@ static int _regmap_bus_formatted_write(v
+ return ret;
+ }
+
++ reg >>= map->format.reg_downshift;
+ map->format.format_write(map, reg, val);
+
+ trace_regmap_hw_write_start(map, reg, 1);
+@@ -2346,6 +2349,7 @@ static int _regmap_raw_multi_reg_write(s
+ unsigned int reg = regs[i].reg;
+ unsigned int val = regs[i].def;
+ trace_regmap_hw_write_start(map, reg, 1);
++ reg >>= map->format.reg_downshift;
+ map->format.format_reg(u8, reg, map->reg_shift);
+ u8 += reg_bytes + pad_bytes;
+ map->format.format_val(u8, val, 0);
+@@ -2673,6 +2677,7 @@ static int _regmap_raw_read(struct regma
+ return ret;
+ }
+
++ reg >>= map->format.reg_downshift;
+ map->format.format_reg(map->work_buf, reg, map->reg_shift);
+ regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+ map->read_flag_mask);
+--- a/include/linux/regmap.h
++++ b/include/linux/regmap.h
+@@ -237,6 +237,8 @@ typedef void (*regmap_unlock)(void *);
+ * @reg_stride: The register address stride. Valid register addresses are a
+ * multiple of this value. If set to 0, a value of 1 will be
+ * used.
++ * @reg_downshift: The number of bits to downshift the register before
++ * performing any operations.
+ * @pad_bits: Number of bits of padding between register and value.
+ * @val_bits: Number of bits in a register value, mandatory.
+ *
+@@ -360,6 +362,7 @@ struct regmap_config {
+
+ int reg_bits;
+ int reg_stride;
++ int reg_downshift;
+ int pad_bits;
+ int val_bits;
+
diff --git a/target/linux/generic/backport-6.1/351-v5.18-regmap-allow-a-defined-reg_base-to-be-added-to-every.patch b/target/linux/generic/backport-6.1/351-v5.18-regmap-allow-a-defined-reg_base-to-be-added-to-every.patch
new file mode 100644
index 0000000000..0f32288fca
--- /dev/null
+++ b/target/linux/generic/backport-6.1/351-v5.18-regmap-allow-a-defined-reg_base-to-be-added-to-every.patch
@@ -0,0 +1,95 @@
+From 0074f3f2b1e43d3cedd97e47fb6980db6d2ba79e Mon Sep 17 00:00:00 2001
+From: Colin Foster <colin.foster@in-advantage.com>
+Date: Sun, 13 Mar 2022 15:45:24 -0700
+Subject: [PATCH 2/2] regmap: allow a defined reg_base to be added to every
+ address
+
+There's an inconsistency that arises when a register set can be accessed
+internally via MMIO, or externally via SPI. The VSC7514 chip allows both
+modes of operation. When internally accessed, the system utilizes __iomem,
+devm_ioremap_resource, and devm_regmap_init_mmio.
+
+For SPI it isn't possible to utilize memory-mapped IO. To properly operate,
+the resource base must be added to the register before every operation.
+
+Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
+Link: https://lore.kernel.org/r/20220313224524.399947-3-colin.foster@in-advantage.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/base/regmap/internal.h | 1 +
+ drivers/base/regmap/regmap.c | 6 ++++++
+ include/linux/regmap.h | 3 +++
+ 3 files changed, 10 insertions(+)
+
+--- a/drivers/base/regmap/internal.h
++++ b/drivers/base/regmap/internal.h
+@@ -63,6 +63,7 @@ struct regmap {
+ regmap_unlock unlock;
+ void *lock_arg; /* This is passed to lock/unlock functions */
+ gfp_t alloc_flags;
++ unsigned int reg_base;
+
+ struct device *dev; /* Device we do I/O on */
+ void *work_buf; /* Scratch buffer used to format I/O */
+--- a/drivers/base/regmap/regmap.c
++++ b/drivers/base/regmap/regmap.c
+@@ -821,6 +821,8 @@ struct regmap *__regmap_init(struct devi
+ else
+ map->alloc_flags = GFP_KERNEL;
+
++ map->reg_base = config->reg_base;
++
+ map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
+ map->format.pad_bytes = config->pad_bits / 8;
+ map->format.reg_downshift = config->reg_downshift;
+@@ -1736,6 +1738,7 @@ static int _regmap_raw_write_impl(struct
+ return ret;
+ }
+
++ reg += map->reg_base;
+ reg >>= map->format.reg_downshift;
+ map->format.format_reg(map->work_buf, reg, map->reg_shift);
+ regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+@@ -1907,6 +1910,7 @@ static int _regmap_bus_formatted_write(v
+ return ret;
+ }
+
++ reg += map->reg_base;
+ reg >>= map->format.reg_downshift;
+ map->format.format_write(map, reg, val);
+
+@@ -2349,6 +2353,7 @@ static int _regmap_raw_multi_reg_write(s
+ unsigned int reg = regs[i].reg;
+ unsigned int val = regs[i].def;
+ trace_regmap_hw_write_start(map, reg, 1);
++ reg += map->reg_base;
+ reg >>= map->format.reg_downshift;
+ map->format.format_reg(u8, reg, map->reg_shift);
+ u8 += reg_bytes + pad_bytes;
+@@ -2677,6 +2682,7 @@ static int _regmap_raw_read(struct regma
+ return ret;
+ }
+
++ reg += map->reg_base;
+ reg >>= map->format.reg_downshift;
+ map->format.format_reg(map->work_buf, reg, map->reg_shift);
+ regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+--- a/include/linux/regmap.h
++++ b/include/linux/regmap.h
+@@ -239,6 +239,8 @@ typedef void (*regmap_unlock)(void *);
+ * used.
+ * @reg_downshift: The number of bits to downshift the register before
+ * performing any operations.
++ * @reg_base: Value to be added to every register address before performing any
++ * operation.
+ * @pad_bits: Number of bits of padding between register and value.
+ * @val_bits: Number of bits in a register value, mandatory.
+ *
+@@ -363,6 +365,7 @@ struct regmap_config {
+ int reg_bits;
+ int reg_stride;
+ int reg_downshift;
++ unsigned int reg_base;
+ int pad_bits;
+ int val_bits;
+
diff --git a/target/linux/generic/backport-6.1/352-v6.3-regmap-apply-reg_base-and-reg_downshift-for-single-r.patch b/target/linux/generic/backport-6.1/352-v6.3-regmap-apply-reg_base-and-reg_downshift-for-single-r.patch
new file mode 100644
index 0000000000..804f68d23c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/352-v6.3-regmap-apply-reg_base-and-reg_downshift-for-single-r.patch
@@ -0,0 +1,57 @@
+From 697c3892d825fb78f42ec8e53bed065dd728db3e Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 30 Jan 2023 02:04:57 +0000
+Subject: [PATCH] regmap: apply reg_base and reg_downshift for single register
+ ops
+
+reg_base and reg_downshift currently don't have any effect if used with
+a regmap_bus or regmap_config which only offers single register
+operations (ie. reg_read, reg_write and optionally reg_update_bits).
+
+Fix that and take them into account also for regmap_bus with only
+reg_read and read_write operations by applying reg_base and
+reg_downshift in _regmap_bus_reg_write, _regmap_bus_reg_read.
+
+Also apply reg_base and reg_downshift in _regmap_update_bits, but only
+in case the operation is carried out with a reg_update_bits call
+defined in either regmap_bus or regmap_config.
+
+Fixes: 0074f3f2b1e43d ("regmap: allow a defined reg_base to be added to every address")
+Fixes: 86fc59ef818beb ("regmap: add configurable downshift for addresses")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Colin Foster <colin.foster@in-advantage.com>
+Link: https://lore.kernel.org/r/Y9clyVS3tQEHlUhA@makrotopia.org
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/base/regmap/regmap.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/base/regmap/regmap.c
++++ b/drivers/base/regmap/regmap.c
+@@ -1929,6 +1929,8 @@ static int _regmap_bus_reg_write(void *c
+ {
+ struct regmap *map = context;
+
++ reg += map->reg_base;
++ reg >>= map->format.reg_downshift;
+ return map->bus->reg_write(map->bus_context, reg, val);
+ }
+
+@@ -2703,6 +2705,8 @@ static int _regmap_bus_reg_read(void *co
+ {
+ struct regmap *map = context;
+
++ reg += map->reg_base;
++ reg >>= map->format.reg_downshift;
+ return map->bus->reg_read(map->bus_context, reg, val);
+ }
+
+@@ -3078,6 +3082,8 @@ static int _regmap_update_bits(struct re
+ *change = false;
+
+ if (regmap_volatile(map, reg) && map->reg_update_bits) {
++ reg += map->reg_base;
++ reg >>= map->format.reg_downshift;
+ ret = map->reg_update_bits(map->bus_context, reg, mask, val);
+ if (ret == 0 && change)
+ *change = true;
diff --git a/target/linux/generic/backport-6.1/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch b/target/linux/generic/backport-6.1/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch
new file mode 100644
index 0000000000..1f3aae13b4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch
@@ -0,0 +1,72 @@
+From bcdf0315a61a29eb753a607d3a85a4032de72d94 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 10 May 2022 15:12:59 +0200
+Subject: [PATCH] mtd: call of_platform_populate() for MTD partitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Until this change MTD subsystem supported handling partitions only with
+MTD partitions parsers. That's a specific / limited API designed around
+partitions.
+
+Some MTD partitions may however require different handling. They may
+contain specific data that needs to be parsed and somehow extracted. For
+that purpose MTD subsystem should allow binding of standard platform
+drivers.
+
+An example can be U-Boot (sub)partition with environment variables.
+There exist a "u-boot,env" DT binding for MTD (sub)partition that
+requires an NVMEM driver.
+
+Ref: 5db1c2dbc04c ("dt-bindings: nvmem: add U-Boot environment variables binding")
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220510131259.555-1-zajec5@gmail.com
+---
+ drivers/mtd/mtdpart.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -17,6 +17,7 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/err.h>
+ #include <linux/of.h>
++#include <linux/of_platform.h>
+
+ #include "mtdcore.h"
+
+@@ -577,10 +578,16 @@ static int mtd_part_of_parse(struct mtd_
+ struct mtd_part_parser *parser;
+ struct device_node *np;
+ struct property *prop;
++ struct device *dev;
+ const char *compat;
+ const char *fixed = "fixed-partitions";
+ int ret, err = 0;
+
++ dev = &master->dev;
++ /* Use parent device (controller) if the top level MTD is not registered */
++ if (!IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) && !mtd_is_partition(master))
++ dev = master->dev.parent;
++
+ np = mtd_get_of_node(master);
+ if (mtd_is_partition(master))
+ of_node_get(np);
+@@ -593,6 +600,7 @@ static int mtd_part_of_parse(struct mtd_
+ continue;
+ ret = mtd_part_do_parse(parser, master, pparts, NULL);
+ if (ret > 0) {
++ of_platform_populate(np, NULL, NULL, dev);
+ of_node_put(np);
+ return ret;
+ }
+@@ -600,6 +608,7 @@ static int mtd_part_of_parse(struct mtd_
+ if (ret < 0 && !err)
+ err = ret;
+ }
++ of_platform_populate(np, NULL, NULL, dev);
+ of_node_put(np);
+
+ /*
diff --git a/target/linux/generic/backport-6.1/401-v6.0-mtd-parsers-add-support-for-Sercomm-partitions.patch b/target/linux/generic/backport-6.1/401-v6.0-mtd-parsers-add-support-for-Sercomm-partitions.patch
new file mode 100644
index 0000000000..113a96ad42
--- /dev/null
+++ b/target/linux/generic/backport-6.1/401-v6.0-mtd-parsers-add-support-for-Sercomm-partitions.patch
@@ -0,0 +1,302 @@
+From 9b78ef0c7997052e9eaa0f7a4513d546fa17358c Mon Sep 17 00:00:00 2001
+From: Mikhail Zhilkin <csharper2005@gmail.com>
+Date: Sun, 29 May 2022 11:07:14 +0000
+Subject: [PATCH] mtd: parsers: add support for Sercomm partitions
+
+This adds an MTD partition parser for the Sercomm partition table that
+is used in some Beeline, Netgear and Sercomm routers.
+
+The Sercomm partition map table contains real partition offsets, which
+may differ from device to device depending on the number and location of
+bad blocks on NAND.
+
+Original patch (proposed by NOGUCHI Hiroshi):
+Link: https://github.com/openwrt/openwrt/pull/1318#issuecomment-420607394
+
+Signed-off-by: NOGUCHI Hiroshi <drvlabo@gmail.com>
+Signed-off-by: Mikhail Zhilkin <csharper2005@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220529110714.189732-1-csharper2005@gmail.com
+---
+ drivers/mtd/parsers/Kconfig | 9 ++
+ drivers/mtd/parsers/Makefile | 1 +
+ drivers/mtd/parsers/scpart.c | 248 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 258 insertions(+)
+ create mode 100644 drivers/mtd/parsers/scpart.c
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -186,3 +186,12 @@ config MTD_QCOMSMEM_PARTS
+ help
+ This provides support for parsing partitions from Shared Memory (SMEM)
+ for NAND and SPI flash on Qualcomm platforms.
++
++config MTD_SERCOMM_PARTS
++ tristate "Sercomm partition table parser"
++ depends on MTD && RALINK
++ help
++ This provides partitions table parser for devices with Sercomm
++ 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.
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)
+ obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
+ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+ obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
++obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
+ obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
+ obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
+ obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o
+--- /dev/null
++++ b/drivers/mtd/parsers/scpart.c
+@@ -0,0 +1,248 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * drivers/mtd/scpart.c: Sercomm Partition Parser
++ *
++ * Copyright (C) 2018 NOGUCHI Hiroshi
++ * Copyright (C) 2022 Mikhail Zhilkin
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/module.h>
++
++#define MOD_NAME "scpart"
++
++#ifdef pr_fmt
++#undef pr_fmt
++#endif
++
++#define pr_fmt(fmt) MOD_NAME ": " fmt
++
++#define ID_ALREADY_FOUND 0xffffffffUL
++
++#define MAP_OFFS_IN_BLK 0x800
++#define MAP_MIRROR_NUM 2
++
++static const char sc_part_magic[] = {
++ 'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
++};
++#define PART_MAGIC_LEN sizeof(sc_part_magic)
++
++/* assumes that all fields are set by CPU native endian */
++struct sc_part_desc {
++ uint32_t part_id;
++ uint32_t part_offs;
++ uint32_t part_bytes;
++};
++
++static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)
++{
++ return ((pdesc->part_id != 0xffffffffUL) &&
++ (pdesc->part_offs != 0xffffffffUL) &&
++ (pdesc->part_bytes != 0xffffffffUL));
++}
++
++static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
++ struct sc_part_desc **ppdesc)
++{
++ int cnt = 0;
++ int res = 0;
++ int res2;
++ loff_t offs;
++ size_t retlen;
++ struct sc_part_desc *pdesc = NULL;
++ struct sc_part_desc *tmpdesc;
++ uint8_t *buf;
++
++ buf = kzalloc(master->erasesize, GFP_KERNEL);
++ if (!buf) {
++ res = -ENOMEM;
++ goto out;
++ }
++
++ res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
++ if (res2 || retlen != master->erasesize) {
++ res = -EIO;
++ goto free;
++ }
++
++ for (offs = MAP_OFFS_IN_BLK;
++ offs < master->erasesize - sizeof(*tmpdesc);
++ offs += sizeof(*tmpdesc)) {
++ tmpdesc = (struct sc_part_desc *)&buf[offs];
++ if (!scpart_desc_is_valid(tmpdesc))
++ break;
++ cnt++;
++ }
++
++ if (cnt > 0) {
++ int bytes = cnt * sizeof(*pdesc);
++
++ pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);
++ if (!pdesc) {
++ res = -ENOMEM;
++ goto free;
++ }
++ memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
++
++ *ppdesc = pdesc;
++ res = cnt;
++ }
++
++free:
++ kfree(buf);
++
++out:
++ return res;
++}
++
++static int scpart_find_partmap(struct mtd_info *master,
++ struct sc_part_desc **ppdesc)
++{
++ int magic_found = 0;
++ int res = 0;
++ int res2;
++ loff_t offs = 0;
++ size_t retlen;
++ uint8_t rdbuf[PART_MAGIC_LEN];
++
++ while ((magic_found < MAP_MIRROR_NUM) &&
++ (offs < master->size) &&
++ !mtd_block_isbad(master, offs)) {
++ res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
++ if (res2 || retlen != PART_MAGIC_LEN) {
++ res = -EIO;
++ goto out;
++ }
++ if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
++ pr_debug("Signature found at 0x%llx\n", offs);
++ magic_found++;
++ res = scpart_scan_partmap(master, offs, ppdesc);
++ if (res > 0)
++ goto out;
++ }
++ offs += master->erasesize;
++ }
++
++out:
++ if (res > 0)
++ pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);
++ else
++ pr_info("No valid 'SC PART MAP' was found\n");
++
++ return res;
++}
++
++static int scpart_parse(struct mtd_info *master,
++ const struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ const char *partname;
++ int n;
++ int nr_scparts;
++ int nr_parts = 0;
++ int res = 0;
++ struct sc_part_desc *scpart_map = NULL;
++ struct mtd_partition *parts = NULL;
++ struct device_node *mtd_node;
++ struct device_node *ofpart_node;
++ struct device_node *pp;
++
++ mtd_node = mtd_get_of_node(master);
++ if (!mtd_node) {
++ res = -ENOENT;
++ goto out;
++ }
++
++ ofpart_node = of_get_child_by_name(mtd_node, "partitions");
++ if (!ofpart_node) {
++ pr_info("%s: 'partitions' subnode not found on %pOF.\n",
++ master->name, mtd_node);
++ res = -ENOENT;
++ goto out;
++ }
++
++ nr_scparts = scpart_find_partmap(master, &scpart_map);
++ if (nr_scparts <= 0) {
++ pr_info("No any partitions was found in 'SC PART MAP'.\n");
++ res = -ENOENT;
++ goto free;
++ }
++
++ parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),
++ GFP_KERNEL);
++ if (!parts) {
++ res = -ENOMEM;
++ goto free;
++ }
++
++ for_each_child_of_node(ofpart_node, pp) {
++ u32 scpart_id;
++
++ if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))
++ continue;
++
++ for (n = 0 ; n < nr_scparts ; n++)
++ if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
++ (scpart_id == scpart_map[n].part_id))
++ break;
++ if (n >= nr_scparts)
++ /* not match */
++ continue;
++
++ /* add the partition found in OF into MTD partition array */
++ parts[nr_parts].offset = scpart_map[n].part_offs;
++ parts[nr_parts].size = scpart_map[n].part_bytes;
++ parts[nr_parts].of_node = pp;
++
++ if (!of_property_read_string(pp, "label", &partname))
++ parts[nr_parts].name = partname;
++ if (of_property_read_bool(pp, "read-only"))
++ parts[nr_parts].mask_flags |= MTD_WRITEABLE;
++ if (of_property_read_bool(pp, "lock"))
++ parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
++
++ /* mark as 'done' */
++ scpart_map[n].part_id = ID_ALREADY_FOUND;
++
++ nr_parts++;
++ }
++
++ if (nr_parts > 0) {
++ *pparts = parts;
++ res = nr_parts;
++ } else
++ pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");
++
++ of_node_put(pp);
++
++free:
++ kfree(scpart_map);
++ if (res <= 0)
++ kfree(parts);
++
++out:
++ return res;
++}
++
++static const struct of_device_id scpart_parser_of_match_table[] = {
++ { .compatible = "sercomm,sc-partitions" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
++
++static struct mtd_part_parser scpart_parser = {
++ .parse_fn = scpart_parse,
++ .name = "scpart",
++ .of_match_table = scpart_parser_of_match_table,
++};
++module_mtd_part_parser(scpart_parser);
++
++/* mtd parsers will request the module by parser name */
++MODULE_ALIAS("scpart");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
++MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
++MODULE_DESCRIPTION("Sercomm partition parser");
diff --git a/target/linux/generic/backport-6.1/402-v6.0-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch b/target/linux/generic/backport-6.1/402-v6.0-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch
new file mode 100644
index 0000000000..ee385416d1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/402-v6.0-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch
@@ -0,0 +1,106 @@
+From ad9b10d1eaada169bd764abcab58f08538877e26 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 22 Jun 2022 03:06:28 +0200
+Subject: mtd: core: introduce of support for dynamic partitions
+
+We have many parser that register mtd partitions at runtime. One example
+is the cmdlinepart or the smem-part parser where the compatible is defined
+in the dts and the partitions gets detected and registered by the
+parser. This is problematic for the NVMEM subsystem that requires an OF
+node to detect NVMEM cells.
+
+To fix this problem, introduce an additional logic that will try to
+assign an OF node to the MTD if declared.
+
+On MTD addition, it will be checked if the MTD has an OF node and if
+not declared will check if a partition with the same label / node name is
+declared in DTS. If an exact match is found, the partition dynamically
+allocated by the parser will have a connected OF node.
+
+The NVMEM subsystem will detect the OF node and register any NVMEM cells
+declared statically in the DTS.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220622010628.30414-4-ansuelsmth@gmail.com
+---
+ drivers/mtd/mtdcore.c | 61 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 61 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -564,6 +564,66 @@ static int mtd_nvmem_add(struct mtd_info
+ return 0;
+ }
+
++static void mtd_check_of_node(struct mtd_info *mtd)
++{
++ struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
++ const char *pname, *prefix = "partition-";
++ int plen, mtd_name_len, offset, prefix_len;
++ struct mtd_info *parent;
++ bool found = false;
++
++ /* Check if MTD already has a device node */
++ if (dev_of_node(&mtd->dev))
++ return;
++
++ /* Check if a partitions node exist */
++ parent = mtd->parent;
++ parent_dn = dev_of_node(&parent->dev);
++ if (!parent_dn)
++ return;
++
++ partitions = of_get_child_by_name(parent_dn, "partitions");
++ if (!partitions)
++ goto exit_parent;
++
++ prefix_len = strlen(prefix);
++ mtd_name_len = strlen(mtd->name);
++
++ /* Search if a partition is defined with the same name */
++ for_each_child_of_node(partitions, mtd_dn) {
++ offset = 0;
++
++ /* Skip partition with no/wrong prefix */
++ if (!of_node_name_prefix(mtd_dn, "partition-"))
++ continue;
++
++ /* Label have priority. Check that first */
++ if (of_property_read_string(mtd_dn, "label", &pname)) {
++ of_property_read_string(mtd_dn, "name", &pname);
++ offset = prefix_len;
++ }
++
++ plen = strlen(pname) - offset;
++ if (plen == mtd_name_len &&
++ !strncmp(mtd->name, pname + offset, plen)) {
++ found = true;
++ break;
++ }
++ }
++
++ if (!found)
++ goto exit_partitions;
++
++ /* Set of_node only for nvmem */
++ if (of_device_is_compatible(mtd_dn, "nvmem-cells"))
++ mtd_set_of_node(mtd, mtd_dn);
++
++exit_partitions:
++ of_node_put(partitions);
++exit_parent:
++ of_node_put(parent_dn);
++}
++
+ /**
+ * add_mtd_device - register an MTD device
+ * @mtd: pointer to new MTD device info structure
+@@ -669,6 +729,7 @@ int add_mtd_device(struct mtd_info *mtd)
+ mtd->dev.devt = MTD_DEVT(i);
+ dev_set_name(&mtd->dev, "mtd%d", i);
+ dev_set_drvdata(&mtd->dev, mtd);
++ mtd_check_of_node(mtd);
+ of_node_get(mtd_get_of_node(mtd));
+ error = device_register(&mtd->dev);
+ if (error) {
diff --git a/target/linux/generic/backport-6.1/403-v6.1-mtd-allow-getting-MTD-device-associated-with-a-speci.patch b/target/linux/generic/backport-6.1/403-v6.1-mtd-allow-getting-MTD-device-associated-with-a-speci.patch
new file mode 100644
index 0000000000..3039eabea5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/403-v6.1-mtd-allow-getting-MTD-device-associated-with-a-speci.patch
@@ -0,0 +1,72 @@
+From b0321721be50b80c03a51866a94fde4f94690e18 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 15 Jun 2022 21:42:59 +0200
+Subject: [PATCH] mtd: allow getting MTD device associated with a specific DT
+ node
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+MTD subsystem API allows interacting with MTD devices (e.g. reading,
+writing, handling bad blocks). So far a random driver could get MTD
+device only by its name (get_mtd_device_nm()). This change allows
+getting them also by a DT node.
+
+This API is required for drivers handling DT defined MTD partitions in a
+specific way (e.g. U-Boot (sub)partition with environment variables).
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+---
+ drivers/mtd/mtdcore.c | 28 ++++++++++++++++++++++++++++
+ include/linux/mtd/mtd.h | 1 +
+ 2 files changed, 29 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -1236,6 +1236,34 @@ int __get_mtd_device(struct mtd_info *mt
+ EXPORT_SYMBOL_GPL(__get_mtd_device);
+
+ /**
++ * of_get_mtd_device_by_node - obtain an MTD device associated with a given node
++ *
++ * @np: device tree node
++ */
++struct mtd_info *of_get_mtd_device_by_node(struct device_node *np)
++{
++ struct mtd_info *mtd = NULL;
++ struct mtd_info *tmp;
++ int err;
++
++ mutex_lock(&mtd_table_mutex);
++
++ err = -EPROBE_DEFER;
++ mtd_for_each_device(tmp) {
++ if (mtd_get_of_node(tmp) == np) {
++ mtd = tmp;
++ err = __get_mtd_device(mtd);
++ break;
++ }
++ }
++
++ mutex_unlock(&mtd_table_mutex);
++
++ return err ? ERR_PTR(err) : mtd;
++}
++EXPORT_SYMBOL_GPL(of_get_mtd_device_by_node);
++
++/**
+ * get_mtd_device_nm - obtain a validated handle for an MTD device by
+ * device name
+ * @name: MTD device name to open
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -682,6 +682,7 @@ extern int mtd_device_unregister(struct
+ extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
+ extern int __get_mtd_device(struct mtd_info *mtd);
+ extern void __put_mtd_device(struct mtd_info *mtd);
++extern struct mtd_info *of_get_mtd_device_by_node(struct device_node *np);
+ extern struct mtd_info *get_mtd_device_nm(const char *name);
+ extern void put_mtd_device(struct mtd_info *mtd);
+
diff --git a/target/linux/generic/backport-6.1/404-v6.0-mtd-core-check-partition-before-dereference.patch b/target/linux/generic/backport-6.1/404-v6.0-mtd-core-check-partition-before-dereference.patch
new file mode 100644
index 0000000000..e45e2ab48e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/404-v6.0-mtd-core-check-partition-before-dereference.patch
@@ -0,0 +1,30 @@
+From 7ec4cdb321738d44ae5d405e7b6ac73dfbf99caa Mon Sep 17 00:00:00 2001
+From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+Date: Mon, 25 Jul 2022 22:49:25 +0900
+Subject: [PATCH] mtd: core: check partition before dereference
+
+syzbot is reporting NULL pointer dereference at mtd_check_of_node() [1],
+for mtdram test device (CONFIG_MTD_MTDRAM) is not partition.
+
+Link: https://syzkaller.appspot.com/bug?extid=fe013f55a2814a9e8cfd [1]
+Reported-by: syzbot <syzbot+fe013f55a2814a9e8cfd@syzkaller.appspotmail.com>
+Reported-by: kernel test robot <oliver.sang@intel.com>
+Fixes: ad9b10d1eaada169 ("mtd: core: introduce of support for dynamic partitions")
+Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+CC: stable@vger.kernel.org
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/mtdcore.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -577,6 +577,8 @@ static void mtd_check_of_node(struct mtd
+ return;
+
+ /* Check if a partitions node exist */
++ if (!mtd_is_partition(mtd))
++ return;
+ parent = mtd->parent;
+ parent_dn = dev_of_node(&parent->dev);
+ if (!parent_dn)
diff --git a/target/linux/generic/backport-6.1/405-v6.1-mtd-core-add-missing-of_node_get-in-dynamic-partitio.patch b/target/linux/generic/backport-6.1/405-v6.1-mtd-core-add-missing-of_node_get-in-dynamic-partitio.patch
new file mode 100644
index 0000000000..9399a00aa1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/405-v6.1-mtd-core-add-missing-of_node_get-in-dynamic-partitio.patch
@@ -0,0 +1,101 @@
+From 12b58961de0bd88b3c7dfa5d21f6d67f4678b780 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 18 Oct 2022 07:18:22 +0200
+Subject: [PATCH] mtd: core: add missing of_node_get() in dynamic partitions
+ code
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This fixes unbalanced of_node_put():
+[ 1.078910] 6 cmdlinepart partitions found on MTD device gpmi-nand
+[ 1.085116] Creating 6 MTD partitions on "gpmi-nand":
+[ 1.090181] 0x000000000000-0x000008000000 : "nandboot"
+[ 1.096952] 0x000008000000-0x000009000000 : "nandfit"
+[ 1.103547] 0x000009000000-0x00000b000000 : "nandkernel"
+[ 1.110317] 0x00000b000000-0x00000c000000 : "nanddtb"
+[ 1.115525] ------------[ cut here ]------------
+[ 1.120141] refcount_t: addition on 0; use-after-free.
+[ 1.125328] WARNING: CPU: 0 PID: 1 at lib/refcount.c:25 refcount_warn_saturate+0xdc/0x148
+[ 1.133528] Modules linked in:
+[ 1.136589] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.0.0-rc7-next-20220930-04543-g8cf3f7
+[ 1.146342] Hardware name: Freescale i.MX8DXL DDR3L EVK (DT)
+[ 1.151999] pstate: 600000c5 (nZCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+[ 1.158965] pc : refcount_warn_saturate+0xdc/0x148
+[ 1.163760] lr : refcount_warn_saturate+0xdc/0x148
+[ 1.168556] sp : ffff800009ddb080
+[ 1.171866] x29: ffff800009ddb080 x28: ffff800009ddb35a x27: 0000000000000002
+[ 1.179015] x26: ffff8000098b06ad x25: ffffffffffffffff x24: ffff0a00ffffff05
+[ 1.186165] x23: ffff00001fdf6470 x22: ffff800009ddb367 x21: 0000000000000000
+[ 1.193314] x20: ffff00001fdfebe8 x19: ffff00001fdfec50 x18: ffffffffffffffff
+[ 1.200464] x17: 0000000000000000 x16: 0000000000000118 x15: 0000000000000004
+[ 1.207614] x14: 0000000000000fff x13: ffff800009bca248 x12: 0000000000000003
+[ 1.214764] x11: 00000000ffffefff x10: c0000000ffffefff x9 : 4762cb2ccb52de00
+[ 1.221914] x8 : 4762cb2ccb52de00 x7 : 205d313431303231 x6 : 312e31202020205b
+[ 1.229063] x5 : ffff800009d55c1f x4 : 0000000000000001 x3 : 0000000000000000
+[ 1.236213] x2 : 0000000000000000 x1 : ffff800009954be6 x0 : 000000000000002a
+[ 1.243365] Call trace:
+[ 1.245806] refcount_warn_saturate+0xdc/0x148
+[ 1.250253] kobject_get+0x98/0x9c
+[ 1.253658] of_node_get+0x20/0x34
+[ 1.257072] of_fwnode_get+0x3c/0x54
+[ 1.260652] fwnode_get_nth_parent+0xd8/0xf4
+[ 1.264926] fwnode_full_name_string+0x3c/0xb4
+[ 1.269373] device_node_string+0x498/0x5b4
+[ 1.273561] pointer+0x41c/0x5d0
+[ 1.276793] vsnprintf+0x4d8/0x694
+[ 1.280198] vprintk_store+0x164/0x528
+[ 1.283951] vprintk_emit+0x98/0x164
+[ 1.287530] vprintk_default+0x44/0x6c
+[ 1.291284] vprintk+0xf0/0x134
+[ 1.294428] _printk+0x54/0x7c
+[ 1.297486] of_node_release+0xe8/0x128
+[ 1.301326] kobject_put+0x98/0xfc
+[ 1.304732] of_node_put+0x1c/0x28
+[ 1.308137] add_mtd_device+0x484/0x6d4
+[ 1.311977] add_mtd_partitions+0xf0/0x1d0
+[ 1.316078] parse_mtd_partitions+0x45c/0x518
+[ 1.320439] mtd_device_parse_register+0xb0/0x274
+[ 1.325147] gpmi_nand_probe+0x51c/0x650
+[ 1.329074] platform_probe+0xa8/0xd0
+[ 1.332740] really_probe+0x130/0x334
+[ 1.336406] __driver_probe_device+0xb4/0xe0
+[ 1.340681] driver_probe_device+0x3c/0x1f8
+[ 1.344869] __driver_attach+0xdc/0x1a4
+[ 1.348708] bus_for_each_dev+0x80/0xcc
+[ 1.352548] driver_attach+0x24/0x30
+[ 1.356127] bus_add_driver+0x108/0x1f4
+[ 1.359967] driver_register+0x78/0x114
+[ 1.363807] __platform_driver_register+0x24/0x30
+[ 1.368515] gpmi_nand_driver_init+0x1c/0x28
+[ 1.372798] do_one_initcall+0xbc/0x238
+[ 1.376638] do_initcall_level+0x94/0xb4
+[ 1.380565] do_initcalls+0x54/0x94
+[ 1.384058] do_basic_setup+0x1c/0x28
+[ 1.387724] kernel_init_freeable+0x110/0x188
+[ 1.392084] kernel_init+0x20/0x1a0
+[ 1.395578] ret_from_fork+0x10/0x20
+[ 1.399157] ---[ end trace 0000000000000000 ]---
+[ 1.403782] ------------[ cut here ]------------
+
+Reported-by: Han Xu <han.xu@nxp.com>
+Fixes: ad9b10d1eaada169 ("mtd: core: introduce of support for dynamic partitions")
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Tested-by: Han Xu <han.xu@nxp.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20221018051822.28685-1-zajec5@gmail.com
+---
+ drivers/mtd/mtdcore.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -580,7 +580,7 @@ static void mtd_check_of_node(struct mtd
+ if (!mtd_is_partition(mtd))
+ return;
+ parent = mtd->parent;
+- parent_dn = dev_of_node(&parent->dev);
++ parent_dn = of_node_get(dev_of_node(&parent->dev));
+ if (!parent_dn)
+ return;
+
diff --git a/target/linux/generic/backport-6.1/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch b/target/linux/generic/backport-6.1/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch
new file mode 100644
index 0000000000..7e9645ea98
--- /dev/null
+++ b/target/linux/generic/backport-6.1/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch
@@ -0,0 +1,65 @@
+From 63db0cb35e1cb3b3c134906d1062f65513fdda2d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 4 Oct 2022 10:37:09 +0200
+Subject: [PATCH] mtd: core: simplify (a bit) code find partition-matching
+ dynamic OF node
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+1. Don't hardcode "partition-" string twice
+2. Use simpler logic & use ->name to avoid of_property_read_string()
+3. Use mtd_get_of_node() helper
+
+Cc: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-1-zajec5@gmail.com
+---
+ drivers/mtd/mtdcore.c | 16 +++++++---------
+ 1 file changed, 7 insertions(+), 9 deletions(-)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -569,18 +569,16 @@ static void mtd_check_of_node(struct mtd
+ struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
+ const char *pname, *prefix = "partition-";
+ int plen, mtd_name_len, offset, prefix_len;
+- struct mtd_info *parent;
+ bool found = false;
+
+ /* Check if MTD already has a device node */
+- if (dev_of_node(&mtd->dev))
++ if (mtd_get_of_node(mtd))
+ return;
+
+ /* Check if a partitions node exist */
+ if (!mtd_is_partition(mtd))
+ return;
+- parent = mtd->parent;
+- parent_dn = of_node_get(dev_of_node(&parent->dev));
++ parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
+ if (!parent_dn)
+ return;
+
+@@ -593,15 +591,15 @@ static void mtd_check_of_node(struct mtd
+
+ /* Search if a partition is defined with the same name */
+ for_each_child_of_node(partitions, mtd_dn) {
+- offset = 0;
+-
+ /* Skip partition with no/wrong prefix */
+- if (!of_node_name_prefix(mtd_dn, "partition-"))
++ if (!of_node_name_prefix(mtd_dn, prefix))
+ continue;
+
+ /* Label have priority. Check that first */
+- if (of_property_read_string(mtd_dn, "label", &pname)) {
+- of_property_read_string(mtd_dn, "name", &pname);
++ if (!of_property_read_string(mtd_dn, "label", &pname)) {
++ offset = 0;
++ } else {
++ pname = mtd_dn->name;
+ offset = prefix_len;
+ }
+
diff --git a/target/linux/generic/backport-6.1/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch b/target/linux/generic/backport-6.1/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch
new file mode 100644
index 0000000000..48a7c13cd0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch
@@ -0,0 +1,84 @@
+From ddb8cefb7af288950447ca6eeeafb09977dab56f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 4 Oct 2022 10:37:10 +0200
+Subject: [PATCH] mtd: core: try to find OF node for every MTD partition
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+So far this feature was limited to the top-level "nvmem-cells" node.
+There are multiple parsers creating partitions and subpartitions
+dynamically. Extend that code to handle them too.
+
+This allows finding partition-* node for every MTD (sub)partition.
+
+Random example:
+
+partitions {
+ compatible = "brcm,bcm947xx-cfe-partitions";
+
+ partition-firmware {
+ compatible = "brcm,trx";
+
+ partition-loader {
+ };
+ };
+};
+
+Cc: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-2-zajec5@gmail.com
+---
+ drivers/mtd/mtdcore.c | 18 ++++++------------
+ 1 file changed, 6 insertions(+), 12 deletions(-)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -569,20 +569,22 @@ static void mtd_check_of_node(struct mtd
+ struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
+ const char *pname, *prefix = "partition-";
+ int plen, mtd_name_len, offset, prefix_len;
+- bool found = false;
+
+ /* Check if MTD already has a device node */
+ if (mtd_get_of_node(mtd))
+ return;
+
+- /* Check if a partitions node exist */
+ if (!mtd_is_partition(mtd))
+ return;
++
+ parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
+ if (!parent_dn)
+ return;
+
+- partitions = of_get_child_by_name(parent_dn, "partitions");
++ if (mtd_is_partition(mtd->parent))
++ partitions = of_node_get(parent_dn);
++ else
++ partitions = of_get_child_by_name(parent_dn, "partitions");
+ if (!partitions)
+ goto exit_parent;
+
+@@ -606,19 +608,11 @@ static void mtd_check_of_node(struct mtd
+ plen = strlen(pname) - offset;
+ if (plen == mtd_name_len &&
+ !strncmp(mtd->name, pname + offset, plen)) {
+- found = true;
++ mtd_set_of_node(mtd, mtd_dn);
+ break;
+ }
+ }
+
+- if (!found)
+- goto exit_partitions;
+-
+- /* Set of_node only for nvmem */
+- if (of_device_is_compatible(mtd_dn, "nvmem-cells"))
+- mtd_set_of_node(mtd, mtd_dn);
+-
+-exit_partitions:
+ of_node_put(partitions);
+ exit_parent:
+ of_node_put(parent_dn);
diff --git a/target/linux/generic/backport-6.1/407-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch b/target/linux/generic/backport-6.1/407-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch
new file mode 100644
index 0000000000..0efad99157
--- /dev/null
+++ b/target/linux/generic/backport-6.1/407-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch
@@ -0,0 +1,32 @@
+From 26bccc9671ba5e01f7153addbe94e7dc3f677375 Mon Sep 17 00:00:00 2001
+From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+Date: Mon, 3 Jan 2022 03:03:16 +0000
+Subject: [PATCH 13/14] mtd: parsers: qcom: Don't print error message on
+ -EPROBE_DEFER
+
+Its possible for the main smem driver to not be loaded by the time we come
+along to parse the smem partition description but, this is a perfectly
+normal thing.
+
+No need to print out an error message in this case.
+
+Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220103030316.58301-3-bryan.odonoghue@linaro.org
+---
+ drivers/mtd/parsers/qcomsmempart.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/parsers/qcomsmempart.c
++++ b/drivers/mtd/parsers/qcomsmempart.c
+@@ -75,7 +75,8 @@ static int parse_qcomsmem_part(struct mt
+ pr_debug("Parsing partition table info from SMEM\n");
+ ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len);
+ if (IS_ERR(ptable)) {
+- pr_err("Error reading partition table header\n");
++ if (PTR_ERR(ptable) != -EPROBE_DEFER)
++ pr_err("Error reading partition table header\n");
+ return PTR_ERR(ptable);
+ }
+
diff --git a/target/linux/generic/backport-6.1/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch b/target/linux/generic/backport-6.1/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch
new file mode 100644
index 0000000000..505e347e40
--- /dev/null
+++ b/target/linux/generic/backport-6.1/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch
@@ -0,0 +1,47 @@
+From 26422ac78e9d8767bd4aabfbae616b15edbf6a1b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sat, 22 Oct 2022 23:13:18 +0200
+Subject: [PATCH] mtd: core: set ROOT_DEV for partitions marked as rootfs in DT
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This adds support for "linux,rootfs" binding that is used to mark flash
+partition containing rootfs. It's useful for devices using device tree
+that don't have bootloader passing root info in cmdline.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20221022211318.32009-2-zajec5@gmail.com
+---
+ drivers/mtd/mtdcore.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -28,6 +28,7 @@
+ #include <linux/leds.h>
+ #include <linux/debugfs.h>
+ #include <linux/nvmem-provider.h>
++#include <linux/root_dev.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+@@ -748,6 +749,17 @@ int add_mtd_device(struct mtd_info *mtd)
+ not->add(mtd);
+
+ mutex_unlock(&mtd_table_mutex);
++
++ if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL)) {
++ if (IS_BUILTIN(CONFIG_MTD)) {
++ pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
++ } else {
++ pr_warn("mtd: can't set mtd%d (%s) as root device - mtd must be builtin\n",
++ mtd->index, mtd->name);
++ }
++ }
++
+ /* We _know_ we aren't being removed, because
+ our caller is still holding us here. So none
+ of this try_ nonsense, and no bitching about it
diff --git a/target/linux/generic/backport-6.1/410-v5.18-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch b/target/linux/generic/backport-6.1/410-v5.18-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch
new file mode 100644
index 0000000000..5c49841760
--- /dev/null
+++ b/target/linux/generic/backport-6.1/410-v5.18-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch
@@ -0,0 +1,33 @@
+From 2365f91c861cbfeef7141c69842848c7b2d3c2db Mon Sep 17 00:00:00 2001
+From: INAGAKI Hiroshi <musashino.open@gmail.com>
+Date: Sun, 13 Feb 2022 15:40:44 +0900
+Subject: [PATCH] mtd: parsers: trx: allow to use on MediaTek MIPS SoCs
+
+Buffalo sells some router devices which have trx-formatted firmware,
+based on MediaTek MIPS SoCs. To use parser_trx on those devices, add
+"RALINK" to dependency and allow to compile for MediaTek MIPS SoCs.
+
+examples:
+
+- WCR-1166DS (MT7628)
+- WSR-1166DHP (MT7621)
+- WSR-2533DHP (MT7621)
+
+Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220213064045.1781-1-musashino.open@gmail.com
+---
+ drivers/mtd/parsers/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -115,7 +115,7 @@ config MTD_AFS_PARTS
+
+ config MTD_PARSER_TRX
+ tristate "Parser for TRX format partitions"
+- depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || COMPILE_TEST)
++ depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
+ help
+ TRX is a firmware format used by Broadcom on their devices. It
+ may contain up to 3/4 partitions (depending on the version).
diff --git a/target/linux/generic/backport-6.1/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch b/target/linux/generic/backport-6.1/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch
new file mode 100644
index 0000000000..181c912fbf
--- /dev/null
+++ b/target/linux/generic/backport-6.1/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch
@@ -0,0 +1,58 @@
+From 573eec222bc82fb5e724586267fbbb1aed9ffd03 Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Sun, 20 Mar 2022 17:59:58 +0800
+Subject: [PATCH 2/5] mtd: spinand: gigadevice: add support for GD5FxGQ4xExxG
+
+Add support for:
+ GD5F1GQ4RExxG
+ GD5F2GQ4{U,R}ExxG
+
+These chips differ from GD5F1GQ4UExxG only in chip ID, voltage
+and capacity.
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-3-gch981213@gmail.com
+---
+ drivers/mtd/nand/spi/gigadevice.c | 30 ++++++++++++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
+
+--- a/drivers/mtd/nand/spi/gigadevice.c
++++ b/drivers/mtd/nand/spi/gigadevice.c
+@@ -333,6 +333,36 @@ static const struct spinand_info gigadev
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F1GQ4RExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc1),
++ NAND_MEMORG(1, 2048, 128, 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(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F2GQ4UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd2),
++ 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(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F2GQ4RExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc2),
++ 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(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
+ SPINAND_INFO("GD5F1GQ4UFxxG",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
diff --git a/target/linux/generic/backport-6.1/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch b/target/linux/generic/backport-6.1/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch
new file mode 100644
index 0000000000..3a1cc9efcf
--- /dev/null
+++ b/target/linux/generic/backport-6.1/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch
@@ -0,0 +1,33 @@
+From 620a988813403318023296b61228ee8f3fcdb8e0 Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Sun, 20 Mar 2022 17:59:59 +0800
+Subject: [PATCH 3/5] mtd: spinand: gigadevice: add support for GD5F1GQ5RExxG
+
+This chip is the 1.8v version of GD5F1GQ5UExxG.
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-4-gch981213@gmail.com
+---
+ drivers/mtd/nand/spi/gigadevice.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/nand/spi/gigadevice.c
++++ b/drivers/mtd/nand/spi/gigadevice.c
+@@ -383,6 +383,16 @@ static const struct spinand_info gigadev
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F1GQ5RExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x41),
++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
+ };
+
+ static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
diff --git a/target/linux/generic/backport-6.1/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch b/target/linux/generic/backport-6.1/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch
new file mode 100644
index 0000000000..cee9d9db3e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch
@@ -0,0 +1,84 @@
+From 194ec04b3a9e7fa97d1fbef296410631bc3cf1c8 Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Sun, 20 Mar 2022 18:00:00 +0800
+Subject: [PATCH 4/5] mtd: spinand: gigadevice: add support for GD5F{2,
+ 4}GQ5xExxG
+
+Add support for:
+ GD5F2GQ5{U,R}ExxG
+ GD5F4GQ6{U,R}ExxG
+
+These chips uses 4 dummy bytes for quad io and 2 dummy bytes for dual io.
+Besides that and memory layout, they are identical to their 1G variant.
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-5-gch981213@gmail.com
+---
+ drivers/mtd/nand/spi/gigadevice.c | 48 +++++++++++++++++++++++++++++++
+ 1 file changed, 48 insertions(+)
+
+--- a/drivers/mtd/nand/spi/gigadevice.c
++++ b/drivers/mtd/nand/spi/gigadevice.c
+@@ -47,6 +47,14 @@ static SPINAND_OP_VARIANTS(read_cache_va
+ 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(read_cache_variants_2gq5,
++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, 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));
+@@ -391,6 +399,46 @@ static const struct spinand_info gigadev
+ &write_cache_variants,
+ &update_cache_variants),
+ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F2GQ5UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F2GQ5RExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x42),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F4GQ6UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x55),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F4GQ6RExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x45),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
++ NAND_ECCREQ(4, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+ gd5fxgq5xexxg_ecc_get_status)),
+ };
diff --git a/target/linux/generic/backport-6.1/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch b/target/linux/generic/backport-6.1/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch
new file mode 100644
index 0000000000..d63113e1a6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch
@@ -0,0 +1,91 @@
+From 54647cd003c08b714474a5b599a147ec6a160486 Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Sun, 20 Mar 2022 18:00:01 +0800
+Subject: [PATCH 5/5] mtd: spinand: gigadevice: add support for GD5FxGM7xExxG
+
+Add support for:
+ GD5F{1,2}GM7{U,R}ExxG
+ GD5F4GM8{U,R}ExxG
+
+These are new 27nm counterparts for the GD5FxGQ4 chips from GigaDevice
+with 8b/512b on-die ECC capability.
+These chips (and currently supported GD5FxGQ5 chips) have QIO DTR
+instruction for reading page cache. It isn't added in this patch because
+I don't have a DTR spi controller for testing.
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-6-gch981213@gmail.com
+---
+ drivers/mtd/nand/spi/gigadevice.c | 60 +++++++++++++++++++++++++++++++
+ 1 file changed, 60 insertions(+)
+
+--- a/drivers/mtd/nand/spi/gigadevice.c
++++ b/drivers/mtd/nand/spi/gigadevice.c
+@@ -441,6 +441,66 @@ static const struct spinand_info gigadev
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+ gd5fxgq5xexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F1GM7UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x91),
++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F1GM7RExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81),
++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F2GM7UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F2GM7RExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x82),
++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F4GM8UExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x95),
++ NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
++ SPINAND_INFO("GD5F4GM8RExxG",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x85),
++ NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++ gd5fxgq4uexxg_ecc_get_status)),
+ };
+
+ static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
diff --git a/target/linux/generic/backport-6.1/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch b/target/linux/generic/backport-6.1/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch
new file mode 100644
index 0000000000..9f543365a5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch
@@ -0,0 +1,229 @@
+From aec4d5f5ffd0f0092bd9dc21ea90e0bc237d4b74 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sat, 15 Oct 2022 11:29:50 +0200
+Subject: [PATCH] mtd: parsers: add TP-Link SafeLoader partitions table parser
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This parser deals with most TP-Link home routers. It reads info about
+partitions and registers them in the MTD subsystem.
+
+Example from TP-Link Archer C5 V2:
+
+spi-nor spi0.0: s25fl128s1 (16384 Kbytes)
+15 tplink-safeloader partitions found on MTD device spi0.0
+Creating 15 MTD partitions on "spi0.0":
+0x000000000000-0x000000040000 : "fs-uboot"
+0x000000040000-0x000000440000 : "os-image"
+0x000000440000-0x000000e40000 : "rootfs"
+0x000000e40000-0x000000e40200 : "default-mac"
+0x000000e40200-0x000000e40400 : "pin"
+0x000000e40400-0x000000e40600 : "product-info"
+0x000000e50000-0x000000e60000 : "partition-table"
+0x000000e60000-0x000000e60200 : "soft-version"
+0x000000e61000-0x000000e70000 : "support-list"
+0x000000e70000-0x000000e80000 : "profile"
+0x000000e80000-0x000000e90000 : "default-config"
+0x000000e90000-0x000000ee0000 : "user-config"
+0x000000ee0000-0x000000fe0000 : "log"
+0x000000fe0000-0x000000ff0000 : "radio_bk"
+0x000000ff0000-0x000001000000 : "radio"
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20221015092950.27467-2-zajec5@gmail.com
+---
+ drivers/mtd/parsers/Kconfig | 15 +++
+ drivers/mtd/parsers/Makefile | 1 +
+ drivers/mtd/parsers/tplink_safeloader.c | 150 ++++++++++++++++++++++++
+ 3 files changed, 166 insertions(+)
+ create mode 100644 drivers/mtd/parsers/tplink_safeloader.c
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -113,6 +113,21 @@ config MTD_AFS_PARTS
+ for your particular device. It won't happen automatically. The
+ 'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example.
+
++config MTD_PARSER_TPLINK_SAFELOADER
++ tristate "TP-Link Safeloader partitions parser"
++ depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST)
++ help
++ TP-Link home routers use flash partitions to store various data. Info
++ about flash space layout is stored in a partitions table using a
++ custom ASCII-based format.
++
++ That format was first found in devices with SafeLoader bootloader and
++ was named after it. Later it was adapted to CFE and U-Boot
++ bootloaders.
++
++ This driver reads partitions table, parses it and creates MTD
++ partitions.
++
+ config MTD_PARSER_TRX
+ tristate "Parser for TRX format partitions"
+ depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -9,6 +9,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) +=
+ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
+ obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
+ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
++obj-$(CONFIG_MTD_PARSER_TPLINK_SAFELOADER) += tplink_safeloader.o
+ obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
+ obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
+ obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
+--- /dev/null
++++ b/drivers/mtd/parsers/tplink_safeloader.c
+@@ -0,0 +1,150 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright © 2022 Rafał Miłecki <rafal@milecki.pl>
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/of.h>
++#include <linux/slab.h>
++
++#define TPLINK_SAFELOADER_DATA_OFFSET 4
++#define TPLINK_SAFELOADER_MAX_PARTS 32
++
++struct safeloader_cmn_header {
++ __be32 size;
++ uint32_t unused;
++} __packed;
++
++static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd)
++{
++ struct safeloader_cmn_header hdr;
++ struct device_node *np;
++ size_t bytes_read;
++ size_t offset;
++ size_t size;
++ char *buf;
++ int err;
++
++ np = mtd_get_of_node(mtd);
++ if (mtd_is_partition(mtd))
++ of_node_get(np);
++ else
++ np = of_get_child_by_name(np, "partitions");
++
++ if (of_property_read_u32(np, "partitions-table-offset", (u32 *)&offset)) {
++ pr_err("Failed to get partitions table offset\n");
++ goto err_put;
++ }
++
++ err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr);
++ if (err && !mtd_is_bitflip(err)) {
++ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset);
++ goto err_put;
++ }
++
++ size = be32_to_cpu(hdr.size);
++
++ buf = kmalloc(size + 1, GFP_KERNEL);
++ if (!buf)
++ goto err_put;
++
++ err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf);
++ if (err && !mtd_is_bitflip(err)) {
++ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr));
++ goto err_kfree;
++ }
++
++ buf[size] = '\0';
++
++ of_node_put(np);
++
++ return buf;
++
++err_kfree:
++ kfree(buf);
++err_put:
++ of_node_put(np);
++ return NULL;
++}
++
++static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
++ const struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ struct mtd_partition *parts;
++ char name[65];
++ size_t offset;
++ size_t bytes;
++ char *buf;
++ int idx;
++ int err;
++
++ parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL);
++ if (!parts) {
++ err = -ENOMEM;
++ goto err_out;
++ }
++
++ buf = mtd_parser_tplink_safeloader_read_table(mtd);
++ if (!buf) {
++ err = -ENOENT;
++ goto err_out;
++ }
++
++ for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET;
++ idx < TPLINK_SAFELOADER_MAX_PARTS &&
++ sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n",
++ name, &parts[idx].offset, &parts[idx].size, &bytes) == 3;
++ idx++, offset += bytes + 1) {
++ parts[idx].name = kstrdup(name, GFP_KERNEL);
++ if (!parts[idx].name) {
++ err = -ENOMEM;
++ goto err_free;
++ }
++ }
++
++ if (idx == TPLINK_SAFELOADER_MAX_PARTS)
++ pr_warn("Reached maximum number of partitions!\n");
++
++ kfree(buf);
++
++ *pparts = parts;
++
++ return idx;
++
++err_free:
++ for (idx -= 1; idx >= 0; idx--)
++ kfree(parts[idx].name);
++err_out:
++ return err;
++};
++
++static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts,
++ int nr_parts)
++{
++ int i;
++
++ for (i = 0; i < nr_parts; i++)
++ kfree(pparts[i].name);
++
++ kfree(pparts);
++}
++
++static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = {
++ { .compatible = "tplink,safeloader-partitions" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table);
++
++static struct mtd_part_parser mtd_parser_tplink_safeloader = {
++ .parse_fn = mtd_parser_tplink_safeloader_parse,
++ .cleanup = mtd_parser_tplink_safeloader_cleanup,
++ .name = "tplink-safeloader",
++ .of_match_table = mtd_parser_tplink_safeloader_of_match_table,
++};
++module_mtd_part_parser(mtd_parser_tplink_safeloader);
++
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.1/422-v5.19-mtd-spi-nor-support-eon-en25qh256a.patch b/target/linux/generic/backport-6.1/422-v5.19-mtd-spi-nor-support-eon-en25qh256a.patch
new file mode 100644
index 0000000000..2358352e93
--- /dev/null
+++ b/target/linux/generic/backport-6.1/422-v5.19-mtd-spi-nor-support-eon-en25qh256a.patch
@@ -0,0 +1,49 @@
+From 6abef37d16d0c570ef5a149e63762fba2a30804b Mon Sep 17 00:00:00 2001
+From: "Leon M. George" <leon@georgemail.eu>
+Date: Wed, 30 Mar 2022 16:16:56 +0200
+Subject: [PATCH] mtd: spi-nor: support eon en25qh256a variant
+
+The EN25QH256A variant of the EN25QH256 doesn't initialize correctly from SFDP
+alone and only accesses memory below 8m (addr_width is 4 but read_opcode takes
+only 3 bytes).
+
+Set SNOR_F_4B_OPCODES if the flash chip variant was detected using hwcaps.
+
+The fix submitted upstream uses the PARSE_SFDP initializer that is not
+available in the kernel used with Openwrt.
+
+Signed-off-by: Leon M. George <leon@georgemail.eu>
+---
+ drivers/mtd/spi-nor/eon.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/drivers/mtd/spi-nor/eon.c
++++ b/drivers/mtd/spi-nor/eon.c
+@@ -8,6 +8,16 @@
+
+ #include "core.h"
+
++static void en25qh256_post_sfdp_fixups(struct spi_nor *nor)
++{
++ if (nor->params->hwcaps.mask & SNOR_HWCAPS_READ_1_1_4)
++ nor->flags |= SNOR_F_4B_OPCODES;
++}
++
++static const struct spi_nor_fixups en25qh256_fixups = {
++ .post_sfdp = en25qh256_post_sfdp_fixups,
++};
++
+ static const struct flash_info eon_parts[] = {
+ /* EON -- en25xxx */
+ { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
+@@ -23,7 +33,9 @@ static const struct flash_info eon_parts
+ { "en25qh64", INFO(0x1c7017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ) },
+ { "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) },
+- { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
++ { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512,
++ SPI_NOR_DUAL_READ)
++ .fixups = &en25qh256_fixups },
+ { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) },
+ };
+
diff --git a/target/linux/generic/backport-6.1/423-v6.1-0001-mtd-track-maximum-number-of-bitflips-for-each-read-r.patch b/target/linux/generic/backport-6.1/423-v6.1-0001-mtd-track-maximum-number-of-bitflips-for-each-read-r.patch
new file mode 100644
index 0000000000..9f1757caa7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/423-v6.1-0001-mtd-track-maximum-number-of-bitflips-for-each-read-r.patch
@@ -0,0 +1,73 @@
+From e237285113963bd1dd2e925770aa8b3aa8a1894c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= <kernel@kempniu.pl>
+Date: Wed, 29 Jun 2022 14:57:34 +0200
+Subject: [PATCH 1/4] mtd: track maximum number of bitflips for each read
+ request
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+mtd_read_oob() callers are currently oblivious to the details of ECC
+errors detected during the read operation - they only learn (through the
+return value) whether any corrected bitflips or uncorrectable errors
+occurred. More detailed ECC information can be useful to user-space
+applications for making better-informed choices about moving data
+around.
+
+Extend struct mtd_oob_ops with a pointer to a newly-introduced struct
+mtd_req_stats and set its 'max_bitflips' field to the maximum number of
+bitflips found in a single ECC step during the read operation performed
+by mtd_read_oob(). This is a prerequisite for ultimately passing that
+value back to user space.
+
+Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
+Signed-off-by: Michał Kępień <kernel@kempniu.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220629125737.14418-2-kernel@kempniu.pl
+---
+ drivers/mtd/mtdcore.c | 5 +++++
+ include/linux/mtd/mtd.h | 5 +++++
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -1676,6 +1676,9 @@ int mtd_read_oob(struct mtd_info *mtd, l
+ if (!master->_read_oob && (!master->_read || ops->oobbuf))
+ return -EOPNOTSUPP;
+
++ if (ops->stats)
++ memset(ops->stats, 0, sizeof(*ops->stats));
++
+ if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
+ ret_code = mtd_io_emulated_slc(mtd, from, true, ops);
+ else
+@@ -1693,6 +1696,8 @@ int mtd_read_oob(struct mtd_info *mtd, l
+ return ret_code;
+ if (mtd->ecc_strength == 0)
+ return 0; /* device lacks ecc */
++ if (ops->stats)
++ ops->stats->max_bitflips = ret_code;
+ return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
+ }
+ EXPORT_SYMBOL_GPL(mtd_read_oob);
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -40,6 +40,10 @@ struct mtd_erase_region_info {
+ unsigned long *lockmap; /* If keeping bitmap of locks */
+ };
+
++struct mtd_req_stats {
++ unsigned int max_bitflips;
++};
++
+ /**
+ * struct mtd_oob_ops - oob operation operands
+ * @mode: operation mode
+@@ -70,6 +74,7 @@ struct mtd_oob_ops {
+ uint32_t ooboffs;
+ uint8_t *datbuf;
+ uint8_t *oobbuf;
++ struct mtd_req_stats *stats;
+ };
+
+ #define MTD_MAX_OOBFREE_ENTRIES_LARGE 32
diff --git a/target/linux/generic/backport-6.1/423-v6.1-0002-mtd-always-initialize-stats-in-struct-mtd_oob_ops.patch b/target/linux/generic/backport-6.1/423-v6.1-0002-mtd-always-initialize-stats-in-struct-mtd_oob_ops.patch
new file mode 100644
index 0000000000..1484624e4e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/423-v6.1-0002-mtd-always-initialize-stats-in-struct-mtd_oob_ops.patch
@@ -0,0 +1,325 @@
+From e97709c9d18903f5acd5fbe2985dd054da0432b1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= <kernel@kempniu.pl>
+Date: Wed, 29 Jun 2022 14:57:35 +0200
+Subject: [PATCH 2/4] mtd: always initialize 'stats' in struct mtd_oob_ops
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+As the 'stats' field in struct mtd_oob_ops is used in conditional
+expressions, ensure it is always zero-initialized in all such structures
+to prevent random stack garbage from being interpreted as a pointer.
+
+Strictly speaking, this problem currently only needs to be fixed for
+struct mtd_oob_ops structures subsequently passed to mtd_read_oob().
+However, this commit goes a step further and makes all instances of
+struct mtd_oob_ops in the tree zero-initialized, in hope of preventing
+future problems, e.g. if struct mtd_req_stats gets extended with write
+statistics at some point.
+
+Signed-off-by: Michał Kępień <kernel@kempniu.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220629125737.14418-3-kernel@kempniu.pl
+---
+ drivers/mtd/inftlcore.c | 6 +++---
+ drivers/mtd/mtdswap.c | 6 +++---
+ drivers/mtd/nand/onenand/onenand_base.c | 4 ++--
+ drivers/mtd/nand/onenand/onenand_bbt.c | 2 +-
+ drivers/mtd/nand/raw/nand_bbt.c | 8 ++++----
+ drivers/mtd/nand/raw/sm_common.c | 2 +-
+ drivers/mtd/nftlcore.c | 6 +++---
+ drivers/mtd/sm_ftl.c | 4 ++--
+ drivers/mtd/ssfdc.c | 2 +-
+ drivers/mtd/tests/nandbiterrs.c | 2 +-
+ drivers/mtd/tests/oobtest.c | 8 ++++----
+ drivers/mtd/tests/readtest.c | 2 +-
+ fs/jffs2/wbuf.c | 6 +++---
+ 13 files changed, 29 insertions(+), 29 deletions(-)
+
+--- a/drivers/mtd/inftlcore.c
++++ b/drivers/mtd/inftlcore.c
+@@ -136,7 +136,7 @@ static void inftl_remove_dev(struct mtd_
+ int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+ size_t *retlen, uint8_t *buf)
+ {
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int res;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+@@ -156,7 +156,7 @@ int inftl_read_oob(struct mtd_info *mtd,
+ int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
+ size_t *retlen, uint8_t *buf)
+ {
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int res;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+@@ -176,7 +176,7 @@ int inftl_write_oob(struct mtd_info *mtd
+ static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
+ size_t *retlen, uint8_t *buf, uint8_t *oob)
+ {
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int res;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+--- a/drivers/mtd/mtdswap.c
++++ b/drivers/mtd/mtdswap.c
+@@ -323,7 +323,7 @@ static int mtdswap_read_markers(struct m
+ struct mtdswap_oobdata *data, *data2;
+ int ret;
+ loff_t offset;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+
+ offset = mtdswap_eb_offset(d, eb);
+
+@@ -370,7 +370,7 @@ static int mtdswap_write_marker(struct m
+ struct mtdswap_oobdata n;
+ int ret;
+ loff_t offset;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+
+ ops.ooboffs = 0;
+ ops.oobbuf = (uint8_t *)&n;
+@@ -879,7 +879,7 @@ static unsigned int mtdswap_eblk_passes(
+ loff_t base, pos;
+ unsigned int *p1 = (unsigned int *)d->page_buf;
+ unsigned char *p2 = (unsigned char *)d->oob_buf;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int ret;
+
+ ops.mode = MTD_OPS_AUTO_OOB;
+--- a/drivers/mtd/nand/onenand/onenand_base.c
++++ b/drivers/mtd/nand/onenand/onenand_base.c
+@@ -2935,7 +2935,7 @@ static int do_otp_write(struct mtd_info
+ struct onenand_chip *this = mtd->priv;
+ unsigned char *pbuf = buf;
+ int ret;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+
+ /* Force buffer page aligned */
+ if (len < mtd->writesize) {
+@@ -2977,7 +2977,7 @@ static int do_otp_lock(struct mtd_info *
+ size_t *retlen, u_char *buf)
+ {
+ struct onenand_chip *this = mtd->priv;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int ret;
+
+ if (FLEXONENAND(this)) {
+--- a/drivers/mtd/nand/onenand/onenand_bbt.c
++++ b/drivers/mtd/nand/onenand/onenand_bbt.c
+@@ -61,7 +61,7 @@ static int create_bbt(struct mtd_info *m
+ int startblock;
+ loff_t from;
+ size_t readlen, ooblen;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int rgn;
+
+ printk(KERN_INFO "Scanning device for bad blocks\n");
+--- a/drivers/mtd/nand/raw/nand_bbt.c
++++ b/drivers/mtd/nand/raw/nand_bbt.c
+@@ -313,7 +313,7 @@ static int scan_read_oob(struct nand_chi
+ size_t len)
+ {
+ struct mtd_info *mtd = nand_to_mtd(this);
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int res, ret = 0;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+@@ -354,7 +354,7 @@ static int scan_write_bbt(struct nand_ch
+ uint8_t *buf, uint8_t *oob)
+ {
+ struct mtd_info *mtd = nand_to_mtd(this);
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.ooboffs = 0;
+@@ -416,7 +416,7 @@ static int scan_block_fast(struct nand_c
+ {
+ struct mtd_info *mtd = nand_to_mtd(this);
+
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int ret, page_offset;
+
+ ops.ooblen = mtd->oobsize;
+@@ -756,7 +756,7 @@ static int write_bbt(struct nand_chip *t
+ uint8_t rcode = td->reserved_block_code;
+ size_t retlen, len = 0;
+ loff_t to;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+
+ ops.ooblen = mtd->oobsize;
+ ops.ooboffs = 0;
+--- a/drivers/mtd/nand/raw/sm_common.c
++++ b/drivers/mtd/nand/raw/sm_common.c
+@@ -99,7 +99,7 @@ static const struct mtd_ooblayout_ops oo
+ static int sm_block_markbad(struct nand_chip *chip, loff_t ofs)
+ {
+ struct mtd_info *mtd = nand_to_mtd(chip);
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ struct sm_oob oob;
+ int ret;
+
+--- a/drivers/mtd/nftlcore.c
++++ b/drivers/mtd/nftlcore.c
+@@ -124,7 +124,7 @@ int nftl_read_oob(struct mtd_info *mtd,
+ size_t *retlen, uint8_t *buf)
+ {
+ loff_t mask = mtd->writesize - 1;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int res;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+@@ -145,7 +145,7 @@ int nftl_write_oob(struct mtd_info *mtd,
+ size_t *retlen, uint8_t *buf)
+ {
+ loff_t mask = mtd->writesize - 1;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int res;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+@@ -168,7 +168,7 @@ static int nftl_write(struct mtd_info *m
+ size_t *retlen, uint8_t *buf, uint8_t *oob)
+ {
+ loff_t mask = mtd->writesize - 1;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int res;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+--- a/drivers/mtd/sm_ftl.c
++++ b/drivers/mtd/sm_ftl.c
+@@ -239,7 +239,7 @@ static int sm_read_sector(struct sm_ftl
+ uint8_t *buffer, struct sm_oob *oob)
+ {
+ struct mtd_info *mtd = ftl->trans->mtd;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ struct sm_oob tmp_oob;
+ int ret = -EIO;
+ int try = 0;
+@@ -323,7 +323,7 @@ static int sm_write_sector(struct sm_ftl
+ int zone, int block, int boffset,
+ uint8_t *buffer, struct sm_oob *oob)
+ {
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ struct mtd_info *mtd = ftl->trans->mtd;
+ int ret;
+
+--- a/drivers/mtd/ssfdc.c
++++ b/drivers/mtd/ssfdc.c
+@@ -163,7 +163,7 @@ static int read_physical_sector(struct m
+ /* Read redundancy area (wrapper to MTD_READ_OOB */
+ static int read_raw_oob(struct mtd_info *mtd, loff_t offs, uint8_t *buf)
+ {
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int ret;
+
+ ops.mode = MTD_OPS_RAW;
+--- a/drivers/mtd/tests/nandbiterrs.c
++++ b/drivers/mtd/tests/nandbiterrs.c
+@@ -99,7 +99,7 @@ static int write_page(int log)
+ static int rewrite_page(int log)
+ {
+ int err = 0;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+
+ if (log)
+ pr_info("rewrite page\n");
+--- a/drivers/mtd/tests/oobtest.c
++++ b/drivers/mtd/tests/oobtest.c
+@@ -56,7 +56,7 @@ static void do_vary_offset(void)
+ static int write_eraseblock(int ebnum)
+ {
+ int i;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int err = 0;
+ loff_t addr = (loff_t)ebnum * mtd->erasesize;
+
+@@ -165,7 +165,7 @@ static size_t memffshow(loff_t addr, lof
+ static int verify_eraseblock(int ebnum)
+ {
+ int i;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int err = 0;
+ loff_t addr = (loff_t)ebnum * mtd->erasesize;
+ size_t bitflips;
+@@ -260,7 +260,7 @@ static int verify_eraseblock(int ebnum)
+
+ static int verify_eraseblock_in_one_go(int ebnum)
+ {
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int err = 0;
+ loff_t addr = (loff_t)ebnum * mtd->erasesize;
+ size_t len = mtd->oobavail * pgcnt;
+@@ -338,7 +338,7 @@ static int __init mtd_oobtest_init(void)
+ int err = 0;
+ unsigned int i;
+ uint64_t tmp;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ loff_t addr = 0, addr0;
+
+ printk(KERN_INFO "\n");
+--- a/drivers/mtd/tests/readtest.c
++++ b/drivers/mtd/tests/readtest.c
+@@ -47,7 +47,7 @@ static int read_eraseblock_by_page(int e
+ err = ret;
+ }
+ if (mtd->oobsize) {
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.len = 0;
+--- a/fs/jffs2/wbuf.c
++++ b/fs/jffs2/wbuf.c
+@@ -1035,7 +1035,7 @@ int jffs2_check_oob_empty(struct jffs2_s
+ {
+ int i, ret;
+ int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
+@@ -1076,7 +1076,7 @@ int jffs2_check_oob_empty(struct jffs2_s
+ int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
+ struct jffs2_eraseblock *jeb)
+ {
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
+
+ ops.mode = MTD_OPS_AUTO_OOB;
+@@ -1101,7 +1101,7 @@ int jffs2_write_nand_cleanmarker(struct
+ struct jffs2_eraseblock *jeb)
+ {
+ int ret;
+- struct mtd_oob_ops ops;
++ struct mtd_oob_ops ops = { };
+ int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
+
+ ops.mode = MTD_OPS_AUTO_OOB;
diff --git a/target/linux/generic/backport-6.1/423-v6.1-0003-mtd-add-ECC-error-accounting-for-each-read-request.patch b/target/linux/generic/backport-6.1/423-v6.1-0003-mtd-add-ECC-error-accounting-for-each-read-request.patch
new file mode 100644
index 0000000000..f2f45eb7bc
--- /dev/null
+++ b/target/linux/generic/backport-6.1/423-v6.1-0003-mtd-add-ECC-error-accounting-for-each-read-request.patch
@@ -0,0 +1,172 @@
+From 2ed18d818d1f7492172f8dd5904344c7d367e8ed Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= <kernel@kempniu.pl>
+Date: Wed, 29 Jun 2022 14:57:36 +0200
+Subject: [PATCH 3/4] mtd: add ECC error accounting for each read request
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Extend struct mtd_req_stats with two new fields holding the number of
+corrected bitflips and uncorrectable errors detected during a read
+operation. This is a prerequisite for ultimately passing those counters
+to user space, where they can be useful to applications for making
+better-informed choices about moving data around.
+
+Unlike 'max_bitflips' (which is set - in a common code path - to the
+return value of a function called while the MTD device's mutex is held),
+these counters have to be maintained in each MTD driver which defines
+the '_read_oob' callback because the statistics need to be calculated
+while the MTD device's mutex is held.
+
+Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
+Signed-off-by: Michał Kępień <kernel@kempniu.pl>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220629125737.14418-4-kernel@kempniu.pl
+---
+ drivers/mtd/devices/docg3.c | 8 ++++++++
+ drivers/mtd/nand/onenand/onenand_base.c | 12 ++++++++++++
+ drivers/mtd/nand/raw/nand_base.c | 10 ++++++++++
+ drivers/mtd/nand/spi/core.c | 10 ++++++++++
+ include/linux/mtd/mtd.h | 2 ++
+ 5 files changed, 42 insertions(+)
+
+--- a/drivers/mtd/devices/docg3.c
++++ b/drivers/mtd/devices/docg3.c
+@@ -871,6 +871,7 @@ static int doc_read_oob(struct mtd_info
+ u8 *buf = ops->datbuf;
+ size_t len, ooblen, nbdata, nboob;
+ u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1;
++ struct mtd_ecc_stats old_stats;
+ int max_bitflips = 0;
+
+ if (buf)
+@@ -895,6 +896,7 @@ static int doc_read_oob(struct mtd_info
+ ret = 0;
+ skip = from % DOC_LAYOUT_PAGE_SIZE;
+ mutex_lock(&docg3->cascade->lock);
++ old_stats = mtd->ecc_stats;
+ while (ret >= 0 && (len > 0 || ooblen > 0)) {
+ calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
+ docg3->reliable);
+@@ -966,6 +968,12 @@ static int doc_read_oob(struct mtd_info
+ }
+
+ out:
++ if (ops->stats) {
++ ops->stats->uncorrectable_errors +=
++ mtd->ecc_stats.failed - old_stats.failed;
++ ops->stats->corrected_bitflips +=
++ mtd->ecc_stats.corrected - old_stats.corrected;
++ }
+ mutex_unlock(&docg3->cascade->lock);
+ return ret;
+ err_in_read:
+--- a/drivers/mtd/nand/onenand/onenand_base.c
++++ b/drivers/mtd/nand/onenand/onenand_base.c
+@@ -1440,6 +1440,7 @@ static int onenand_read_oob(struct mtd_i
+ struct mtd_oob_ops *ops)
+ {
+ struct onenand_chip *this = mtd->priv;
++ struct mtd_ecc_stats old_stats;
+ int ret;
+
+ switch (ops->mode) {
+@@ -1453,12 +1454,23 @@ static int onenand_read_oob(struct mtd_i
+ }
+
+ onenand_get_device(mtd, FL_READING);
++
++ old_stats = mtd->ecc_stats;
++
+ if (ops->datbuf)
+ ret = ONENAND_IS_4KB_PAGE(this) ?
+ onenand_mlc_read_ops_nolock(mtd, from, ops) :
+ onenand_read_ops_nolock(mtd, from, ops);
+ else
+ ret = onenand_read_oob_nolock(mtd, from, ops);
++
++ if (ops->stats) {
++ ops->stats->uncorrectable_errors +=
++ mtd->ecc_stats.failed - old_stats.failed;
++ ops->stats->corrected_bitflips +=
++ mtd->ecc_stats.corrected - old_stats.corrected;
++ }
++
+ onenand_release_device(mtd);
+
+ return ret;
+--- a/drivers/mtd/nand/raw/nand_base.c
++++ b/drivers/mtd/nand/raw/nand_base.c
+@@ -3815,6 +3815,7 @@ static int nand_read_oob(struct mtd_info
+ struct mtd_oob_ops *ops)
+ {
+ struct nand_chip *chip = mtd_to_nand(mtd);
++ struct mtd_ecc_stats old_stats;
+ int ret;
+
+ ops->retlen = 0;
+@@ -3826,11 +3827,20 @@ static int nand_read_oob(struct mtd_info
+
+ nand_get_device(chip);
+
++ old_stats = mtd->ecc_stats;
++
+ if (!ops->datbuf)
+ ret = nand_do_read_oob(chip, from, ops);
+ else
+ ret = nand_do_read_ops(chip, from, ops);
+
++ if (ops->stats) {
++ ops->stats->uncorrectable_errors +=
++ mtd->ecc_stats.failed - old_stats.failed;
++ ops->stats->corrected_bitflips +=
++ mtd->ecc_stats.corrected - old_stats.corrected;
++ }
++
+ nand_release_device(chip);
+ return ret;
+ }
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -629,6 +629,7 @@ static int spinand_mtd_read(struct mtd_i
+ {
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ struct nand_device *nand = mtd_to_nanddev(mtd);
++ struct mtd_ecc_stats old_stats;
+ unsigned int max_bitflips = 0;
+ struct nand_io_iter iter;
+ bool disable_ecc = false;
+@@ -640,6 +641,8 @@ static int spinand_mtd_read(struct mtd_i
+
+ mutex_lock(&spinand->lock);
+
++ old_stats = mtd->ecc_stats;
++
+ nanddev_io_for_each_page(nand, NAND_PAGE_READ, from, ops, &iter) {
+ if (disable_ecc)
+ iter.req.mode = MTD_OPS_RAW;
+@@ -662,6 +665,13 @@ static int spinand_mtd_read(struct mtd_i
+ ops->oobretlen += iter.req.ooblen;
+ }
+
++ if (ops->stats) {
++ ops->stats->uncorrectable_errors +=
++ mtd->ecc_stats.failed - old_stats.failed;
++ ops->stats->corrected_bitflips +=
++ mtd->ecc_stats.corrected - old_stats.corrected;
++ }
++
+ mutex_unlock(&spinand->lock);
+
+ if (ecc_failed && !ret)
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -41,6 +41,8 @@ struct mtd_erase_region_info {
+ };
+
+ struct mtd_req_stats {
++ unsigned int uncorrectable_errors;
++ unsigned int corrected_bitflips;
+ unsigned int max_bitflips;
+ };
+
diff --git a/target/linux/generic/backport-6.1/423-v6.1-0004-mtdchar-add-MEMREAD-ioctl.patch b/target/linux/generic/backport-6.1/423-v6.1-0004-mtdchar-add-MEMREAD-ioctl.patch
new file mode 100644
index 0000000000..182e4f6ab5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/423-v6.1-0004-mtdchar-add-MEMREAD-ioctl.patch
@@ -0,0 +1,321 @@
+From 2c9745d36e04ac27161acd78514f647b9b587ad4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= <kernel@kempniu.pl>
+Date: Wed, 29 Jun 2022 14:57:37 +0200
+Subject: [PATCH 4/4] mtdchar: add MEMREAD ioctl
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+User-space applications making use of MTD devices via /dev/mtd*
+character devices currently have limited capabilities for reading data:
+
+ - only deprecated methods of accessing OOB layout information exist,
+
+ - there is no way to explicitly specify MTD operation mode to use; it
+ is auto-selected based on the MTD file mode (MTD_FILE_MODE_*) set
+ for the character device; in particular, this prevents using
+ MTD_OPS_AUTO_OOB for reads,
+
+ - all existing user-space interfaces which cause mtd_read() or
+ mtd_read_oob() to be called (via mtdchar_read() and
+ mtdchar_read_oob(), respectively) return success even when those
+ functions return -EUCLEAN or -EBADMSG; this renders user-space
+ applications using these interfaces unaware of any corrected
+ bitflips or uncorrectable ECC errors detected during reads.
+
+Note that the existing MEMWRITE ioctl allows the MTD operation mode to
+be explicitly set, allowing user-space applications to write page data
+and OOB data without requiring them to know anything about the OOB
+layout of the MTD device they are writing to (MTD_OPS_AUTO_OOB). Also,
+the MEMWRITE ioctl does not mangle the return value of mtd_write_oob().
+
+Add a new ioctl, MEMREAD, which addresses the above issues. It is
+intended to be a read-side counterpart of the existing MEMWRITE ioctl.
+Similarly to the latter, the read operation is performed in a loop which
+processes at most mtd->erasesize bytes in each iteration. This is done
+to prevent unbounded memory allocations caused by calling kmalloc() with
+the 'size' argument taken directly from the struct mtd_read_req provided
+by user space. However, the new ioctl is implemented so that the values
+it returns match those that would have been returned if just a single
+mtd_read_oob() call was issued to handle the entire read operation in
+one go.
+
+Note that while just returning -EUCLEAN or -EBADMSG to user space would
+already be a valid and useful indication of the ECC algorithm detecting
+errors during a read operation, that signal would not be granular enough
+to cover all use cases. For example, knowing the maximum number of
+bitflips detected in a single ECC step during a read operation performed
+on a given page may be useful when dealing with an MTD partition whose
+ECC layout varies across pages (e.g. a partition consisting of a
+bootloader area using a "custom" ECC layout followed by data pages using
+a "standard" ECC layout). To address that, include ECC statistics in
+the structure returned to user space by the new MEMREAD ioctl.
+
+Link: https://www.infradead.org/pipermail/linux-mtd/2016-April/067085.html
+
+Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
+Signed-off-by: Michał Kępień <kernel@kempniu.pl>
+Acked-by: Richard Weinberger <richard@nod.at>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20220629125737.14418-5-kernel@kempniu.pl
+---
+ drivers/mtd/mtdchar.c | 139 +++++++++++++++++++++++++++++++++++++
+ include/uapi/mtd/mtd-abi.h | 64 +++++++++++++++--
+ 2 files changed, 198 insertions(+), 5 deletions(-)
+
+--- a/drivers/mtd/mtdchar.c
++++ b/drivers/mtd/mtdchar.c
+@@ -621,6 +621,137 @@ static int mtdchar_write_ioctl(struct mt
+ return ret;
+ }
+
++static int mtdchar_read_ioctl(struct mtd_info *mtd,
++ struct mtd_read_req __user *argp)
++{
++ struct mtd_info *master = mtd_get_master(mtd);
++ struct mtd_read_req req;
++ void __user *usr_data, *usr_oob;
++ uint8_t *datbuf = NULL, *oobbuf = NULL;
++ size_t datbuf_len, oobbuf_len;
++ size_t orig_len, orig_ooblen;
++ int ret = 0;
++
++ if (copy_from_user(&req, argp, sizeof(req)))
++ return -EFAULT;
++
++ orig_len = req.len;
++ orig_ooblen = req.ooblen;
++
++ usr_data = (void __user *)(uintptr_t)req.usr_data;
++ usr_oob = (void __user *)(uintptr_t)req.usr_oob;
++
++ if (!master->_read_oob)
++ return -EOPNOTSUPP;
++
++ if (!usr_data)
++ req.len = 0;
++
++ if (!usr_oob)
++ req.ooblen = 0;
++
++ req.ecc_stats.uncorrectable_errors = 0;
++ req.ecc_stats.corrected_bitflips = 0;
++ req.ecc_stats.max_bitflips = 0;
++
++ req.len &= 0xffffffff;
++ req.ooblen &= 0xffffffff;
++
++ if (req.start + req.len > mtd->size) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ datbuf_len = min_t(size_t, req.len, mtd->erasesize);
++ if (datbuf_len > 0) {
++ datbuf = kvmalloc(datbuf_len, GFP_KERNEL);
++ if (!datbuf) {
++ ret = -ENOMEM;
++ goto out;
++ }
++ }
++
++ oobbuf_len = min_t(size_t, req.ooblen, mtd->erasesize);
++ if (oobbuf_len > 0) {
++ oobbuf = kvmalloc(oobbuf_len, GFP_KERNEL);
++ if (!oobbuf) {
++ ret = -ENOMEM;
++ goto out;
++ }
++ }
++
++ while (req.len > 0 || (!usr_data && req.ooblen > 0)) {
++ struct mtd_req_stats stats;
++ struct mtd_oob_ops ops = {
++ .mode = req.mode,
++ .len = min_t(size_t, req.len, datbuf_len),
++ .ooblen = min_t(size_t, req.ooblen, oobbuf_len),
++ .datbuf = datbuf,
++ .oobbuf = oobbuf,
++ .stats = &stats,
++ };
++
++ /*
++ * Shorten non-page-aligned, eraseblock-sized reads so that the
++ * read ends on an eraseblock boundary. This is necessary in
++ * order to prevent OOB data for some pages from being
++ * duplicated in the output of non-page-aligned reads requiring
++ * multiple mtd_read_oob() calls to be completed.
++ */
++ if (ops.len == mtd->erasesize)
++ ops.len -= mtd_mod_by_ws(req.start + ops.len, mtd);
++
++ ret = mtd_read_oob(mtd, (loff_t)req.start, &ops);
++
++ req.ecc_stats.uncorrectable_errors +=
++ stats.uncorrectable_errors;
++ req.ecc_stats.corrected_bitflips += stats.corrected_bitflips;
++ req.ecc_stats.max_bitflips =
++ max(req.ecc_stats.max_bitflips, stats.max_bitflips);
++
++ if (ret && !mtd_is_bitflip_or_eccerr(ret))
++ break;
++
++ if (copy_to_user(usr_data, ops.datbuf, ops.retlen) ||
++ copy_to_user(usr_oob, ops.oobbuf, ops.oobretlen)) {
++ ret = -EFAULT;
++ break;
++ }
++
++ req.start += ops.retlen;
++ req.len -= ops.retlen;
++ usr_data += ops.retlen;
++
++ req.ooblen -= ops.oobretlen;
++ usr_oob += ops.oobretlen;
++ }
++
++ /*
++ * As multiple iterations of the above loop (and therefore multiple
++ * mtd_read_oob() calls) may be necessary to complete the read request,
++ * adjust the final return code to ensure it accounts for all detected
++ * ECC errors.
++ */
++ if (!ret || mtd_is_bitflip(ret)) {
++ if (req.ecc_stats.uncorrectable_errors > 0)
++ ret = -EBADMSG;
++ else if (req.ecc_stats.corrected_bitflips > 0)
++ ret = -EUCLEAN;
++ }
++
++out:
++ req.len = orig_len - req.len;
++ req.ooblen = orig_ooblen - req.ooblen;
++
++ if (copy_to_user(argp, &req, sizeof(req)))
++ ret = -EFAULT;
++
++ kvfree(datbuf);
++ kvfree(oobbuf);
++
++ return ret;
++}
++
+ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
+ {
+ struct mtd_file_info *mfi = file->private_data;
+@@ -643,6 +774,7 @@ static int mtdchar_ioctl(struct file *fi
+ case MEMGETINFO:
+ case MEMREADOOB:
+ case MEMREADOOB64:
++ case MEMREAD:
+ case MEMISLOCKED:
+ case MEMGETOOBSEL:
+ case MEMGETBADBLOCK:
+@@ -817,6 +949,13 @@ static int mtdchar_ioctl(struct file *fi
+ break;
+ }
+
++ case MEMREAD:
++ {
++ ret = mtdchar_read_ioctl(mtd,
++ (struct mtd_read_req __user *)arg);
++ break;
++ }
++
+ case MEMLOCK:
+ {
+ struct erase_info_user einfo;
+--- a/include/uapi/mtd/mtd-abi.h
++++ b/include/uapi/mtd/mtd-abi.h
+@@ -55,9 +55,9 @@ struct mtd_oob_buf64 {
+ * @MTD_OPS_RAW: data are transferred as-is, with no error correction;
+ * this mode implies %MTD_OPS_PLACE_OOB
+ *
+- * These modes can be passed to ioctl(MEMWRITE) and are also used internally.
+- * See notes on "MTD file modes" for discussion on %MTD_OPS_RAW vs.
+- * %MTD_FILE_MODE_RAW.
++ * These modes can be passed to ioctl(MEMWRITE) and ioctl(MEMREAD); they are
++ * also used internally. See notes on "MTD file modes" for discussion on
++ * %MTD_OPS_RAW vs. %MTD_FILE_MODE_RAW.
+ */
+ enum {
+ MTD_OPS_PLACE_OOB = 0,
+@@ -91,6 +91,53 @@ struct mtd_write_req {
+ __u8 padding[7];
+ };
+
++/**
++ * struct mtd_read_req_ecc_stats - ECC statistics for a read operation
++ *
++ * @uncorrectable_errors: the number of uncorrectable errors that happened
++ * during the read operation
++ * @corrected_bitflips: the number of bitflips corrected during the read
++ * operation
++ * @max_bitflips: the maximum number of bitflips detected in any single ECC
++ * step for the data read during the operation; this information
++ * can be used to decide whether the data stored in a specific
++ * region of the MTD device should be moved somewhere else to
++ * avoid data loss.
++ */
++struct mtd_read_req_ecc_stats {
++ __u32 uncorrectable_errors;
++ __u32 corrected_bitflips;
++ __u32 max_bitflips;
++};
++
++/**
++ * struct mtd_read_req - data structure for requesting a read operation
++ *
++ * @start: start address
++ * @len: length of data buffer (only lower 32 bits are used)
++ * @ooblen: length of OOB buffer (only lower 32 bits are used)
++ * @usr_data: user-provided data buffer
++ * @usr_oob: user-provided OOB buffer
++ * @mode: MTD mode (see "MTD operation modes")
++ * @padding: reserved, must be set to 0
++ * @ecc_stats: ECC statistics for the read operation
++ *
++ * This structure supports ioctl(MEMREAD) operations, allowing data and/or OOB
++ * reads in various modes. To read from OOB-only, set @usr_data == NULL, and to
++ * read data-only, set @usr_oob == NULL. However, setting both @usr_data and
++ * @usr_oob to NULL is not allowed.
++ */
++struct mtd_read_req {
++ __u64 start;
++ __u64 len;
++ __u64 ooblen;
++ __u64 usr_data;
++ __u64 usr_oob;
++ __u8 mode;
++ __u8 padding[7];
++ struct mtd_read_req_ecc_stats ecc_stats;
++};
++
+ #define MTD_ABSENT 0
+ #define MTD_RAM 1
+ #define MTD_ROM 2
+@@ -207,6 +254,12 @@ struct otp_info {
+ #define MEMWRITE _IOWR('M', 24, struct mtd_write_req)
+ /* Erase a given range of user data (must be in mode %MTD_FILE_MODE_OTP_USER) */
+ #define OTPERASE _IOW('M', 25, struct otp_info)
++/*
++ * Most generic read interface; can read in-band and/or out-of-band in various
++ * modes (see "struct mtd_read_req"). This ioctl is not supported for flashes
++ * without OOB, e.g., NOR flash.
++ */
++#define MEMREAD _IOWR('M', 26, struct mtd_read_req)
+
+ /*
+ * Obsolete legacy interface. Keep it in order not to break userspace
+@@ -270,8 +323,9 @@ struct mtd_ecc_stats {
+ * Note: %MTD_FILE_MODE_RAW provides the same functionality as %MTD_OPS_RAW -
+ * raw access to the flash, without error correction or autoplacement schemes.
+ * Wherever possible, the MTD_OPS_* mode will override the MTD_FILE_MODE_* mode
+- * (e.g., when using ioctl(MEMWRITE)), but in some cases, the MTD_FILE_MODE is
+- * used out of necessity (e.g., `write()', ioctl(MEMWRITEOOB64)).
++ * (e.g., when using ioctl(MEMWRITE) or ioctl(MEMREAD)), but in some cases, the
++ * MTD_FILE_MODE is used out of necessity (e.g., `write()',
++ * ioctl(MEMWRITEOOB64)).
+ */
+ enum mtd_file_modes {
+ MTD_FILE_MODE_NORMAL = MTD_OTP_OFF,
diff --git a/target/linux/generic/backport-6.1/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch b/target/linux/generic/backport-6.1/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch
new file mode 100644
index 0000000000..7dbc271725
--- /dev/null
+++ b/target/linux/generic/backport-6.1/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch
@@ -0,0 +1,35 @@
+From ebed787a0becb9354f0a23620a5130cccd6c730c Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 19 Jan 2023 03:45:43 +0000
+Subject: [PATCH] mtd: spinand: macronix: use scratch buffer for DMA operation
+
+The mx35lf1ge4ab_get_eccsr() function uses an SPI DMA operation to
+read the eccsr, hence the buffer should not be on stack. Since commit
+380583227c0c7f ("spi: spi-mem: Add extra sanity checks on the op param")
+the kernel emmits a warning and blocks such operations.
+
+Use the scratch buffer to get eccsr instead of trying to directly read
+into a stack-allocated variable.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Dhruva Gole <d-gole@ti.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/Y8i85zM0u4XdM46z@makrotopia.org
+---
+ drivers/mtd/nand/spi/macronix.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/nand/spi/macronix.c
++++ b/drivers/mtd/nand/spi/macronix.c
+@@ -83,9 +83,10 @@ static int mx35lf1ge4ab_ecc_get_status(s
+ * in order to avoid forcing the wear-leveling layer to move
+ * data around if it's not necessary.
+ */
+- if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr))
++ if (mx35lf1ge4ab_get_eccsr(spinand, spinand->scratchbuf))
+ return nanddev_get_ecc_conf(nand)->strength;
+
++ eccsr = *spinand->scratchbuf;
+ if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength ||
+ !eccsr))
+ return nanddev_get_ecc_conf(nand)->strength;
diff --git a/target/linux/generic/backport-6.1/424-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch b/target/linux/generic/backport-6.1/424-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch
new file mode 100644
index 0000000000..2d89a5db12
--- /dev/null
+++ b/target/linux/generic/backport-6.1/424-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch
@@ -0,0 +1,47 @@
+From 281f7a6c1a33fffcde32001bacbb4f672140fbf9 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Wed, 8 Mar 2023 09:20:21 +0100
+Subject: [PATCH] mtd: core: prepare mtd_otp_nvmem_add() to handle
+ -EPROBE_DEFER
+
+NVMEM soon will get the ability for nvmem layouts and these might
+not be ready when nvmem_register() is called and thus it might
+return -EPROBE_DEFER. Don't print the error message in this case.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20230308082021.870459-4-michael@walle.cc
+---
+ drivers/mtd/mtdcore.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -960,8 +960,8 @@ static int mtd_otp_nvmem_add(struct mtd_
+ nvmem = mtd_otp_nvmem_register(mtd, "user-otp", size,
+ mtd_nvmem_user_otp_reg_read);
+ if (IS_ERR(nvmem)) {
+- dev_err(dev, "Failed to register OTP NVMEM device\n");
+- return PTR_ERR(nvmem);
++ err = PTR_ERR(nvmem);
++ goto err;
+ }
+ mtd->otp_user_nvmem = nvmem;
+ }
+@@ -978,7 +978,6 @@ static int mtd_otp_nvmem_add(struct mtd_
+ nvmem = mtd_otp_nvmem_register(mtd, "factory-otp", size,
+ mtd_nvmem_fact_otp_reg_read);
+ if (IS_ERR(nvmem)) {
+- dev_err(dev, "Failed to register OTP NVMEM device\n");
+ err = PTR_ERR(nvmem);
+ goto err;
+ }
+@@ -991,7 +990,7 @@ static int mtd_otp_nvmem_add(struct mtd_
+ err:
+ if (mtd->otp_user_nvmem)
+ nvmem_unregister(mtd->otp_user_nvmem);
+- return err;
++ return dev_err_probe(dev, err, "Failed to register OTP NVMEM device\n");
+ }
+
+ /**
diff --git a/target/linux/generic/backport-6.1/600-v5.18-page_pool-Add-allocation-stats.patch b/target/linux/generic/backport-6.1/600-v5.18-page_pool-Add-allocation-stats.patch
new file mode 100644
index 0000000000..9e383de92c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/600-v5.18-page_pool-Add-allocation-stats.patch
@@ -0,0 +1,165 @@
+From 8610037e8106b48c79cfe0afb92b2b2466e51c3d Mon Sep 17 00:00:00 2001
+From: Joe Damato <jdamato@fastly.com>
+Date: Tue, 1 Mar 2022 23:55:47 -0800
+Subject: [PATCH] page_pool: Add allocation stats
+
+Add per-pool statistics counters for the allocation path of a page pool.
+These stats are incremented in softirq context, so no locking or per-cpu
+variables are needed.
+
+This code is disabled by default and a kernel config option is provided for
+users who wish to enable them.
+
+The statistics added are:
+ - fast: successful fast path allocations
+ - slow: slow path order-0 allocations
+ - slow_high_order: slow path high order allocations
+ - empty: ptr ring is empty, so a slow path allocation was forced.
+ - refill: an allocation which triggered a refill of the cache
+ - waive: pages obtained from the ptr ring that cannot be added to
+ the cache due to a NUMA mismatch.
+
+Signed-off-by: Joe Damato <jdamato@fastly.com>
+Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
+Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/page_pool.h | 18 ++++++++++++++++++
+ net/Kconfig | 13 +++++++++++++
+ net/core/page_pool.c | 24 ++++++++++++++++++++----
+ 3 files changed, 51 insertions(+), 4 deletions(-)
+
+--- a/include/net/page_pool.h
++++ b/include/net/page_pool.h
+@@ -82,6 +82,19 @@ struct page_pool_params {
+ unsigned int offset; /* DMA addr offset */
+ };
+
++#ifdef CONFIG_PAGE_POOL_STATS
++struct page_pool_alloc_stats {
++ u64 fast; /* fast path allocations */
++ u64 slow; /* slow-path order 0 allocations */
++ u64 slow_high_order; /* slow-path high order allocations */
++ u64 empty; /* failed refills due to empty ptr ring, forcing
++ * slow path allocation
++ */
++ u64 refill; /* allocations via successful refill */
++ u64 waive; /* failed refills due to numa zone mismatch */
++};
++#endif
++
+ struct page_pool {
+ struct page_pool_params p;
+
+@@ -132,6 +145,11 @@ struct page_pool {
+ refcount_t user_cnt;
+
+ u64 destroy_cnt;
++
++#ifdef CONFIG_PAGE_POOL_STATS
++ /* these stats are incremented while in softirq context */
++ struct page_pool_alloc_stats alloc_stats;
++#endif
+ };
+
+ struct page *page_pool_alloc_pages(struct page_pool *pool, gfp_t gfp);
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -434,6 +434,19 @@ config NET_DEVLINK
+ config PAGE_POOL
+ bool
+
++config PAGE_POOL_STATS
++ default n
++ bool "Page pool stats"
++ depends on PAGE_POOL
++ help
++ Enable page pool statistics to track page allocation and recycling
++ in page pools. This option incurs additional CPU cost in allocation
++ and recycle paths and additional memory cost to store the statistics.
++ These statistics are only available if this option is enabled and if
++ the driver using the page pool supports exporting this data.
++
++ If unsure, say N.
++
+ config FAILOVER
+ tristate "Generic failover module"
+ help
+--- a/net/core/page_pool.c
++++ b/net/core/page_pool.c
+@@ -26,6 +26,13 @@
+
+ #define BIAS_MAX LONG_MAX
+
++#ifdef CONFIG_PAGE_POOL_STATS
++/* alloc_stat_inc is intended to be used in softirq context */
++#define alloc_stat_inc(pool, __stat) (pool->alloc_stats.__stat++)
++#else
++#define alloc_stat_inc(pool, __stat)
++#endif
++
+ static int page_pool_init(struct page_pool *pool,
+ const struct page_pool_params *params)
+ {
+@@ -117,8 +124,10 @@ static struct page *page_pool_refill_all
+ int pref_nid; /* preferred NUMA node */
+
+ /* Quicker fallback, avoid locks when ring is empty */
+- if (__ptr_ring_empty(r))
++ if (__ptr_ring_empty(r)) {
++ alloc_stat_inc(pool, empty);
+ return NULL;
++ }
+
+ /* Softirq guarantee CPU and thus NUMA node is stable. This,
+ * assumes CPU refilling driver RX-ring will also run RX-NAPI.
+@@ -148,14 +157,17 @@ static struct page *page_pool_refill_all
+ * This limit stress on page buddy alloactor.
+ */
+ page_pool_return_page(pool, page);
++ alloc_stat_inc(pool, waive);
+ page = NULL;
+ break;
+ }
+ } while (pool->alloc.count < PP_ALLOC_CACHE_REFILL);
+
+ /* Return last page */
+- if (likely(pool->alloc.count > 0))
++ if (likely(pool->alloc.count > 0)) {
+ page = pool->alloc.cache[--pool->alloc.count];
++ alloc_stat_inc(pool, refill);
++ }
+
+ spin_unlock(&r->consumer_lock);
+ return page;
+@@ -170,6 +182,7 @@ static struct page *__page_pool_get_cach
+ if (likely(pool->alloc.count)) {
+ /* Fast-path */
+ page = pool->alloc.cache[--pool->alloc.count];
++ alloc_stat_inc(pool, fast);
+ } else {
+ page = page_pool_refill_alloc_cache(pool);
+ }
+@@ -241,6 +254,7 @@ static struct page *__page_pool_alloc_pa
+ return NULL;
+ }
+
++ alloc_stat_inc(pool, slow_high_order);
+ page_pool_set_pp_info(pool, page);
+
+ /* Track how many pages are held 'in-flight' */
+@@ -295,10 +309,12 @@ static struct page *__page_pool_alloc_pa
+ }
+
+ /* Return last page */
+- if (likely(pool->alloc.count > 0))
++ if (likely(pool->alloc.count > 0)) {
+ page = pool->alloc.cache[--pool->alloc.count];
+- else
++ alloc_stat_inc(pool, slow);
++ } else {
+ page = NULL;
++ }
+
+ /* When page just alloc'ed is should/must have refcnt 1. */
+ return page;
diff --git a/target/linux/generic/backport-6.1/601-v5.18-page_pool-Add-recycle-stats.patch b/target/linux/generic/backport-6.1/601-v5.18-page_pool-Add-recycle-stats.patch
new file mode 100644
index 0000000000..fb11f0035f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/601-v5.18-page_pool-Add-recycle-stats.patch
@@ -0,0 +1,140 @@
+From ad6fa1e1ab1b8164f1ba296b1b4dc556a483bcad Mon Sep 17 00:00:00 2001
+From: Joe Damato <jdamato@fastly.com>
+Date: Tue, 1 Mar 2022 23:55:48 -0800
+Subject: [PATCH 2/3] page_pool: Add recycle stats
+
+Add per-cpu stats tracking page pool recycling events:
+ - cached: recycling placed page in the page pool cache
+ - cache_full: page pool cache was full
+ - ring: page placed into the ptr ring
+ - ring_full: page released from page pool because the ptr ring was full
+ - released_refcnt: page released (and not recycled) because refcnt > 1
+
+Signed-off-by: Joe Damato <jdamato@fastly.com>
+Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
+Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/page_pool.h | 16 ++++++++++++++++
+ net/core/page_pool.c | 30 ++++++++++++++++++++++++++++--
+ 2 files changed, 44 insertions(+), 2 deletions(-)
+
+--- a/include/net/page_pool.h
++++ b/include/net/page_pool.h
+@@ -93,6 +93,18 @@ struct page_pool_alloc_stats {
+ u64 refill; /* allocations via successful refill */
+ u64 waive; /* failed refills due to numa zone mismatch */
+ };
++
++struct page_pool_recycle_stats {
++ u64 cached; /* recycling placed page in the cache. */
++ u64 cache_full; /* cache was full */
++ u64 ring; /* recycling placed page back into ptr ring */
++ u64 ring_full; /* page was released from page-pool because
++ * PTR ring was full.
++ */
++ u64 released_refcnt; /* page released because of elevated
++ * refcnt
++ */
++};
+ #endif
+
+ struct page_pool {
+@@ -136,6 +148,10 @@ struct page_pool {
+ */
+ struct ptr_ring ring;
+
++#ifdef CONFIG_PAGE_POOL_STATS
++ /* recycle stats are per-cpu to avoid locking */
++ struct page_pool_recycle_stats __percpu *recycle_stats;
++#endif
+ atomic_t pages_state_release_cnt;
+
+ /* A page_pool is strictly tied to a single RX-queue being
+--- a/net/core/page_pool.c
++++ b/net/core/page_pool.c
+@@ -29,8 +29,15 @@
+ #ifdef CONFIG_PAGE_POOL_STATS
+ /* alloc_stat_inc is intended to be used in softirq context */
+ #define alloc_stat_inc(pool, __stat) (pool->alloc_stats.__stat++)
++/* recycle_stat_inc is safe to use when preemption is possible. */
++#define recycle_stat_inc(pool, __stat) \
++ do { \
++ struct page_pool_recycle_stats __percpu *s = pool->recycle_stats; \
++ this_cpu_inc(s->__stat); \
++ } while (0)
+ #else
+ #define alloc_stat_inc(pool, __stat)
++#define recycle_stat_inc(pool, __stat)
+ #endif
+
+ static int page_pool_init(struct page_pool *pool,
+@@ -80,6 +87,12 @@ static int page_pool_init(struct page_po
+ pool->p.flags & PP_FLAG_PAGE_FRAG)
+ return -EINVAL;
+
++#ifdef CONFIG_PAGE_POOL_STATS
++ pool->recycle_stats = alloc_percpu(struct page_pool_recycle_stats);
++ if (!pool->recycle_stats)
++ return -ENOMEM;
++#endif
++
+ if (ptr_ring_init(&pool->ring, ring_qsize, GFP_KERNEL) < 0)
+ return -ENOMEM;
+
+@@ -412,7 +425,12 @@ static bool page_pool_recycle_in_ring(st
+ else
+ ret = ptr_ring_produce_bh(&pool->ring, page);
+
+- return (ret == 0) ? true : false;
++ if (!ret) {
++ recycle_stat_inc(pool, ring);
++ return true;
++ }
++
++ return false;
+ }
+
+ /* Only allow direct recycling in special circumstances, into the
+@@ -423,11 +441,14 @@ static bool page_pool_recycle_in_ring(st
+ static bool page_pool_recycle_in_cache(struct page *page,
+ struct page_pool *pool)
+ {
+- if (unlikely(pool->alloc.count == PP_ALLOC_CACHE_SIZE))
++ if (unlikely(pool->alloc.count == PP_ALLOC_CACHE_SIZE)) {
++ recycle_stat_inc(pool, cache_full);
+ return false;
++ }
+
+ /* Caller MUST have verified/know (page_ref_count(page) == 1) */
+ pool->alloc.cache[pool->alloc.count++] = page;
++ recycle_stat_inc(pool, cached);
+ return true;
+ }
+
+@@ -482,6 +503,7 @@ __page_pool_put_page(struct page_pool *p
+ * doing refcnt based recycle tricks, meaning another process
+ * will be invoking put_page.
+ */
++ recycle_stat_inc(pool, released_refcnt);
+ /* Do not replace this with page_pool_return_page() */
+ page_pool_release_page(pool, page);
+ put_page(page);
+@@ -495,6 +517,7 @@ void page_pool_put_page(struct page_pool
+ page = __page_pool_put_page(pool, page, dma_sync_size, allow_direct);
+ if (page && !page_pool_recycle_in_ring(pool, page)) {
+ /* Cache full, fallback to free pages */
++ recycle_stat_inc(pool, ring_full);
+ page_pool_return_page(pool, page);
+ }
+ }
+@@ -641,6 +664,9 @@ static void page_pool_free(struct page_p
+ if (pool->p.flags & PP_FLAG_DMA_MAP)
+ put_device(pool->p.dev);
+
++#ifdef CONFIG_PAGE_POOL_STATS
++ free_percpu(pool->recycle_stats);
++#endif
+ kfree(pool);
+ }
+
diff --git a/target/linux/generic/backport-6.1/602-v5.18-page_pool-Add-function-to-batch-and-return-stats.patch b/target/linux/generic/backport-6.1/602-v5.18-page_pool-Add-function-to-batch-and-return-stats.patch
new file mode 100644
index 0000000000..41188fb7f2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/602-v5.18-page_pool-Add-function-to-batch-and-return-stats.patch
@@ -0,0 +1,77 @@
+From 6b95e3388b1ea0ca63500c5a6e39162dbf828433 Mon Sep 17 00:00:00 2001
+From: Joe Damato <jdamato@fastly.com>
+Date: Tue, 1 Mar 2022 23:55:49 -0800
+Subject: [PATCH 3/3] page_pool: Add function to batch and return stats
+
+Adds a function page_pool_get_stats which can be used by drivers to obtain
+stats for a specified page_pool.
+
+Signed-off-by: Joe Damato <jdamato@fastly.com>
+Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
+Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/page_pool.h | 17 +++++++++++++++++
+ net/core/page_pool.c | 25 +++++++++++++++++++++++++
+ 2 files changed, 42 insertions(+)
+
+--- a/include/net/page_pool.h
++++ b/include/net/page_pool.h
+@@ -105,6 +105,23 @@ struct page_pool_recycle_stats {
+ * refcnt
+ */
+ };
++
++/* This struct wraps the above stats structs so users of the
++ * page_pool_get_stats API can pass a single argument when requesting the
++ * stats for the page pool.
++ */
++struct page_pool_stats {
++ struct page_pool_alloc_stats alloc_stats;
++ struct page_pool_recycle_stats recycle_stats;
++};
++
++/*
++ * Drivers that wish to harvest page pool stats and report them to users
++ * (perhaps via ethtool, debugfs, or another mechanism) can allocate a
++ * struct page_pool_stats call page_pool_get_stats to get stats for the specified pool.
++ */
++bool page_pool_get_stats(struct page_pool *pool,
++ struct page_pool_stats *stats);
+ #endif
+
+ struct page_pool {
+--- a/net/core/page_pool.c
++++ b/net/core/page_pool.c
+@@ -35,6 +35,31 @@
+ struct page_pool_recycle_stats __percpu *s = pool->recycle_stats; \
+ this_cpu_inc(s->__stat); \
+ } while (0)
++
++bool page_pool_get_stats(struct page_pool *pool,
++ struct page_pool_stats *stats)
++{
++ int cpu = 0;
++
++ if (!stats)
++ return false;
++
++ memcpy(&stats->alloc_stats, &pool->alloc_stats, sizeof(pool->alloc_stats));
++
++ for_each_possible_cpu(cpu) {
++ const struct page_pool_recycle_stats *pcpu =
++ per_cpu_ptr(pool->recycle_stats, cpu);
++
++ stats->recycle_stats.cached += pcpu->cached;
++ stats->recycle_stats.cache_full += pcpu->cache_full;
++ stats->recycle_stats.ring += pcpu->ring;
++ stats->recycle_stats.ring_full += pcpu->ring_full;
++ stats->recycle_stats.released_refcnt += pcpu->released_refcnt;
++ }
++
++ return true;
++}
++EXPORT_SYMBOL(page_pool_get_stats);
+ #else
+ #define alloc_stat_inc(pool, __stat)
+ #define recycle_stat_inc(pool, __stat)
diff --git a/target/linux/generic/backport-6.1/603-v5.19-page_pool-Add-recycle-stats-to-page_pool_put_page_bu.patch b/target/linux/generic/backport-6.1/603-v5.19-page_pool-Add-recycle-stats-to-page_pool_put_page_bu.patch
new file mode 100644
index 0000000000..6ae3fb7133
--- /dev/null
+++ b/target/linux/generic/backport-6.1/603-v5.19-page_pool-Add-recycle-stats-to-page_pool_put_page_bu.patch
@@ -0,0 +1,55 @@
+From 590032a4d2133ecc10d3078a8db1d85a4842f12c Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 11 Apr 2022 16:05:26 +0200
+Subject: [PATCH] page_pool: Add recycle stats to page_pool_put_page_bulk
+
+Add missing recycle stats to page_pool_put_page_bulk routine.
+
+Reviewed-by: Joe Damato <jdamato@fastly.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
+Link: https://lore.kernel.org/r/3712178b51c007cfaed910ea80e68f00c916b1fa.1649685634.git.lorenzo@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ net/core/page_pool.c | 15 +++++++++++++--
+ 1 file changed, 13 insertions(+), 2 deletions(-)
+
+--- a/net/core/page_pool.c
++++ b/net/core/page_pool.c
+@@ -36,6 +36,12 @@
+ this_cpu_inc(s->__stat); \
+ } while (0)
+
++#define recycle_stat_add(pool, __stat, val) \
++ do { \
++ struct page_pool_recycle_stats __percpu *s = pool->recycle_stats; \
++ this_cpu_add(s->__stat, val); \
++ } while (0)
++
+ bool page_pool_get_stats(struct page_pool *pool,
+ struct page_pool_stats *stats)
+ {
+@@ -63,6 +69,7 @@ EXPORT_SYMBOL(page_pool_get_stats);
+ #else
+ #define alloc_stat_inc(pool, __stat)
+ #define recycle_stat_inc(pool, __stat)
++#define recycle_stat_add(pool, __stat, val)
+ #endif
+
+ static int page_pool_init(struct page_pool *pool,
+@@ -569,9 +576,13 @@ void page_pool_put_page_bulk(struct page
+ /* Bulk producer into ptr_ring page_pool cache */
+ page_pool_ring_lock(pool);
+ for (i = 0; i < bulk_len; i++) {
+- if (__ptr_ring_produce(&pool->ring, data[i]))
+- break; /* ring full */
++ if (__ptr_ring_produce(&pool->ring, data[i])) {
++ /* ring full */
++ recycle_stat_inc(pool, ring_full);
++ break;
++ }
+ }
++ recycle_stat_add(pool, ring, i);
+ page_pool_ring_unlock(pool);
+
+ /* Hopefully all pages was return into ptr_ring */
diff --git a/target/linux/generic/backport-6.1/604-v5.19-net-page_pool-introduce-ethtool-stats.patch b/target/linux/generic/backport-6.1/604-v5.19-net-page_pool-introduce-ethtool-stats.patch
new file mode 100644
index 0000000000..e5cf91ceee
--- /dev/null
+++ b/target/linux/generic/backport-6.1/604-v5.19-net-page_pool-introduce-ethtool-stats.patch
@@ -0,0 +1,147 @@
+From f3c5264f452a5b0ac1de1f2f657efbabdea3c76a Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Tue, 12 Apr 2022 18:31:58 +0200
+Subject: [PATCH] net: page_pool: introduce ethtool stats
+
+Introduce page_pool APIs to report stats through ethtool and reduce
+duplicated code in each driver.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Jakub Kicinski <kuba@kernel.org>
+Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/page_pool.h | 21 ++++++++++++++
+ net/core/page_pool.c | 63 ++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 83 insertions(+), 1 deletion(-)
+
+--- a/include/net/page_pool.h
++++ b/include/net/page_pool.h
+@@ -115,6 +115,10 @@ struct page_pool_stats {
+ struct page_pool_recycle_stats recycle_stats;
+ };
+
++int page_pool_ethtool_stats_get_count(void);
++u8 *page_pool_ethtool_stats_get_strings(u8 *data);
++u64 *page_pool_ethtool_stats_get(u64 *data, void *stats);
++
+ /*
+ * Drivers that wish to harvest page pool stats and report them to users
+ * (perhaps via ethtool, debugfs, or another mechanism) can allocate a
+@@ -122,6 +126,23 @@ struct page_pool_stats {
+ */
+ bool page_pool_get_stats(struct page_pool *pool,
+ struct page_pool_stats *stats);
++#else
++
++static inline int page_pool_ethtool_stats_get_count(void)
++{
++ return 0;
++}
++
++static inline u8 *page_pool_ethtool_stats_get_strings(u8 *data)
++{
++ return data;
++}
++
++static inline u64 *page_pool_ethtool_stats_get(u64 *data, void *stats)
++{
++ return data;
++}
++
+ #endif
+
+ struct page_pool {
+--- a/net/core/page_pool.c
++++ b/net/core/page_pool.c
+@@ -18,6 +18,7 @@
+ #include <linux/page-flags.h>
+ #include <linux/mm.h> /* for __put_page() */
+ #include <linux/poison.h>
++#include <linux/ethtool.h>
+
+ #include <trace/events/page_pool.h>
+
+@@ -42,6 +43,20 @@
+ this_cpu_add(s->__stat, val); \
+ } while (0)
+
++static const char pp_stats[][ETH_GSTRING_LEN] = {
++ "rx_pp_alloc_fast",
++ "rx_pp_alloc_slow",
++ "rx_pp_alloc_slow_ho",
++ "rx_pp_alloc_empty",
++ "rx_pp_alloc_refill",
++ "rx_pp_alloc_waive",
++ "rx_pp_recycle_cached",
++ "rx_pp_recycle_cache_full",
++ "rx_pp_recycle_ring",
++ "rx_pp_recycle_ring_full",
++ "rx_pp_recycle_released_ref",
++};
++
+ bool page_pool_get_stats(struct page_pool *pool,
+ struct page_pool_stats *stats)
+ {
+@@ -50,7 +65,13 @@ bool page_pool_get_stats(struct page_poo
+ if (!stats)
+ return false;
+
+- memcpy(&stats->alloc_stats, &pool->alloc_stats, sizeof(pool->alloc_stats));
++ /* The caller is responsible to initialize stats. */
++ stats->alloc_stats.fast += pool->alloc_stats.fast;
++ stats->alloc_stats.slow += pool->alloc_stats.slow;
++ stats->alloc_stats.slow_high_order += pool->alloc_stats.slow_high_order;
++ stats->alloc_stats.empty += pool->alloc_stats.empty;
++ stats->alloc_stats.refill += pool->alloc_stats.refill;
++ stats->alloc_stats.waive += pool->alloc_stats.waive;
+
+ for_each_possible_cpu(cpu) {
+ const struct page_pool_recycle_stats *pcpu =
+@@ -66,6 +87,46 @@ bool page_pool_get_stats(struct page_poo
+ return true;
+ }
+ EXPORT_SYMBOL(page_pool_get_stats);
++
++u8 *page_pool_ethtool_stats_get_strings(u8 *data)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(pp_stats); i++) {
++ memcpy(data, pp_stats[i], ETH_GSTRING_LEN);
++ data += ETH_GSTRING_LEN;
++ }
++
++ return data;
++}
++EXPORT_SYMBOL(page_pool_ethtool_stats_get_strings);
++
++int page_pool_ethtool_stats_get_count(void)
++{
++ return ARRAY_SIZE(pp_stats);
++}
++EXPORT_SYMBOL(page_pool_ethtool_stats_get_count);
++
++u64 *page_pool_ethtool_stats_get(u64 *data, void *stats)
++{
++ struct page_pool_stats *pool_stats = stats;
++
++ *data++ = pool_stats->alloc_stats.fast;
++ *data++ = pool_stats->alloc_stats.slow;
++ *data++ = pool_stats->alloc_stats.slow_high_order;
++ *data++ = pool_stats->alloc_stats.empty;
++ *data++ = pool_stats->alloc_stats.refill;
++ *data++ = pool_stats->alloc_stats.waive;
++ *data++ = pool_stats->recycle_stats.cached;
++ *data++ = pool_stats->recycle_stats.cache_full;
++ *data++ = pool_stats->recycle_stats.ring;
++ *data++ = pool_stats->recycle_stats.ring_full;
++ *data++ = pool_stats->recycle_stats.released_refcnt;
++
++ return data;
++}
++EXPORT_SYMBOL(page_pool_ethtool_stats_get);
++
+ #else
+ #define alloc_stat_inc(pool, __stat)
+ #define recycle_stat_inc(pool, __stat)
diff --git a/target/linux/generic/backport-6.1/605-v5.18-xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch b/target/linux/generic/backport-6.1/605-v5.18-xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch
new file mode 100644
index 0000000000..4a914404a2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/605-v5.18-xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch
@@ -0,0 +1,99 @@
+From 2e88d4ff03013937028f5397268b21e10cf68713 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 21 Jan 2022 11:09:45 +0100
+Subject: [PATCH] xdp: introduce flags field in xdp_buff/xdp_frame
+
+Introduce flags field in xdp_frame and xdp_buffer data structures
+to define additional buffer features. At the moment the only
+supported buffer feature is frags bit (XDP_FLAGS_HAS_FRAGS).
+frags bit is used to specify if this is a linear buffer
+(XDP_FLAGS_HAS_FRAGS not set) or a frags frame (XDP_FLAGS_HAS_FRAGS
+set). In the latter case the driver is expected to initialize the
+skb_shared_info structure at the end of the first buffer to link together
+subsequent buffers belonging to the same frame.
+
+Acked-by: Toke Hoiland-Jorgensen <toke@redhat.com>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/e389f14f3a162c0a5bc6a2e1aa8dd01a90be117d.1642758637.git.lorenzo@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+---
+ include/net/xdp.h | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+--- a/include/net/xdp.h
++++ b/include/net/xdp.h
+@@ -66,6 +66,10 @@ struct xdp_txq_info {
+ struct net_device *dev;
+ };
+
++enum xdp_buff_flags {
++ XDP_FLAGS_HAS_FRAGS = BIT(0), /* non-linear xdp buff */
++};
++
+ struct xdp_buff {
+ void *data;
+ void *data_end;
+@@ -74,13 +78,30 @@ struct xdp_buff {
+ struct xdp_rxq_info *rxq;
+ struct xdp_txq_info *txq;
+ u32 frame_sz; /* frame size to deduce data_hard_end/reserved tailroom*/
++ u32 flags; /* supported values defined in xdp_buff_flags */
+ };
+
++static __always_inline bool xdp_buff_has_frags(struct xdp_buff *xdp)
++{
++ return !!(xdp->flags & XDP_FLAGS_HAS_FRAGS);
++}
++
++static __always_inline void xdp_buff_set_frags_flag(struct xdp_buff *xdp)
++{
++ xdp->flags |= XDP_FLAGS_HAS_FRAGS;
++}
++
++static __always_inline void xdp_buff_clear_frags_flag(struct xdp_buff *xdp)
++{
++ xdp->flags &= ~XDP_FLAGS_HAS_FRAGS;
++}
++
+ static __always_inline void
+ xdp_init_buff(struct xdp_buff *xdp, u32 frame_sz, struct xdp_rxq_info *rxq)
+ {
+ xdp->frame_sz = frame_sz;
+ xdp->rxq = rxq;
++ xdp->flags = 0;
+ }
+
+ static __always_inline void
+@@ -122,8 +143,14 @@ struct xdp_frame {
+ */
+ struct xdp_mem_info mem;
+ struct net_device *dev_rx; /* used by cpumap */
++ u32 flags; /* supported values defined in xdp_buff_flags */
+ };
+
++static __always_inline bool xdp_frame_has_frags(struct xdp_frame *frame)
++{
++ return !!(frame->flags & XDP_FLAGS_HAS_FRAGS);
++}
++
+ #define XDP_BULK_QUEUE_SIZE 16
+ struct xdp_frame_bulk {
+ int count;
+@@ -180,6 +207,7 @@ void xdp_convert_frame_to_buff(struct xd
+ xdp->data_end = frame->data + frame->len;
+ xdp->data_meta = frame->data - frame->metasize;
+ xdp->frame_sz = frame->frame_sz;
++ xdp->flags = frame->flags;
+ }
+
+ static inline
+@@ -206,6 +234,7 @@ int xdp_update_frame_from_buff(struct xd
+ xdp_frame->headroom = headroom - sizeof(*xdp_frame);
+ xdp_frame->metasize = metasize;
+ xdp_frame->frame_sz = xdp->frame_sz;
++ xdp_frame->flags = xdp->flags;
+
+ return 0;
+ }
diff --git a/target/linux/generic/backport-6.1/606-v5.18-xdp-add-frags-support-to-xdp_return_-buff-frame.patch b/target/linux/generic/backport-6.1/606-v5.18-xdp-add-frags-support-to-xdp_return_-buff-frame.patch
new file mode 100644
index 0000000000..86d24367dd
--- /dev/null
+++ b/target/linux/generic/backport-6.1/606-v5.18-xdp-add-frags-support-to-xdp_return_-buff-frame.patch
@@ -0,0 +1,137 @@
+From 7c48cb0176c6d6d3b55029f7ff4ffa05faee6446 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 21 Jan 2022 11:09:50 +0100
+Subject: [PATCH] xdp: add frags support to xdp_return_{buff/frame}
+
+Take into account if the received xdp_buff/xdp_frame is non-linear
+recycling/returning the frame memory to the allocator or into
+xdp_frame_bulk.
+
+Acked-by: Toke Hoiland-Jorgensen <toke@redhat.com>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/a961069febc868508ce1bdf5e53a343eb4e57cb2.1642758637.git.lorenzo@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+---
+ include/net/xdp.h | 18 ++++++++++++++--
+ net/core/xdp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 69 insertions(+), 3 deletions(-)
+
+--- a/include/net/xdp.h
++++ b/include/net/xdp.h
+@@ -275,10 +275,24 @@ void __xdp_release_frame(void *data, str
+ static inline void xdp_release_frame(struct xdp_frame *xdpf)
+ {
+ struct xdp_mem_info *mem = &xdpf->mem;
++ struct skb_shared_info *sinfo;
++ int i;
+
+ /* Curr only page_pool needs this */
+- if (mem->type == MEM_TYPE_PAGE_POOL)
+- __xdp_release_frame(xdpf->data, mem);
++ if (mem->type != MEM_TYPE_PAGE_POOL)
++ return;
++
++ if (likely(!xdp_frame_has_frags(xdpf)))
++ goto out;
++
++ sinfo = xdp_get_shared_info_from_frame(xdpf);
++ for (i = 0; i < sinfo->nr_frags; i++) {
++ struct page *page = skb_frag_page(&sinfo->frags[i]);
++
++ __xdp_release_frame(page_address(page), mem);
++ }
++out:
++ __xdp_release_frame(xdpf->data, mem);
+ }
+
+ int xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq,
+--- a/net/core/xdp.c
++++ b/net/core/xdp.c
+@@ -376,12 +376,38 @@ static void __xdp_return(void *data, str
+
+ void xdp_return_frame(struct xdp_frame *xdpf)
+ {
++ struct skb_shared_info *sinfo;
++ int i;
++
++ if (likely(!xdp_frame_has_frags(xdpf)))
++ goto out;
++
++ sinfo = xdp_get_shared_info_from_frame(xdpf);
++ for (i = 0; i < sinfo->nr_frags; i++) {
++ struct page *page = skb_frag_page(&sinfo->frags[i]);
++
++ __xdp_return(page_address(page), &xdpf->mem, false, NULL);
++ }
++out:
+ __xdp_return(xdpf->data, &xdpf->mem, false, NULL);
+ }
+ EXPORT_SYMBOL_GPL(xdp_return_frame);
+
+ void xdp_return_frame_rx_napi(struct xdp_frame *xdpf)
+ {
++ struct skb_shared_info *sinfo;
++ int i;
++
++ if (likely(!xdp_frame_has_frags(xdpf)))
++ goto out;
++
++ sinfo = xdp_get_shared_info_from_frame(xdpf);
++ for (i = 0; i < sinfo->nr_frags; i++) {
++ struct page *page = skb_frag_page(&sinfo->frags[i]);
++
++ __xdp_return(page_address(page), &xdpf->mem, true, NULL);
++ }
++out:
+ __xdp_return(xdpf->data, &xdpf->mem, true, NULL);
+ }
+ EXPORT_SYMBOL_GPL(xdp_return_frame_rx_napi);
+@@ -417,7 +443,7 @@ void xdp_return_frame_bulk(struct xdp_fr
+ struct xdp_mem_allocator *xa;
+
+ if (mem->type != MEM_TYPE_PAGE_POOL) {
+- __xdp_return(xdpf->data, &xdpf->mem, false, NULL);
++ xdp_return_frame(xdpf);
+ return;
+ }
+
+@@ -436,12 +462,38 @@ void xdp_return_frame_bulk(struct xdp_fr
+ bq->xa = rhashtable_lookup(mem_id_ht, &mem->id, mem_id_rht_params);
+ }
+
++ if (unlikely(xdp_frame_has_frags(xdpf))) {
++ struct skb_shared_info *sinfo;
++ int i;
++
++ sinfo = xdp_get_shared_info_from_frame(xdpf);
++ for (i = 0; i < sinfo->nr_frags; i++) {
++ skb_frag_t *frag = &sinfo->frags[i];
++
++ bq->q[bq->count++] = skb_frag_address(frag);
++ if (bq->count == XDP_BULK_QUEUE_SIZE)
++ xdp_flush_frame_bulk(bq);
++ }
++ }
+ bq->q[bq->count++] = xdpf->data;
+ }
+ EXPORT_SYMBOL_GPL(xdp_return_frame_bulk);
+
+ void xdp_return_buff(struct xdp_buff *xdp)
+ {
++ struct skb_shared_info *sinfo;
++ int i;
++
++ if (likely(!xdp_buff_has_frags(xdp)))
++ goto out;
++
++ sinfo = xdp_get_shared_info_from_buff(xdp);
++ for (i = 0; i < sinfo->nr_frags; i++) {
++ struct page *page = skb_frag_page(&sinfo->frags[i]);
++
++ __xdp_return(page_address(page), &xdp->rxq->mem, true, xdp);
++ }
++out:
+ __xdp_return(xdp->data, &xdp->rxq->mem, true, xdp);
+ }
+
diff --git a/target/linux/generic/backport-6.1/607-v5.18-net-skbuff-add-size-metadata-to-skb_shared_info-for-.patch b/target/linux/generic/backport-6.1/607-v5.18-net-skbuff-add-size-metadata-to-skb_shared_info-for-.patch
new file mode 100644
index 0000000000..36f55d511a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/607-v5.18-net-skbuff-add-size-metadata-to-skb_shared_info-for-.patch
@@ -0,0 +1,31 @@
+From d16697cb6261d4cc23422e6b1cb2759df8aa76d0 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 21 Jan 2022 11:09:44 +0100
+Subject: [PATCH] net: skbuff: add size metadata to skb_shared_info for xdp
+
+Introduce xdp_frags_size field in skb_shared_info data structure
+to store xdp_buff/xdp_frame frame paged size (xdp_frags_size will
+be used in xdp frags support). In order to not increase
+skb_shared_info size we will use a hole due to skb_shared_info
+alignment.
+
+Acked-by: Toke Hoiland-Jorgensen <toke@redhat.com>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/8a849819a3e0a143d540f78a3a5add76e17e980d.1642758637.git.lorenzo@kernel.org
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+---
+ include/linux/skbuff.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -568,6 +568,7 @@ struct skb_shared_info {
+ * Warning : all fields before dataref are cleared in __alloc_skb()
+ */
+ atomic_t dataref;
++ unsigned int xdp_frags_size;
+
+ /* Intermediate layers must ensure that destructor_arg
+ * remains valid until skb destructor */
diff --git a/target/linux/generic/backport-6.1/608-v5.18-net-veth-Account-total-xdp_frame-len-running-ndo_xdp.patch b/target/linux/generic/backport-6.1/608-v5.18-net-veth-Account-total-xdp_frame-len-running-ndo_xdp.patch
new file mode 100644
index 0000000000..3bdba8728c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/608-v5.18-net-veth-Account-total-xdp_frame-len-running-ndo_xdp.patch
@@ -0,0 +1,65 @@
+From 5142239a22219921a7863cf00c9ab853c00689d8 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 11 Mar 2022 10:14:18 +0100
+Subject: [PATCH] net: veth: Account total xdp_frame len running ndo_xdp_xmit
+
+Even if this is a theoretical issue since it is not possible to perform
+XDP_REDIRECT on a non-linear xdp_frame, veth driver does not account
+paged area in ndo_xdp_xmit function pointer.
+Introduce xdp_get_frame_len utility routine to get the xdp_frame full
+length and account total frame size running XDP_REDIRECT of a
+non-linear xdp frame into a veth device.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Toke Hoiland-Jorgensen <toke@redhat.com>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Link: https://lore.kernel.org/bpf/54f9fd3bb65d190daf2c0bbae2f852ff16cfbaa0.1646989407.git.lorenzo@kernel.org
+---
+ drivers/net/veth.c | 4 ++--
+ include/net/xdp.h | 14 ++++++++++++++
+ 2 files changed, 16 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/veth.c
++++ b/drivers/net/veth.c
+@@ -501,7 +501,7 @@ static int veth_xdp_xmit(struct net_devi
+ struct xdp_frame *frame = frames[i];
+ void *ptr = veth_xdp_to_ptr(frame);
+
+- if (unlikely(frame->len > max_len ||
++ if (unlikely(xdp_get_frame_len(frame) > max_len ||
+ __ptr_ring_produce(&rq->xdp_ring, ptr)))
+ break;
+ nxmit++;
+@@ -862,7 +862,7 @@ static int veth_xdp_rcv(struct veth_rq *
+ /* ndo_xdp_xmit */
+ struct xdp_frame *frame = veth_ptr_to_xdp(ptr);
+
+- stats->xdp_bytes += frame->len;
++ stats->xdp_bytes += xdp_get_frame_len(frame);
+ frame = veth_xdp_rcv_one(rq, frame, bq, stats);
+ if (frame) {
+ /* XDP_PASS */
+--- a/include/net/xdp.h
++++ b/include/net/xdp.h
+@@ -295,6 +295,20 @@ out:
+ __xdp_release_frame(xdpf->data, mem);
+ }
+
++static __always_inline unsigned int xdp_get_frame_len(struct xdp_frame *xdpf)
++{
++ struct skb_shared_info *sinfo;
++ unsigned int len = xdpf->len;
++
++ if (likely(!xdp_frame_has_frags(xdpf)))
++ goto out;
++
++ sinfo = xdp_get_shared_info_from_frame(xdpf);
++ len += sinfo->xdp_frags_size;
++out:
++ return len;
++}
++
+ int xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq,
+ struct net_device *dev, u32 queue_index, unsigned int napi_id);
+ void xdp_rxq_info_unreg(struct xdp_rxq_info *xdp_rxq);
diff --git a/target/linux/generic/backport-6.1/609-v5.18-veth-Allow-jumbo-frames-in-xdp-mode.patch b/target/linux/generic/backport-6.1/609-v5.18-veth-Allow-jumbo-frames-in-xdp-mode.patch
new file mode 100644
index 0000000000..31b44f1170
--- /dev/null
+++ b/target/linux/generic/backport-6.1/609-v5.18-veth-Allow-jumbo-frames-in-xdp-mode.patch
@@ -0,0 +1,40 @@
+From 7cda76d858a4e71ac4a04066c093679a12e1312c Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 11 Mar 2022 10:14:20 +0100
+Subject: [PATCH] veth: Allow jumbo frames in xdp mode
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Allow increasing the MTU over page boundaries on veth devices
+if the attached xdp program declares to support xdp fragments.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Link: https://lore.kernel.org/bpf/d5dc039c3d4123426e7023a488c449181a7bc57f.1646989407.git.lorenzo@kernel.org
+---
+ drivers/net/veth.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/veth.c
++++ b/drivers/net/veth.c
+@@ -1471,9 +1471,14 @@ static int veth_xdp_set(struct net_devic
+ goto err;
+ }
+
+- max_mtu = PAGE_SIZE - VETH_XDP_HEADROOM -
+- peer->hard_header_len -
+- SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
++ max_mtu = SKB_WITH_OVERHEAD(PAGE_SIZE - VETH_XDP_HEADROOM) -
++ peer->hard_header_len;
++ /* Allow increasing the max_mtu if the program supports
++ * XDP fragments.
++ */
++ //if (prog->aux->xdp_has_frags)
++ max_mtu += PAGE_SIZE * MAX_SKB_FRAGS;
++
+ if (peer->mtu > max_mtu) {
+ NL_SET_ERR_MSG_MOD(extack, "Peer MTU is too large to set XDP");
+ err = -ERANGE;
diff --git a/target/linux/generic/backport-6.1/610-v6.3-net-page_pool-use-in_softirq-instead.patch b/target/linux/generic/backport-6.1/610-v6.3-net-page_pool-use-in_softirq-instead.patch
new file mode 100644
index 0000000000..71e1140595
--- /dev/null
+++ b/target/linux/generic/backport-6.1/610-v6.3-net-page_pool-use-in_softirq-instead.patch
@@ -0,0 +1,56 @@
+From: Qingfang DENG <qingfang.deng@siflower.com.cn>
+Date: Fri, 3 Feb 2023 09:16:11 +0800
+Subject: [PATCH] net: page_pool: use in_softirq() instead
+
+We use BH context only for synchronization, so we don't care if it's
+actually serving softirq or not.
+
+As a side node, in case of threaded NAPI, in_serving_softirq() will
+return false because it's in process context with BH off, making
+page_pool_recycle_in_cache() unreachable.
+
+Signed-off-by: Qingfang DENG <qingfang.deng@siflower.com.cn>
+---
+
+--- a/include/net/page_pool.h
++++ b/include/net/page_pool.h
+@@ -357,7 +357,7 @@ static inline void page_pool_nid_changed
+ static inline void page_pool_ring_lock(struct page_pool *pool)
+ __acquires(&pool->ring.producer_lock)
+ {
+- if (in_serving_softirq())
++ if (in_softirq())
+ spin_lock(&pool->ring.producer_lock);
+ else
+ spin_lock_bh(&pool->ring.producer_lock);
+@@ -366,7 +366,7 @@ static inline void page_pool_ring_lock(s
+ static inline void page_pool_ring_unlock(struct page_pool *pool)
+ __releases(&pool->ring.producer_lock)
+ {
+- if (in_serving_softirq())
++ if (in_softirq())
+ spin_unlock(&pool->ring.producer_lock);
+ else
+ spin_unlock_bh(&pool->ring.producer_lock);
+--- a/net/core/page_pool.c
++++ b/net/core/page_pool.c
+@@ -512,8 +512,8 @@ static void page_pool_return_page(struct
+ static bool page_pool_recycle_in_ring(struct page_pool *pool, struct page *page)
+ {
+ int ret;
+- /* BH protection not needed if current is serving softirq */
+- if (in_serving_softirq())
++ /* BH protection not needed if current is softirq */
++ if (in_softirq())
+ ret = ptr_ring_produce(&pool->ring, page);
+ else
+ ret = ptr_ring_produce_bh(&pool->ring, page);
+@@ -576,7 +576,7 @@ __page_pool_put_page(struct page_pool *p
+ page_pool_dma_sync_for_device(pool, page,
+ dma_sync_size);
+
+- if (allow_direct && in_serving_softirq() &&
++ if (allow_direct && in_softirq() &&
+ page_pool_recycle_in_cache(page, pool))
+ return NULL;
+
diff --git a/target/linux/generic/backport-6.1/611-v6.3-net-add-helper-eth_addr_add.patch b/target/linux/generic/backport-6.1/611-v6.3-net-add-helper-eth_addr_add.patch
new file mode 100644
index 0000000000..c5d5d2c3a9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/611-v6.3-net-add-helper-eth_addr_add.patch
@@ -0,0 +1,41 @@
+From 7390609b0121a1b982c5ecdfcd72dc328e5784ee Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 6 Feb 2023 13:43:42 +0000
+Subject: [PATCH] net: add helper eth_addr_add()
+
+Add a helper to add an offset to a ethernet address. This comes in handy
+if you have a base ethernet address for multiple interfaces.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Acked-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-9-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/etherdevice.h | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/include/linux/etherdevice.h
++++ b/include/linux/etherdevice.h
+@@ -478,6 +478,20 @@ static inline void eth_addr_inc(u8 *addr
+ }
+
+ /**
++ * eth_addr_add() - Add (or subtract) an offset to/from the given MAC address.
++ *
++ * @offset: Offset to add.
++ * @addr: Pointer to a six-byte array containing Ethernet address to increment.
++ */
++static inline void eth_addr_add(u8 *addr, long offset)
++{
++ u64 u = ether_addr_to_u64(addr);
++
++ u += offset;
++ u64_to_ether_addr(u, addr);
++}
++
++/**
+ * is_etherdev_addr - Tell if given Ethernet address belongs to the device.
+ * @dev: Pointer to a device structure
+ * @addr: Pointer to a six-byte array containing the Ethernet address
diff --git a/target/linux/generic/backport-6.1/700-v5.17-net-dsa-introduce-tagger-owned-storage-for-private.patch b/target/linux/generic/backport-6.1/700-v5.17-net-dsa-introduce-tagger-owned-storage-for-private.patch
new file mode 100644
index 0000000000..f2b651f0c6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/700-v5.17-net-dsa-introduce-tagger-owned-storage-for-private.patch
@@ -0,0 +1,279 @@
+From dc452a471dbae8aca8257c565174212620880093 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Fri, 10 Dec 2021 01:34:37 +0200
+Subject: net: dsa: introduce tagger-owned storage for private and shared data
+
+Ansuel is working on register access over Ethernet for the qca8k switch
+family. This requires the qca8k tagging protocol driver to receive
+frames which aren't intended for the network stack, but instead for the
+qca8k switch driver itself.
+
+The dp->priv is currently the prevailing method for passing data back
+and forth between the tagging protocol driver and the switch driver.
+However, this method is riddled with caveats.
+
+The DSA design allows in principle for any switch driver to return any
+protocol it desires in ->get_tag_protocol(). The dsa_loop driver can be
+modified to do just that. But in the current design, the memory behind
+dp->priv has to be allocated by the switch driver, so if the tagging
+protocol is paired to an unexpected switch driver, we may end up in NULL
+pointer dereferences inside the kernel, or worse (a switch driver may
+allocate dp->priv according to the expectations of a different tagger).
+
+The latter possibility is even more plausible considering that DSA
+switches can dynamically change tagging protocols in certain cases
+(dsa <-> edsa, ocelot <-> ocelot-8021q), and the current design lends
+itself to mistakes that are all too easy to make.
+
+This patch proposes that the tagging protocol driver should manage its
+own memory, instead of relying on the switch driver to do so.
+After analyzing the different in-tree needs, it can be observed that the
+required tagger storage is per switch, therefore a ds->tagger_data
+pointer is introduced. In principle, per-port storage could also be
+introduced, although there is no need for it at the moment. Future
+changes will replace the current usage of dp->priv with ds->tagger_data.
+
+We define a "binding" event between the DSA switch tree and the tagging
+protocol. During this binding event, the tagging protocol's ->connect()
+method is called first, and this may allocate some memory for each
+switch of the tree. Then a cross-chip notifier is emitted for the
+switches within that tree, and they are given the opportunity to fix up
+the tagger's memory (for example, they might set up some function
+pointers that represent virtual methods for consuming packets).
+Because the memory is owned by the tagger, there exists a ->disconnect()
+method for the tagger (which is the place to free the resources), but
+there doesn't exist a ->disconnect() method for the switch driver.
+This is part of the design. The switch driver should make minimal use of
+the public part of the tagger data, and only after type-checking it
+using the supplied "proto" argument.
+
+In the code there are in fact two binding events, one is the initial
+event in dsa_switch_setup_tag_protocol(). At this stage, the cross chip
+notifier chains aren't initialized, so we call each switch's connect()
+method by hand. Then there is dsa_tree_bind_tag_proto() during
+dsa_tree_change_tag_proto(), and here we have an old protocol and a new
+one. We first connect to the new one before disconnecting from the old
+one, to simplify error handling a bit and to ensure we remain in a valid
+state at all times.
+
+Co-developed-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 12 +++++++++
+ net/dsa/dsa2.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++---
+ net/dsa/dsa_priv.h | 1 +
+ net/dsa/switch.c | 14 +++++++++++
+ 4 files changed, 96 insertions(+), 4 deletions(-)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -80,12 +80,15 @@ enum dsa_tag_protocol {
+ };
+
+ struct dsa_switch;
++struct dsa_switch_tree;
+
+ struct dsa_device_ops {
+ struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev);
+ struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev);
+ void (*flow_dissect)(const struct sk_buff *skb, __be16 *proto,
+ int *offset);
++ int (*connect)(struct dsa_switch_tree *dst);
++ void (*disconnect)(struct dsa_switch_tree *dst);
+ unsigned int needed_headroom;
+ unsigned int needed_tailroom;
+ const char *name;
+@@ -329,6 +332,8 @@ struct dsa_switch {
+ */
+ void *priv;
+
++ void *tagger_data;
++
+ /*
+ * Configuration data for this switch.
+ */
+@@ -584,6 +589,13 @@ struct dsa_switch_ops {
+ enum dsa_tag_protocol mprot);
+ int (*change_tag_protocol)(struct dsa_switch *ds, int port,
+ enum dsa_tag_protocol proto);
++ /*
++ * Method for switch drivers to connect to the tagging protocol driver
++ * in current use. The switch driver can provide handlers for certain
++ * types of packets for switch management.
++ */
++ int (*connect_tag_protocol)(struct dsa_switch *ds,
++ enum dsa_tag_protocol proto);
+
+ /* Optional switch-wide initialization and destruction methods */
+ int (*setup)(struct dsa_switch *ds);
+--- a/net/dsa/dsa2.c
++++ b/net/dsa/dsa2.c
+@@ -230,8 +230,12 @@ static struct dsa_switch_tree *dsa_tree_
+
+ static void dsa_tree_free(struct dsa_switch_tree *dst)
+ {
+- if (dst->tag_ops)
++ if (dst->tag_ops) {
++ if (dst->tag_ops->disconnect)
++ dst->tag_ops->disconnect(dst);
++
+ dsa_tag_driver_put(dst->tag_ops);
++ }
+ list_del(&dst->list);
+ kfree(dst);
+ }
+@@ -805,7 +809,7 @@ static int dsa_switch_setup_tag_protocol
+ int port, err;
+
+ if (tag_ops->proto == dst->default_proto)
+- return 0;
++ goto connect;
+
+ for (port = 0; port < ds->num_ports; port++) {
+ if (!dsa_is_cpu_port(ds, port))
+@@ -821,6 +825,17 @@ static int dsa_switch_setup_tag_protocol
+ }
+ }
+
++connect:
++ if (ds->ops->connect_tag_protocol) {
++ err = ds->ops->connect_tag_protocol(ds, tag_ops->proto);
++ if (err) {
++ dev_err(ds->dev,
++ "Unable to connect to tag protocol \"%s\": %pe\n",
++ tag_ops->name, ERR_PTR(err));
++ return err;
++ }
++ }
++
+ return 0;
+ }
+
+@@ -1132,6 +1147,46 @@ static void dsa_tree_teardown(struct dsa
+ dst->setup = false;
+ }
+
++static int dsa_tree_bind_tag_proto(struct dsa_switch_tree *dst,
++ const struct dsa_device_ops *tag_ops)
++{
++ const struct dsa_device_ops *old_tag_ops = dst->tag_ops;
++ struct dsa_notifier_tag_proto_info info;
++ int err;
++
++ dst->tag_ops = tag_ops;
++
++ /* Notify the new tagger about the connection to this tree */
++ if (tag_ops->connect) {
++ err = tag_ops->connect(dst);
++ if (err)
++ goto out_revert;
++ }
++
++ /* Notify the switches from this tree about the connection
++ * to the new tagger
++ */
++ info.tag_ops = tag_ops;
++ err = dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_CONNECT, &info);
++ if (err && err != -EOPNOTSUPP)
++ goto out_disconnect;
++
++ /* Notify the old tagger about the disconnection from this tree */
++ if (old_tag_ops->disconnect)
++ old_tag_ops->disconnect(dst);
++
++ return 0;
++
++out_disconnect:
++ /* Revert the new tagger's connection to this tree */
++ if (tag_ops->disconnect)
++ tag_ops->disconnect(dst);
++out_revert:
++ dst->tag_ops = old_tag_ops;
++
++ return err;
++}
++
+ /* Since the dsa/tagging sysfs device attribute is per master, the assumption
+ * is that all DSA switches within a tree share the same tagger, otherwise
+ * they would have formed disjoint trees (different "dsa,member" values).
+@@ -1164,12 +1219,15 @@ int dsa_tree_change_tag_proto(struct dsa
+ goto out_unlock;
+ }
+
++ /* Notify the tag protocol change */
+ info.tag_ops = tag_ops;
+ err = dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO, &info);
+ if (err)
+- goto out_unwind_tagger;
++ return err;
+
+- dst->tag_ops = tag_ops;
++ err = dsa_tree_bind_tag_proto(dst, tag_ops);
++ if (err)
++ goto out_unwind_tagger;
+
+ rtnl_unlock();
+
+@@ -1257,6 +1315,7 @@ static int dsa_port_parse_cpu(struct dsa
+ struct dsa_switch *ds = dp->ds;
+ struct dsa_switch_tree *dst = ds->dst;
+ enum dsa_tag_protocol default_proto;
++ int err;
+
+ /* Find out which protocol the switch would prefer. */
+ default_proto = dsa_get_tag_protocol(dp, master);
+@@ -1311,6 +1370,12 @@ static int dsa_port_parse_cpu(struct dsa
+ */
+ dsa_tag_driver_put(tag_ops);
+ } else {
++ if (tag_ops->connect) {
++ err = tag_ops->connect(dst);
++ if (err)
++ return err;
++ }
++
+ dst->tag_ops = tag_ops;
+ }
+
+--- a/net/dsa/dsa_priv.h
++++ b/net/dsa/dsa_priv.h
+@@ -37,6 +37,7 @@ enum {
+ DSA_NOTIFIER_VLAN_DEL,
+ DSA_NOTIFIER_MTU,
+ DSA_NOTIFIER_TAG_PROTO,
++ DSA_NOTIFIER_TAG_PROTO_CONNECT,
+ DSA_NOTIFIER_MRP_ADD,
+ DSA_NOTIFIER_MRP_DEL,
+ DSA_NOTIFIER_MRP_ADD_RING_ROLE,
+--- a/net/dsa/switch.c
++++ b/net/dsa/switch.c
+@@ -616,6 +616,17 @@ static int dsa_switch_change_tag_proto(s
+ return 0;
+ }
+
++static int dsa_switch_connect_tag_proto(struct dsa_switch *ds,
++ struct dsa_notifier_tag_proto_info *info)
++{
++ const struct dsa_device_ops *tag_ops = info->tag_ops;
++
++ if (!ds->ops->connect_tag_protocol)
++ return -EOPNOTSUPP;
++
++ return ds->ops->connect_tag_protocol(ds, tag_ops->proto);
++}
++
+ static int dsa_switch_mrp_add(struct dsa_switch *ds,
+ struct dsa_notifier_mrp_info *info)
+ {
+@@ -735,6 +746,9 @@ static int dsa_switch_event(struct notif
+ case DSA_NOTIFIER_TAG_PROTO:
+ err = dsa_switch_change_tag_proto(ds, info);
+ break;
++ case DSA_NOTIFIER_TAG_PROTO_CONNECT:
++ err = dsa_switch_connect_tag_proto(ds, info);
++ break;
+ case DSA_NOTIFIER_MRP_ADD:
+ err = dsa_switch_mrp_add(ds, info);
+ break;
diff --git a/target/linux/generic/backport-6.1/701-v5.17-dsa-make-tagging-protocols-connect-to-individual-switches.patch b/target/linux/generic/backport-6.1/701-v5.17-dsa-make-tagging-protocols-connect-to-individual-switches.patch
new file mode 100644
index 0000000000..0c50ae6fb9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/701-v5.17-dsa-make-tagging-protocols-connect-to-individual-switches.patch
@@ -0,0 +1,274 @@
+From 7f2973149c22e7a6fee4c0c9fa6b8e4108e9c208 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Tue, 14 Dec 2021 03:45:36 +0200
+Subject: net: dsa: make tagging protocols connect to individual switches from
+ a tree
+
+On the NXP Bluebox 3 board which uses a multi-switch setup with sja1105,
+the mechanism through which the tagger connects to the switch tree is
+broken, due to improper DSA code design. At the time when tag_ops->connect()
+is called in dsa_port_parse_cpu(), DSA hasn't finished "touching" all
+the ports, so it doesn't know how large the tree is and how many ports
+it has. It has just seen the first CPU port by this time. As a result,
+this function will call the tagger's ->connect method too early, and the
+tagger will connect only to the first switch from the tree.
+
+This could be perhaps addressed a bit more simply by just moving the
+tag_ops->connect(dst) call a bit later (for example in dsa_tree_setup),
+but there is already a design inconsistency at present: on the switch
+side, the notification is on a per-switch basis, but on the tagger side,
+it is on a per-tree basis. Furthermore, the persistent storage itself is
+per switch (ds->tagger_data). And the tagger connect and disconnect
+procedures (at least the ones that exist currently) could see a fair bit
+of simplification if they didn't have to iterate through the switches of
+a tree.
+
+To fix the issue, this change transforms tag_ops->connect(dst) into
+tag_ops->connect(ds) and moves it somewhere where we already iterate
+over all switches of a tree. That is in dsa_switch_setup_tag_protocol(),
+which is a good placement because we already have there the connection
+call to the switch side of things.
+
+As for the dsa_tree_bind_tag_proto() method (called from the code path
+that changes the tag protocol), things are a bit more complicated
+because we receive the tree as argument, yet when we unwind on errors,
+it would be nice to not call tag_ops->disconnect(ds) where we didn't
+previously call tag_ops->connect(ds). We didn't have this problem before
+because the tag_ops connection operations passed the entire dst before,
+and this is more fine grained now. To solve the error rewind case using
+the new API, we have to create yet one more cross-chip notifier for
+disconnection, and stay connected with the old tag protocol to all the
+switches in the tree until we've succeeded to connect with the new one
+as well. So if something fails half way, the whole tree is still
+connected to the old tagger. But there may still be leaks if the tagger
+fails to connect to the 2nd out of 3 switches in a tree: somebody needs
+to tell the tagger to disconnect from the first switch. Nothing comes
+for free, and this was previously handled privately by the tagging
+protocol driver before, but now we need to emit a disconnect cross-chip
+notifier for that, because DSA has to take care of the unwind path. We
+assume that the tagging protocol has connected to a switch if it has set
+ds->tagger_data to something, otherwise we avoid calling its
+disconnection method in the error rewind path.
+
+The rest of the changes are in the tagging protocol drivers, and have to
+do with the replacement of dst with ds. The iteration is removed and the
+error unwind path is simplified, as mentioned above.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 5 ++--
+ net/dsa/dsa2.c | 44 +++++++++++++-----------------
+ net/dsa/dsa_priv.h | 1 +
+ net/dsa/switch.c | 52 ++++++++++++++++++++++++++++++++---
+ net/dsa/tag_ocelot_8021q.c | 53 +++++++++++-------------------------
+ net/dsa/tag_sja1105.c | 67 ++++++++++++++++------------------------------
+ 6 files changed, 109 insertions(+), 113 deletions(-)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -80,15 +80,14 @@ enum dsa_tag_protocol {
+ };
+
+ struct dsa_switch;
+-struct dsa_switch_tree;
+
+ struct dsa_device_ops {
+ struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev);
+ struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev);
+ void (*flow_dissect)(const struct sk_buff *skb, __be16 *proto,
+ int *offset);
+- int (*connect)(struct dsa_switch_tree *dst);
+- void (*disconnect)(struct dsa_switch_tree *dst);
++ int (*connect)(struct dsa_switch *ds);
++ void (*disconnect)(struct dsa_switch *ds);
+ unsigned int needed_headroom;
+ unsigned int needed_tailroom;
+ const char *name;
+--- a/net/dsa/dsa2.c
++++ b/net/dsa/dsa2.c
+@@ -230,12 +230,8 @@ static struct dsa_switch_tree *dsa_tree_
+
+ static void dsa_tree_free(struct dsa_switch_tree *dst)
+ {
+- if (dst->tag_ops) {
+- if (dst->tag_ops->disconnect)
+- dst->tag_ops->disconnect(dst);
+-
++ if (dst->tag_ops)
+ dsa_tag_driver_put(dst->tag_ops);
+- }
+ list_del(&dst->list);
+ kfree(dst);
+ }
+@@ -826,17 +822,29 @@ static int dsa_switch_setup_tag_protocol
+ }
+
+ connect:
++ if (tag_ops->connect) {
++ err = tag_ops->connect(ds);
++ if (err)
++ return err;
++ }
++
+ if (ds->ops->connect_tag_protocol) {
+ err = ds->ops->connect_tag_protocol(ds, tag_ops->proto);
+ if (err) {
+ dev_err(ds->dev,
+ "Unable to connect to tag protocol \"%s\": %pe\n",
+ tag_ops->name, ERR_PTR(err));
+- return err;
++ goto disconnect;
+ }
+ }
+
+ return 0;
++
++disconnect:
++ if (tag_ops->disconnect)
++ tag_ops->disconnect(ds);
++
++ return err;
+ }
+
+ static int dsa_switch_setup(struct dsa_switch *ds)
+@@ -1156,13 +1164,6 @@ static int dsa_tree_bind_tag_proto(struc
+
+ dst->tag_ops = tag_ops;
+
+- /* Notify the new tagger about the connection to this tree */
+- if (tag_ops->connect) {
+- err = tag_ops->connect(dst);
+- if (err)
+- goto out_revert;
+- }
+-
+ /* Notify the switches from this tree about the connection
+ * to the new tagger
+ */
+@@ -1172,16 +1173,14 @@ static int dsa_tree_bind_tag_proto(struc
+ goto out_disconnect;
+
+ /* Notify the old tagger about the disconnection from this tree */
+- if (old_tag_ops->disconnect)
+- old_tag_ops->disconnect(dst);
++ info.tag_ops = old_tag_ops;
++ dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_DISCONNECT, &info);
+
+ return 0;
+
+ out_disconnect:
+- /* Revert the new tagger's connection to this tree */
+- if (tag_ops->disconnect)
+- tag_ops->disconnect(dst);
+-out_revert:
++ info.tag_ops = tag_ops;
++ dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_DISCONNECT, &info);
+ dst->tag_ops = old_tag_ops;
+
+ return err;
+@@ -1315,7 +1314,6 @@ static int dsa_port_parse_cpu(struct dsa
+ struct dsa_switch *ds = dp->ds;
+ struct dsa_switch_tree *dst = ds->dst;
+ enum dsa_tag_protocol default_proto;
+- int err;
+
+ /* Find out which protocol the switch would prefer. */
+ default_proto = dsa_get_tag_protocol(dp, master);
+@@ -1370,12 +1368,6 @@ static int dsa_port_parse_cpu(struct dsa
+ */
+ dsa_tag_driver_put(tag_ops);
+ } else {
+- if (tag_ops->connect) {
+- err = tag_ops->connect(dst);
+- if (err)
+- return err;
+- }
+-
+ dst->tag_ops = tag_ops;
+ }
+
+--- a/net/dsa/dsa_priv.h
++++ b/net/dsa/dsa_priv.h
+@@ -38,6 +38,7 @@ enum {
+ DSA_NOTIFIER_MTU,
+ DSA_NOTIFIER_TAG_PROTO,
+ DSA_NOTIFIER_TAG_PROTO_CONNECT,
++ DSA_NOTIFIER_TAG_PROTO_DISCONNECT,
+ DSA_NOTIFIER_MRP_ADD,
+ DSA_NOTIFIER_MRP_DEL,
+ DSA_NOTIFIER_MRP_ADD_RING_ROLE,
+--- a/net/dsa/switch.c
++++ b/net/dsa/switch.c
+@@ -616,15 +616,58 @@ static int dsa_switch_change_tag_proto(s
+ return 0;
+ }
+
+-static int dsa_switch_connect_tag_proto(struct dsa_switch *ds,
+- struct dsa_notifier_tag_proto_info *info)
++/* We use the same cross-chip notifiers to inform both the tagger side, as well
++ * as the switch side, of connection and disconnection events.
++ * Since ds->tagger_data is owned by the tagger, it isn't a hard error if the
++ * switch side doesn't support connecting to this tagger, and therefore, the
++ * fact that we don't disconnect the tagger side doesn't constitute a memory
++ * leak: the tagger will still operate with persistent per-switch memory, just
++ * with the switch side unconnected to it. What does constitute a hard error is
++ * when the switch side supports connecting but fails.
++ */
++static int
++dsa_switch_connect_tag_proto(struct dsa_switch *ds,
++ struct dsa_notifier_tag_proto_info *info)
+ {
+ const struct dsa_device_ops *tag_ops = info->tag_ops;
++ int err;
++
++ /* Notify the new tagger about the connection to this switch */
++ if (tag_ops->connect) {
++ err = tag_ops->connect(ds);
++ if (err)
++ return err;
++ }
+
+ if (!ds->ops->connect_tag_protocol)
+ return -EOPNOTSUPP;
+
+- return ds->ops->connect_tag_protocol(ds, tag_ops->proto);
++ /* Notify the switch about the connection to the new tagger */
++ err = ds->ops->connect_tag_protocol(ds, tag_ops->proto);
++ if (err) {
++ /* Revert the new tagger's connection to this tree */
++ if (tag_ops->disconnect)
++ tag_ops->disconnect(ds);
++ return err;
++ }
++
++ return 0;
++}
++
++static int
++dsa_switch_disconnect_tag_proto(struct dsa_switch *ds,
++ struct dsa_notifier_tag_proto_info *info)
++{
++ const struct dsa_device_ops *tag_ops = info->tag_ops;
++
++ /* Notify the tagger about the disconnection from this switch */
++ if (tag_ops->disconnect && ds->tagger_data)
++ tag_ops->disconnect(ds);
++
++ /* No need to notify the switch, since it shouldn't have any
++ * resources to tear down
++ */
++ return 0;
+ }
+
+ static int dsa_switch_mrp_add(struct dsa_switch *ds,
+@@ -749,6 +792,9 @@ static int dsa_switch_event(struct notif
+ case DSA_NOTIFIER_TAG_PROTO_CONNECT:
+ err = dsa_switch_connect_tag_proto(ds, info);
+ break;
++ case DSA_NOTIFIER_TAG_PROTO_DISCONNECT:
++ err = dsa_switch_disconnect_tag_proto(ds, info);
++ break;
+ case DSA_NOTIFIER_MRP_ADD:
+ err = dsa_switch_mrp_add(ds, info);
+ break;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch b/target/linux/generic/backport-6.1/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch
new file mode 100644
index 0000000000..c83d659d1c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch
@@ -0,0 +1,327 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 5 Feb 2022 17:59:07 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for coherent
+ DMA
+
+It improves performance by eliminating the need for a cache flush on rx and tx
+In preparation for supporting WED (Wireless Ethernet Dispatch), also add a
+function for disabling coherent DMA at runtime.
+
+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
+@@ -9,6 +9,7 @@
+ #include <linux/of_device.h>
+ #include <linux/of_mdio.h>
+ #include <linux/of_net.h>
++#include <linux/of_address.h>
+ #include <linux/mfd/syscon.h>
+ #include <linux/regmap.h>
+ #include <linux/clk.h>
+@@ -840,7 +841,7 @@ static int mtk_init_fq_dma(struct mtk_et
+ dma_addr_t dma_addr;
+ int i;
+
+- eth->scratch_ring = dma_alloc_coherent(eth->dev,
++ eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
+ cnt * sizeof(struct mtk_tx_dma),
+ &eth->phy_scratch_ring,
+ GFP_ATOMIC);
+@@ -852,10 +853,10 @@ static int mtk_init_fq_dma(struct mtk_et
+ if (unlikely(!eth->scratch_head))
+ return -ENOMEM;
+
+- dma_addr = dma_map_single(eth->dev,
++ dma_addr = dma_map_single(eth->dma_dev,
+ eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
+ DMA_FROM_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
++ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
+ return -ENOMEM;
+
+ phy_ring_tail = eth->phy_scratch_ring +
+@@ -909,26 +910,26 @@ static void mtk_tx_unmap(struct mtk_eth
+ {
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+ if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
+- dma_unmap_single(eth->dev,
++ dma_unmap_single(eth->dma_dev,
+ dma_unmap_addr(tx_buf, dma_addr0),
+ dma_unmap_len(tx_buf, dma_len0),
+ DMA_TO_DEVICE);
+ } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) {
+- dma_unmap_page(eth->dev,
++ dma_unmap_page(eth->dma_dev,
+ dma_unmap_addr(tx_buf, dma_addr0),
+ dma_unmap_len(tx_buf, dma_len0),
+ DMA_TO_DEVICE);
+ }
+ } else {
+ if (dma_unmap_len(tx_buf, dma_len0)) {
+- dma_unmap_page(eth->dev,
++ dma_unmap_page(eth->dma_dev,
+ dma_unmap_addr(tx_buf, dma_addr0),
+ dma_unmap_len(tx_buf, dma_len0),
+ DMA_TO_DEVICE);
+ }
+
+ if (dma_unmap_len(tx_buf, dma_len1)) {
+- dma_unmap_page(eth->dev,
++ dma_unmap_page(eth->dma_dev,
+ dma_unmap_addr(tx_buf, dma_addr1),
+ dma_unmap_len(tx_buf, dma_len1),
+ DMA_TO_DEVICE);
+@@ -1006,9 +1007,9 @@ static int mtk_tx_map(struct sk_buff *sk
+ if (skb_vlan_tag_present(skb))
+ txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb);
+
+- mapped_addr = dma_map_single(eth->dev, skb->data,
++ mapped_addr = dma_map_single(eth->dma_dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
++ if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
+ return -ENOMEM;
+
+ WRITE_ONCE(itxd->txd1, mapped_addr);
+@@ -1047,10 +1048,10 @@ static int mtk_tx_map(struct sk_buff *sk
+
+
+ frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN);
+- mapped_addr = skb_frag_dma_map(eth->dev, frag, offset,
++ mapped_addr = skb_frag_dma_map(eth->dma_dev, frag, offset,
+ frag_map_size,
+ DMA_TO_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
++ if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
+ goto err_dma;
+
+ if (i == nr_frags - 1 &&
+@@ -1331,18 +1332,18 @@ static int mtk_poll_rx(struct napi_struc
+ netdev->stats.rx_dropped++;
+ goto release_desc;
+ }
+- dma_addr = dma_map_single(eth->dev,
++ dma_addr = dma_map_single(eth->dma_dev,
+ new_data + NET_SKB_PAD +
+ eth->ip_align,
+ ring->buf_size,
+ DMA_FROM_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dev, dma_addr))) {
++ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) {
+ skb_free_frag(new_data);
+ netdev->stats.rx_dropped++;
+ goto release_desc;
+ }
+
+- dma_unmap_single(eth->dev, trxd.rxd1,
++ dma_unmap_single(eth->dma_dev, trxd.rxd1,
+ ring->buf_size, DMA_FROM_DEVICE);
+
+ /* receive data */
+@@ -1615,7 +1616,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ if (!ring->buf)
+ goto no_tx_mem;
+
+- ring->dma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
++ ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
+ &ring->phys, GFP_ATOMIC);
+ if (!ring->dma)
+ goto no_tx_mem;
+@@ -1633,7 +1634,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ * descriptors in ring->dma_pdma.
+ */
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+- ring->dma_pdma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
++ ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
+ &ring->phys_pdma,
+ GFP_ATOMIC);
+ if (!ring->dma_pdma)
+@@ -1692,7 +1693,7 @@ static void mtk_tx_clean(struct mtk_eth
+ }
+
+ if (ring->dma) {
+- dma_free_coherent(eth->dev,
++ dma_free_coherent(eth->dma_dev,
+ MTK_DMA_SIZE * sizeof(*ring->dma),
+ ring->dma,
+ ring->phys);
+@@ -1700,7 +1701,7 @@ static void mtk_tx_clean(struct mtk_eth
+ }
+
+ if (ring->dma_pdma) {
+- dma_free_coherent(eth->dev,
++ dma_free_coherent(eth->dma_dev,
+ MTK_DMA_SIZE * sizeof(*ring->dma_pdma),
+ ring->dma_pdma,
+ ring->phys_pdma);
+@@ -1748,18 +1749,18 @@ static int mtk_rx_alloc(struct mtk_eth *
+ return -ENOMEM;
+ }
+
+- ring->dma = dma_alloc_coherent(eth->dev,
++ ring->dma = dma_alloc_coherent(eth->dma_dev,
+ rx_dma_size * sizeof(*ring->dma),
+ &ring->phys, GFP_ATOMIC);
+ if (!ring->dma)
+ return -ENOMEM;
+
+ for (i = 0; i < rx_dma_size; i++) {
+- dma_addr_t dma_addr = dma_map_single(eth->dev,
++ dma_addr_t dma_addr = dma_map_single(eth->dma_dev,
+ ring->data[i] + NET_SKB_PAD + eth->ip_align,
+ ring->buf_size,
+ DMA_FROM_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
++ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
+ return -ENOMEM;
+ ring->dma[i].rxd1 = (unsigned int)dma_addr;
+
+@@ -1795,7 +1796,7 @@ static void mtk_rx_clean(struct mtk_eth
+ continue;
+ if (!ring->dma[i].rxd1)
+ continue;
+- dma_unmap_single(eth->dev,
++ dma_unmap_single(eth->dma_dev,
+ ring->dma[i].rxd1,
+ ring->buf_size,
+ DMA_FROM_DEVICE);
+@@ -1806,7 +1807,7 @@ static void mtk_rx_clean(struct mtk_eth
+ }
+
+ if (ring->dma) {
+- dma_free_coherent(eth->dev,
++ dma_free_coherent(eth->dma_dev,
+ ring->dma_size * sizeof(*ring->dma),
+ ring->dma,
+ ring->phys);
+@@ -2162,7 +2163,7 @@ static void mtk_dma_free(struct mtk_eth
+ if (eth->netdev[i])
+ netdev_reset_queue(eth->netdev[i]);
+ if (eth->scratch_ring) {
+- dma_free_coherent(eth->dev,
++ dma_free_coherent(eth->dma_dev,
+ MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
+ eth->scratch_ring,
+ eth->phy_scratch_ring);
+@@ -2514,6 +2515,8 @@ static void mtk_dim_tx(struct work_struc
+
+ static int mtk_hw_init(struct mtk_eth *eth)
+ {
++ u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
++ ETHSYS_DMA_AG_MAP_PPE;
+ int i, val, ret;
+
+ if (test_and_set_bit(MTK_HW_INIT, &eth->state))
+@@ -2526,6 +2529,10 @@ static int mtk_hw_init(struct mtk_eth *e
+ if (ret)
+ goto err_disable_pm;
+
++ if (eth->ethsys)
++ regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask,
++ of_dma_is_coherent(eth->dma_dev->of_node) * dma_mask);
++
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
+ ret = device_reset(eth->dev);
+ if (ret) {
+@@ -3079,6 +3086,35 @@ free_netdev:
+ return err;
+ }
+
++void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev)
++{
++ struct net_device *dev, *tmp;
++ LIST_HEAD(dev_list);
++ int i;
++
++ rtnl_lock();
++
++ for (i = 0; i < MTK_MAC_COUNT; i++) {
++ dev = eth->netdev[i];
++
++ if (!dev || !(dev->flags & IFF_UP))
++ continue;
++
++ list_add_tail(&dev->close_list, &dev_list);
++ }
++
++ dev_close_many(&dev_list, false);
++
++ eth->dma_dev = dma_dev;
++
++ list_for_each_entry_safe(dev, tmp, &dev_list, close_list) {
++ list_del_init(&dev->close_list);
++ dev_open(dev, NULL);
++ }
++
++ rtnl_unlock();
++}
++
+ static int mtk_probe(struct platform_device *pdev)
+ {
+ struct device_node *mac_np;
+@@ -3092,6 +3128,7 @@ static int mtk_probe(struct platform_dev
+ eth->soc = of_device_get_match_data(&pdev->dev);
+
+ eth->dev = &pdev->dev;
++ eth->dma_dev = &pdev->dev;
+ eth->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(eth->base))
+ return PTR_ERR(eth->base);
+@@ -3140,6 +3177,16 @@ static int mtk_probe(struct platform_dev
+ }
+ }
+
++ if (of_dma_is_coherent(pdev->dev.of_node)) {
++ struct regmap *cci;
++
++ cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
++ "mediatek,cci-control");
++ /* enable CPU/bus coherency */
++ if (!IS_ERR(cci))
++ regmap_write(cci, 0, 3);
++ }
++
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
+ eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
+ GFP_KERNEL);
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -463,6 +463,12 @@
+ #define RSTCTRL_FE BIT(6)
+ #define RSTCTRL_PPE BIT(31)
+
++/* ethernet dma channel agent map */
++#define ETHSYS_DMA_AG_MAP 0x408
++#define ETHSYS_DMA_AG_MAP_PDMA BIT(0)
++#define ETHSYS_DMA_AG_MAP_QDMA BIT(1)
++#define ETHSYS_DMA_AG_MAP_PPE BIT(2)
++
+ /* SGMII subsystem config registers */
+ /* Register to auto-negotiation restart */
+ #define SGMSYS_PCS_CONTROL_1 0x0
+@@ -880,6 +886,7 @@ struct mtk_sgmii {
+ /* struct mtk_eth - This is the main datasructure for holding the state
+ * of the driver
+ * @dev: The device pointer
++ * @dev: The device pointer used for dma mapping/alloc
+ * @base: The mapped register i/o base
+ * @page_lock: Make sure that register operations are atomic
+ * @tx_irq__lock: Make sure that IRQ register operations are atomic
+@@ -923,6 +930,7 @@ struct mtk_sgmii {
+
+ struct mtk_eth {
+ struct device *dev;
++ struct device *dma_dev;
+ void __iomem *base;
+ spinlock_t page_lock;
+ spinlock_t tx_irq_lock;
+@@ -1021,6 +1029,7 @@ int mtk_gmac_rgmii_path_setup(struct mtk
+ int mtk_eth_offload_init(struct mtk_eth *eth);
+ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data);
++void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
+
+
+ #endif /* MTK_ETH_H */
diff --git a/target/linux/generic/backport-6.1/702-v5.19-01-arm64-dts-mediatek-mt7622-add-support-for-coherent-D.patch b/target/linux/generic/backport-6.1/702-v5.19-01-arm64-dts-mediatek-mt7622-add-support-for-coherent-D.patch
new file mode 100644
index 0000000000..9f2512a1d0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-01-arm64-dts-mediatek-mt7622-add-support-for-coherent-D.patch
@@ -0,0 +1,30 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 7 Feb 2022 10:27:22 +0100
+Subject: [PATCH] arm64: dts: mediatek: mt7622: add support for coherent
+ DMA
+
+It improves performance by eliminating the need for a cache flush on rx and tx
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi
++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
+@@ -357,7 +357,7 @@
+ };
+
+ cci_control2: slave-if@5000 {
+- compatible = "arm,cci-400-ctrl-if";
++ compatible = "arm,cci-400-ctrl-if", "syscon";
+ interface-type = "ace";
+ reg = <0x5000 0x1000>;
+ };
+@@ -938,6 +938,8 @@
+ power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>;
+ mediatek,ethsys = <&ethsys>;
+ mediatek,sgmiisys = <&sgmiisys>;
++ mediatek,cci-control = <&cci_control2>;
++ dma-coherent;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
diff --git a/target/linux/generic/backport-6.1/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch b/target/linux/generic/backport-6.1/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch
new file mode 100644
index 0000000000..2f3a0827fe
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch
@@ -0,0 +1,1679 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 5 Feb 2022 17:56:08 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for Wireless
+ Ethernet Dispatch (WED)
+
+The Wireless Ethernet Dispatch subsystem on the MT7622 SoC can be
+configured to intercept and handle access to the DMA queues and
+PCIe interrupts for a MT7615/MT7915 wireless card.
+It can manage the internal WDMA (Wireless DMA) controller, which allows
+ethernet packets to be passed from the packet switch engine (PSE) to the
+wireless card, bypassing the CPU entirely.
+This can be used to implement hardware flow offloading from ethernet to
+WLAN.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_wed.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_wed.h
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_ops.c
+ create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_regs.h
+ create mode 100644 include/linux/soc/mediatek/mtk_wed.h
+
+--- a/drivers/net/ethernet/mediatek/Kconfig
++++ b/drivers/net/ethernet/mediatek/Kconfig
+@@ -7,6 +7,10 @@ config NET_VENDOR_MEDIATEK
+
+ if NET_VENDOR_MEDIATEK
+
++config NET_MEDIATEK_SOC_WED
++ depends on ARCH_MEDIATEK || COMPILE_TEST
++ def_bool NET_MEDIATEK_SOC != n
++
+ config NET_MEDIATEK_SOC
+ tristate "MediaTek SoC Gigabit Ethernet support"
+ depends on NET_DSA || !NET_DSA
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -5,4 +5,9 @@
+
+ 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
++ifdef CONFIG_DEBUG_FS
++mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
++endif
++obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o
+ obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -24,6 +24,7 @@
+ #include <net/dsa.h>
+
+ #include "mtk_eth_soc.h"
++#include "mtk_wed.h"
+
+ static int mtk_msg_level = -1;
+ module_param_named(msg_level, mtk_msg_level, int, 0);
+@@ -3209,6 +3210,22 @@ static int mtk_probe(struct platform_dev
+ }
+ }
+
++ for (i = 0;; i++) {
++ struct device_node *np = of_parse_phandle(pdev->dev.of_node,
++ "mediatek,wed", i);
++ static const u32 wdma_regs[] = {
++ MTK_WDMA0_BASE,
++ MTK_WDMA1_BASE
++ };
++ void __iomem *wdma;
++
++ if (!np || i >= ARRAY_SIZE(wdma_regs))
++ break;
++
++ wdma = eth->base + wdma_regs[i];
++ mtk_wed_add_hw(np, eth, wdma, i);
++ }
++
+ for (i = 0; i < 3; i++) {
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0)
+ eth->irq[i] = eth->irq[0];
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -295,6 +295,9 @@
+ #define MTK_GDM1_TX_GPCNT 0x2438
+ #define MTK_STAT_OFFSET 0x40
+
++#define MTK_WDMA0_BASE 0x2800
++#define MTK_WDMA1_BASE 0x2c00
++
+ /* QDMA descriptor txd4 */
+ #define TX_DMA_CHKSUM (0x7 << 29)
+ #define TX_DMA_TSO BIT(28)
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -0,0 +1,875 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/bitfield.h>
++#include <linux/dma-mapping.h>
++#include <linux/skbuff.h>
++#include <linux/of_platform.h>
++#include <linux/of_address.h>
++#include <linux/mfd/syscon.h>
++#include <linux/debugfs.h>
++#include <linux/soc/mediatek/mtk_wed.h>
++#include "mtk_eth_soc.h"
++#include "mtk_wed_regs.h"
++#include "mtk_wed.h"
++#include "mtk_ppe.h"
++
++#define MTK_PCIE_BASE(n) (0x1a143000 + (n) * 0x2000)
++
++#define MTK_WED_PKT_SIZE 1900
++#define MTK_WED_BUF_SIZE 2048
++#define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048)
++
++#define MTK_WED_TX_RING_SIZE 2048
++#define MTK_WED_WDMA_RING_SIZE 1024
++
++static struct mtk_wed_hw *hw_list[2];
++static DEFINE_MUTEX(hw_lock);
++
++static void
++wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val)
++{
++ regmap_update_bits(dev->hw->regs, reg, mask | val, val);
++}
++
++static void
++wed_set(struct mtk_wed_device *dev, u32 reg, u32 mask)
++{
++ return wed_m32(dev, reg, 0, mask);
++}
++
++static void
++wed_clr(struct mtk_wed_device *dev, u32 reg, u32 mask)
++{
++ return wed_m32(dev, reg, mask, 0);
++}
++
++static void
++wdma_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val)
++{
++ wdma_w32(dev, reg, (wdma_r32(dev, reg) & ~mask) | val);
++}
++
++static void
++wdma_set(struct mtk_wed_device *dev, u32 reg, u32 mask)
++{
++ wdma_m32(dev, reg, 0, mask);
++}
++
++static u32
++mtk_wed_read_reset(struct mtk_wed_device *dev)
++{
++ return wed_r32(dev, MTK_WED_RESET);
++}
++
++static void
++mtk_wed_reset(struct mtk_wed_device *dev, u32 mask)
++{
++ u32 status;
++
++ wed_w32(dev, MTK_WED_RESET, mask);
++ if (readx_poll_timeout(mtk_wed_read_reset, dev, status,
++ !(status & mask), 0, 1000))
++ WARN_ON_ONCE(1);
++}
++
++static struct mtk_wed_hw *
++mtk_wed_assign(struct mtk_wed_device *dev)
++{
++ struct mtk_wed_hw *hw;
++
++ hw = hw_list[pci_domain_nr(dev->wlan.pci_dev->bus)];
++ if (!hw || hw->wed_dev)
++ return NULL;
++
++ hw->wed_dev = dev;
++ return hw;
++}
++
++static int
++mtk_wed_buffer_alloc(struct mtk_wed_device *dev)
++{
++ struct mtk_wdma_desc *desc;
++ dma_addr_t desc_phys;
++ void **page_list;
++ int token = dev->wlan.token_start;
++ int ring_size;
++ int n_pages;
++ int i, page_idx;
++
++ ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
++ n_pages = ring_size / MTK_WED_BUF_PER_PAGE;
++
++ page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL);
++ if (!page_list)
++ return -ENOMEM;
++
++ dev->buf_ring.size = ring_size;
++ dev->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;
++
++ for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) {
++ dma_addr_t page_phys, buf_phys;
++ struct page *page;
++ void *buf;
++ int s;
++
++ page = __dev_alloc_pages(GFP_KERNEL, 0);
++ if (!page)
++ return -ENOMEM;
++
++ page_phys = dma_map_page(dev->hw->dev, page, 0, PAGE_SIZE,
++ DMA_BIDIRECTIONAL);
++ if (dma_mapping_error(dev->hw->dev, page_phys)) {
++ __free_page(page);
++ return -ENOMEM;
++ }
++
++ page_list[page_idx++] = page;
++ dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE,
++ DMA_BIDIRECTIONAL);
++
++ buf = page_to_virt(page);
++ buf_phys = page_phys;
++
++ for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) {
++ u32 txd_size;
++
++ txd_size = dev->wlan.init_buf(buf, buf_phys, token++);
++
++ desc->buf0 = buf_phys;
++ desc->buf1 = buf_phys + txd_size;
++ desc->ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0,
++ txd_size) |
++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
++ MTK_WED_BUF_SIZE - txd_size) |
++ MTK_WDMA_DESC_CTRL_LAST_SEG1;
++ desc->info = 0;
++ desc++;
++
++ buf += MTK_WED_BUF_SIZE;
++ buf_phys += MTK_WED_BUF_SIZE;
++ }
++
++ dma_sync_single_for_device(dev->hw->dev, page_phys, PAGE_SIZE,
++ DMA_BIDIRECTIONAL);
++ }
++
++ return 0;
++}
++
++static void
++mtk_wed_free_buffer(struct mtk_wed_device *dev)
++{
++ struct mtk_wdma_desc *desc = dev->buf_ring.desc;
++ void **page_list = dev->buf_ring.pages;
++ int page_idx;
++ int i;
++
++ if (!page_list)
++ return;
++
++ if (!desc)
++ goto free_pagelist;
++
++ for (i = 0, page_idx = 0; i < dev->buf_ring.size; i += MTK_WED_BUF_PER_PAGE) {
++ void *page = page_list[page_idx++];
++
++ if (!page)
++ break;
++
++ dma_unmap_page(dev->hw->dev, desc[i].buf0,
++ PAGE_SIZE, DMA_BIDIRECTIONAL);
++ __free_page(page);
++ }
++
++ dma_free_coherent(dev->hw->dev, dev->buf_ring.size * sizeof(*desc),
++ desc, dev->buf_ring.desc_phys);
++
++free_pagelist:
++ kfree(page_list);
++}
++
++static void
++mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring)
++{
++ if (!ring->desc)
++ return;
++
++ dma_free_coherent(dev->hw->dev, ring->size * sizeof(*ring->desc),
++ ring->desc, ring->desc_phys);
++}
++
++static void
++mtk_wed_free_tx_rings(struct mtk_wed_device *dev)
++{
++ int i;
++
++ 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]);
++}
++
++static void
++mtk_wed_set_ext_int(struct mtk_wed_device *dev, bool en)
++{
++ u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
++
++ if (!dev->hw->num_flows)
++ mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
++
++ wed_w32(dev, MTK_WED_EXT_INT_MASK, en ? mask : 0);
++ wed_r32(dev, MTK_WED_EXT_INT_MASK);
++}
++
++static void
++mtk_wed_stop(struct mtk_wed_device *dev)
++{
++ regmap_write(dev->hw->mirror, dev->hw->index * 4, 0);
++ mtk_wed_set_ext_int(dev, false);
++
++ wed_clr(dev, MTK_WED_CTRL,
++ MTK_WED_CTRL_WDMA_INT_AGENT_EN |
++ MTK_WED_CTRL_WPDMA_INT_AGENT_EN |
++ MTK_WED_CTRL_WED_TX_BM_EN |
++ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
++ wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, 0);
++ wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, 0);
++ 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);
++
++ wed_clr(dev, MTK_WED_GLO_CFG,
++ MTK_WED_GLO_CFG_TX_DMA_EN |
++ MTK_WED_GLO_CFG_RX_DMA_EN);
++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
++ wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
++ MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
++}
++
++static void
++mtk_wed_detach(struct mtk_wed_device *dev)
++{
++ struct device_node *wlan_node = dev->wlan.pci_dev->dev.of_node;
++ struct mtk_wed_hw *hw = dev->hw;
++
++ mutex_lock(&hw_lock);
++
++ mtk_wed_stop(dev);
++
++ wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
++ wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
++
++ mtk_wed_reset(dev, MTK_WED_RESET_WED);
++
++ mtk_wed_free_buffer(dev);
++ mtk_wed_free_tx_rings(dev);
++
++ if (of_dma_is_coherent(wlan_node))
++ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
++ BIT(hw->index), BIT(hw->index));
++
++ if (!hw_list[!hw->index]->wed_dev &&
++ hw->eth->dma_dev != hw->eth->dev)
++ mtk_eth_set_dma_device(hw->eth, hw->eth->dev);
++
++ memset(dev, 0, sizeof(*dev));
++ module_put(THIS_MODULE);
++
++ hw->wed_dev = NULL;
++ mutex_unlock(&hw_lock);
++}
++
++static void
++mtk_wed_hw_init_early(struct mtk_wed_device *dev)
++{
++ u32 mask, set;
++ u32 offset;
++
++ mtk_wed_stop(dev);
++ mtk_wed_reset(dev, MTK_WED_RESET_WED);
++
++ mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE |
++ MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE |
++ MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE;
++ set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2) |
++ MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP |
++ MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
++ wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set);
++
++ wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_INFO_PRERES);
++
++ offset = dev->hw->index ? 0x04000400 : 0;
++ wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset);
++ wed_w32(dev, MTK_WED_WDMA_OFFSET1, 0x29002800 + offset);
++
++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, MTK_PCIE_BASE(dev->hw->index));
++ wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys);
++}
++
++static void
++mtk_wed_hw_init(struct mtk_wed_device *dev)
++{
++ if (dev->init_done)
++ return;
++
++ dev->init_done = true;
++ mtk_wed_set_ext_int(dev, false);
++ 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) |
++ 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_TKID,
++ FIELD_PREP(MTK_WED_TX_BM_TKID_START,
++ dev->wlan.token_start) |
++ FIELD_PREP(MTK_WED_TX_BM_TKID_END,
++ dev->wlan.token_start + dev->wlan.nbuf - 1));
++
++ wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
++
++ wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
++ FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) |
++ MTK_WED_TX_BM_DYN_THR_HI);
++
++ mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
++
++ wed_set(dev, MTK_WED_CTRL,
++ MTK_WED_CTRL_WED_TX_BM_EN |
++ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
++
++ wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE);
++}
++
++static void
++mtk_wed_ring_reset(struct mtk_wdma_desc *desc, int size)
++{
++ int i;
++
++ for (i = 0; i < size; i++) {
++ desc[i].buf0 = 0;
++ desc[i].ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
++ desc[i].buf1 = 0;
++ desc[i].info = 0;
++ }
++}
++
++static u32
++mtk_wed_check_busy(struct mtk_wed_device *dev)
++{
++ if (wed_r32(dev, MTK_WED_GLO_CFG) & MTK_WED_GLO_CFG_TX_DMA_BUSY)
++ return true;
++
++ if (wed_r32(dev, MTK_WED_WPDMA_GLO_CFG) &
++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY)
++ return true;
++
++ if (wed_r32(dev, MTK_WED_CTRL) & MTK_WED_CTRL_WDMA_INT_AGENT_BUSY)
++ return true;
++
++ if (wed_r32(dev, MTK_WED_WDMA_GLO_CFG) &
++ MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY)
++ return true;
++
++ if (wdma_r32(dev, MTK_WDMA_GLO_CFG) &
++ MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY)
++ return true;
++
++ if (wed_r32(dev, MTK_WED_CTRL) &
++ (MTK_WED_CTRL_WED_TX_BM_BUSY | MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY))
++ return true;
++
++ return false;
++}
++
++static int
++mtk_wed_poll_busy(struct mtk_wed_device *dev)
++{
++ int sleep = 15000;
++ int timeout = 100 * sleep;
++ u32 val;
++
++ return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
++ timeout, false, dev);
++}
++
++static void
++mtk_wed_reset_dma(struct mtk_wed_device *dev)
++{
++ bool busy = false;
++ u32 val;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++) {
++ struct mtk_wdma_desc *desc = dev->tx_ring[i].desc;
++
++ if (!desc)
++ continue;
++
++ mtk_wed_ring_reset(desc, MTK_WED_TX_RING_SIZE);
++ }
++
++ if (mtk_wed_poll_busy(dev))
++ busy = mtk_wed_check_busy(dev);
++
++ if (busy) {
++ mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA);
++ } else {
++ wed_w32(dev, MTK_WED_RESET_IDX,
++ MTK_WED_RESET_IDX_TX |
++ MTK_WED_RESET_IDX_RX);
++ wed_w32(dev, MTK_WED_RESET_IDX, 0);
++ }
++
++ wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
++ wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
++
++ if (busy) {
++ mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
++ mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV);
++ } else {
++ wed_w32(dev, MTK_WED_WDMA_RESET_IDX,
++ MTK_WED_WDMA_RESET_IDX_RX | MTK_WED_WDMA_RESET_IDX_DRV);
++ wed_w32(dev, MTK_WED_WDMA_RESET_IDX, 0);
++
++ wed_set(dev, MTK_WED_WDMA_GLO_CFG,
++ MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE);
++
++ wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
++ MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE);
++ }
++
++ for (i = 0; i < 100; i++) {
++ val = wed_r32(dev, MTK_WED_TX_BM_INTF);
++ if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40)
++ break;
++ }
++
++ mtk_wed_reset(dev, MTK_WED_RESET_TX_FREE_AGENT);
++ mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
++
++ if (busy) {
++ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
++ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV);
++ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_DRV);
++ } else {
++ wed_w32(dev, MTK_WED_WPDMA_RESET_IDX,
++ MTK_WED_WPDMA_RESET_IDX_TX |
++ 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)
++{
++ ring->desc = dma_alloc_coherent(dev->hw->dev,
++ size * sizeof(*ring->desc),
++ &ring->desc_phys, GFP_KERNEL);
++ if (!ring->desc)
++ return -ENOMEM;
++
++ ring->size = size;
++ mtk_wed_ring_reset(ring->desc, size);
++
++ return 0;
++}
++
++static int
++mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size)
++{
++ struct mtk_wed_ring *wdma = &dev->tx_wdma[idx];
++
++ if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE))
++ return -ENOMEM;
++
++ wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
++ wdma->desc_phys);
++ wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_COUNT,
++ size);
++ wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0);
++
++ wed_w32(dev, MTK_WED_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
++ wdma->desc_phys);
++ wed_w32(dev, MTK_WED_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_COUNT,
++ size);
++
++ return 0;
++}
++
++static void
++mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
++{
++ u32 wdma_mask;
++ u32 val;
++ 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);
++
++ wdma_mask = FIELD_PREP(MTK_WDMA_INT_MASK_RX_DONE, GENMASK(1, 0));
++
++ mtk_wed_hw_init(dev);
++
++ wed_set(dev, MTK_WED_CTRL,
++ MTK_WED_CTRL_WDMA_INT_AGENT_EN |
++ MTK_WED_CTRL_WPDMA_INT_AGENT_EN |
++ MTK_WED_CTRL_WED_TX_BM_EN |
++ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
++
++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, MTK_WED_PCIE_INT_TRIGGER_STATUS);
++
++ wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER,
++ MTK_WED_WPDMA_INT_TRIGGER_RX_DONE |
++ MTK_WED_WPDMA_INT_TRIGGER_TX_DONE);
++
++ wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
++ MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
++
++ wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, wdma_mask);
++ wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
++
++ wdma_w32(dev, MTK_WDMA_INT_MASK, wdma_mask);
++ wdma_w32(dev, MTK_WDMA_INT_GRP2, wdma_mask);
++
++ wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask);
++ wed_w32(dev, MTK_WED_INT_MASK, irq_mask);
++
++ wed_set(dev, MTK_WED_GLO_CFG,
++ MTK_WED_GLO_CFG_TX_DMA_EN |
++ MTK_WED_GLO_CFG_RX_DMA_EN);
++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
++ wed_set(dev, MTK_WED_WDMA_GLO_CFG,
++ MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
++
++ mtk_wed_set_ext_int(dev, true);
++ val = dev->wlan.wpdma_phys |
++ MTK_PCIE_MIRROR_MAP_EN |
++ FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, dev->hw->index);
++
++ if (dev->hw->index)
++ val |= BIT(1);
++ val |= BIT(0);
++ regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
++
++ dev->running = true;
++}
++
++static int
++mtk_wed_attach(struct mtk_wed_device *dev)
++ __releases(RCU)
++{
++ struct mtk_wed_hw *hw;
++ int ret = 0;
++
++ RCU_LOCKDEP_WARN(!rcu_read_lock_held(),
++ "mtk_wed_attach without holding the RCU read lock");
++
++ if (pci_domain_nr(dev->wlan.pci_dev->bus) > 1 ||
++ !try_module_get(THIS_MODULE))
++ ret = -ENODEV;
++
++ rcu_read_unlock();
++
++ if (ret)
++ return ret;
++
++ mutex_lock(&hw_lock);
++
++ hw = mtk_wed_assign(dev);
++ if (!hw) {
++ module_put(THIS_MODULE);
++ ret = -ENODEV;
++ goto out;
++ }
++
++ dev_info(&dev->wlan.pci_dev->dev, "attaching wed device %d\n", hw->index);
++
++ dev->hw = hw;
++ dev->dev = hw->dev;
++ dev->irq = hw->irq;
++ dev->wdma_idx = hw->index;
++
++ 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);
++ goto out;
++ }
++
++ mtk_wed_hw_init_early(dev);
++ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, BIT(hw->index), 0);
++
++out:
++ mutex_unlock(&hw_lock);
++
++ return ret;
++}
++
++static int
++mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
++{
++ struct mtk_wed_ring *ring = &dev->tx_ring[idx];
++
++ /*
++ * Tx ring redirection:
++ * Instead of configuring the WLAN PDMA TX ring directly, the WLAN
++ * driver allocated DMA ring gets configured into WED MTK_WED_RING_TX(n)
++ * registers.
++ *
++ * WED driver posts its own DMA ring as WLAN PDMA TX and configures it
++ * into MTK_WED_WPDMA_RING_TX(n) registers.
++ * It gets filled with packets picked up from WED TX ring and from
++ * WDMA RX.
++ */
++
++ BUG_ON(idx > ARRAY_SIZE(dev->tx_ring));
++
++ if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE))
++ return -ENOMEM;
++
++ if (mtk_wed_wdma_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
++ return -ENOMEM;
++
++ ring->reg_base = MTK_WED_RING_TX(idx);
++ ring->wpdma = regs;
++
++ /* WED -> WPDMA */
++ wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys);
++ wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_COUNT, MTK_WED_TX_RING_SIZE);
++ wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_CPU_IDX, 0);
++
++ wed_w32(dev, MTK_WED_WPDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
++ ring->desc_phys);
++ wed_w32(dev, MTK_WED_WPDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT,
++ MTK_WED_TX_RING_SIZE);
++ wed_w32(dev, MTK_WED_WPDMA_RING_TX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0);
++
++ return 0;
++}
++
++static int
++mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
++{
++ struct mtk_wed_ring *ring = &dev->txfree_ring;
++ int i;
++
++ /*
++ * For txfree event handling, the same DMA ring is shared between WED
++ * and WLAN. The WLAN driver accesses the ring index registers through
++ * WED
++ */
++ ring->reg_base = MTK_WED_RING_RX(1);
++ ring->wpdma = regs;
++
++ for (i = 0; i < 12; i += 4) {
++ u32 val = readl(regs + i);
++
++ wed_w32(dev, MTK_WED_RING_RX(1) + i, val);
++ wed_w32(dev, MTK_WED_WPDMA_RING_RX(1) + i, val);
++ }
++
++ return 0;
++}
++
++static u32
++mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
++{
++ u32 val;
++
++ val = wed_r32(dev, MTK_WED_EXT_INT_STATUS);
++ wed_w32(dev, MTK_WED_EXT_INT_STATUS, val);
++ val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK;
++ if (!dev->hw->num_flows)
++ val &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
++ if (val && net_ratelimit())
++ pr_err("mtk_wed%d: error status=%08x\n", dev->hw->index, val);
++
++ val = wed_r32(dev, MTK_WED_INT_STATUS);
++ val &= mask;
++ wed_w32(dev, MTK_WED_INT_STATUS, val); /* ACK */
++
++ return val;
++}
++
++static void
++mtk_wed_irq_set_mask(struct mtk_wed_device *dev, u32 mask)
++{
++ if (!dev->running)
++ return;
++
++ mtk_wed_set_ext_int(dev, !!mask);
++ wed_w32(dev, MTK_WED_INT_MASK, mask);
++}
++
++int mtk_wed_flow_add(int index)
++{
++ struct mtk_wed_hw *hw = hw_list[index];
++ int ret;
++
++ if (!hw || !hw->wed_dev)
++ return -ENODEV;
++
++ if (hw->num_flows) {
++ hw->num_flows++;
++ return 0;
++ }
++
++ mutex_lock(&hw_lock);
++ if (!hw->wed_dev) {
++ ret = -ENODEV;
++ goto out;
++ }
++
++ ret = hw->wed_dev->wlan.offload_enable(hw->wed_dev);
++ if (!ret)
++ hw->num_flows++;
++ mtk_wed_set_ext_int(hw->wed_dev, true);
++
++out:
++ mutex_unlock(&hw_lock);
++
++ return ret;
++}
++
++void mtk_wed_flow_remove(int index)
++{
++ struct mtk_wed_hw *hw = hw_list[index];
++
++ if (!hw)
++ return;
++
++ if (--hw->num_flows)
++ return;
++
++ mutex_lock(&hw_lock);
++ if (!hw->wed_dev)
++ goto out;
++
++ hw->wed_dev->wlan.offload_disable(hw->wed_dev);
++ mtk_wed_set_ext_int(hw->wed_dev, true);
++
++out:
++ mutex_unlock(&hw_lock);
++}
++
++void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
++ void __iomem *wdma, int index)
++{
++ static const struct mtk_wed_ops wed_ops = {
++ .attach = mtk_wed_attach,
++ .tx_ring_setup = mtk_wed_tx_ring_setup,
++ .txfree_ring_setup = mtk_wed_txfree_ring_setup,
++ .start = mtk_wed_start,
++ .stop = mtk_wed_stop,
++ .reset_dma = mtk_wed_reset_dma,
++ .reg_read = wed_r32,
++ .reg_write = wed_w32,
++ .irq_get = mtk_wed_irq_get,
++ .irq_set_mask = mtk_wed_irq_set_mask,
++ .detach = mtk_wed_detach,
++ };
++ struct device_node *eth_np = eth->dev->of_node;
++ struct platform_device *pdev;
++ struct mtk_wed_hw *hw;
++ struct regmap *regs;
++ int irq;
++
++ if (!np)
++ return;
++
++ pdev = of_find_device_by_node(np);
++ if (!pdev)
++ return;
++
++ get_device(&pdev->dev);
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return;
++
++ regs = syscon_regmap_lookup_by_phandle(np, NULL);
++ if (!regs)
++ return;
++
++ rcu_assign_pointer(mtk_soc_wed_ops, &wed_ops);
++
++ mutex_lock(&hw_lock);
++
++ if (WARN_ON(hw_list[index]))
++ goto unlock;
++
++ hw = kzalloc(sizeof(*hw), GFP_KERNEL);
++ hw->node = np;
++ hw->regs = regs;
++ hw->eth = eth;
++ hw->dev = &pdev->dev;
++ hw->wdma = wdma;
++ hw->index = index;
++ hw->irq = irq;
++ hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
++ "mediatek,pcie-mirror");
++ hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np,
++ "mediatek,hifsys");
++ if (IS_ERR(hw->mirror) || IS_ERR(hw->hifsys)) {
++ kfree(hw);
++ goto unlock;
++ }
++
++ if (!index) {
++ regmap_write(hw->mirror, 0, 0);
++ regmap_write(hw->mirror, 4, 0);
++ }
++ mtk_wed_hw_add_debugfs(hw);
++
++ hw_list[index] = hw;
++
++unlock:
++ mutex_unlock(&hw_lock);
++}
++
++void mtk_wed_exit(void)
++{
++ int i;
++
++ rcu_assign_pointer(mtk_soc_wed_ops, NULL);
++
++ synchronize_rcu();
++
++ for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
++ struct mtk_wed_hw *hw;
++
++ hw = hw_list[i];
++ if (!hw)
++ continue;
++
++ hw_list[i] = NULL;
++ debugfs_remove(hw->debugfs_dir);
++ put_device(hw->dev);
++ kfree(hw);
++ }
++}
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
+@@ -0,0 +1,128 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name> */
++
++#ifndef __MTK_WED_PRIV_H
++#define __MTK_WED_PRIV_H
++
++#include <linux/soc/mediatek/mtk_wed.h>
++#include <linux/debugfs.h>
++#include <linux/regmap.h>
++
++struct mtk_eth;
++
++struct mtk_wed_hw {
++ struct device_node *node;
++ struct mtk_eth *eth;
++ struct regmap *regs;
++ struct regmap *hifsys;
++ struct device *dev;
++ void __iomem *wdma;
++ struct regmap *mirror;
++ struct dentry *debugfs_dir;
++ struct mtk_wed_device *wed_dev;
++ u32 debugfs_reg;
++ u32 num_flows;
++ char dirname[5];
++ int irq;
++ int index;
++};
++
++
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++static inline void
++wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val)
++{
++ regmap_write(dev->hw->regs, reg, val);
++}
++
++static inline u32
++wed_r32(struct mtk_wed_device *dev, u32 reg)
++{
++ unsigned int val;
++
++ regmap_read(dev->hw->regs, reg, &val);
++
++ return val;
++}
++
++static inline void
++wdma_w32(struct mtk_wed_device *dev, u32 reg, u32 val)
++{
++ writel(val, dev->hw->wdma + reg);
++}
++
++static inline u32
++wdma_r32(struct mtk_wed_device *dev, u32 reg)
++{
++ return readl(dev->hw->wdma + reg);
++}
++
++static inline u32
++wpdma_tx_r32(struct mtk_wed_device *dev, int ring, u32 reg)
++{
++ if (!dev->tx_ring[ring].wpdma)
++ return 0;
++
++ return readl(dev->tx_ring[ring].wpdma + reg);
++}
++
++static inline void
++wpdma_tx_w32(struct mtk_wed_device *dev, int ring, u32 reg, u32 val)
++{
++ if (!dev->tx_ring[ring].wpdma)
++ return;
++
++ writel(val, dev->tx_ring[ring].wpdma + reg);
++}
++
++static inline u32
++wpdma_txfree_r32(struct mtk_wed_device *dev, u32 reg)
++{
++ if (!dev->txfree_ring.wpdma)
++ return 0;
++
++ return readl(dev->txfree_ring.wpdma + reg);
++}
++
++static inline void
++wpdma_txfree_w32(struct mtk_wed_device *dev, u32 reg, u32 val)
++{
++ if (!dev->txfree_ring.wpdma)
++ return;
++
++ writel(val, dev->txfree_ring.wpdma + reg);
++}
++
++void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
++ void __iomem *wdma, int index);
++void mtk_wed_exit(void);
++int mtk_wed_flow_add(int index);
++void mtk_wed_flow_remove(int index);
++#else
++static inline void
++mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
++ void __iomem *wdma, int index)
++{
++}
++static inline void
++mtk_wed_exit(void)
++{
++}
++static inline int mtk_wed_flow_add(int index)
++{
++ return -EINVAL;
++}
++static inline void mtk_wed_flow_remove(int index)
++{
++}
++#endif
++
++#ifdef CONFIG_DEBUG_FS
++void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw);
++#else
++static inline void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw)
++{
++}
++#endif
++
++#endif
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
+@@ -0,0 +1,175 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/seq_file.h>
++#include "mtk_wed.h"
++#include "mtk_wed_regs.h"
++
++struct reg_dump {
++ const char *name;
++ u16 offset;
++ u8 type;
++ u8 base;
++};
++
++enum {
++ DUMP_TYPE_STRING,
++ DUMP_TYPE_WED,
++ DUMP_TYPE_WDMA,
++ DUMP_TYPE_WPDMA_TX,
++ DUMP_TYPE_WPDMA_TXFREE,
++};
++
++#define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING }
++#define DUMP_REG(_reg, ...) { #_reg, MTK_##_reg, __VA_ARGS__ }
++#define DUMP_RING(_prefix, _base, ...) \
++ { _prefix " BASE", _base, __VA_ARGS__ }, \
++ { _prefix " CNT", _base + 0x4, __VA_ARGS__ }, \
++ { _prefix " CIDX", _base + 0x8, __VA_ARGS__ }, \
++ { _prefix " DIDX", _base + 0xc, __VA_ARGS__ }
++
++#define DUMP_WED(_reg) DUMP_REG(_reg, DUMP_TYPE_WED)
++#define DUMP_WED_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WED)
++
++#define DUMP_WDMA(_reg) DUMP_REG(_reg, DUMP_TYPE_WDMA)
++#define DUMP_WDMA_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WDMA)
++
++#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)
++
++static void
++print_reg_val(struct seq_file *s, const char *name, u32 val)
++{
++ seq_printf(s, "%-32s %08x\n", name, val);
++}
++
++static void
++dump_wed_regs(struct seq_file *s, struct mtk_wed_device *dev,
++ const struct reg_dump *regs, int n_regs)
++{
++ const struct reg_dump *cur;
++ u32 val;
++
++ for (cur = regs; cur < &regs[n_regs]; cur++) {
++ switch (cur->type) {
++ case DUMP_TYPE_STRING:
++ seq_printf(s, "%s======== %s:\n",
++ cur > regs ? "\n" : "",
++ cur->name);
++ continue;
++ case DUMP_TYPE_WED:
++ val = wed_r32(dev, cur->offset);
++ break;
++ case DUMP_TYPE_WDMA:
++ val = wdma_r32(dev, cur->offset);
++ break;
++ case DUMP_TYPE_WPDMA_TX:
++ val = wpdma_tx_r32(dev, cur->base, cur->offset);
++ break;
++ case DUMP_TYPE_WPDMA_TXFREE:
++ val = wpdma_txfree_r32(dev, cur->offset);
++ break;
++ }
++ print_reg_val(s, cur->name, val);
++ }
++}
++
++
++static int
++wed_txinfo_show(struct seq_file *s, void *data)
++{
++ static const struct reg_dump regs[] = {
++ DUMP_STR("WED TX"),
++ DUMP_WED(WED_TX_MIB(0)),
++ DUMP_WED_RING(WED_RING_TX(0)),
++
++ DUMP_WED(WED_TX_MIB(1)),
++ DUMP_WED_RING(WED_RING_TX(1)),
++
++ DUMP_STR("WPDMA TX"),
++ DUMP_WED(WED_WPDMA_TX_MIB(0)),
++ DUMP_WED_RING(WED_WPDMA_RING_TX(0)),
++ DUMP_WED(WED_WPDMA_TX_COHERENT_MIB(0)),
++
++ DUMP_WED(WED_WPDMA_TX_MIB(1)),
++ DUMP_WED_RING(WED_WPDMA_RING_TX(1)),
++ DUMP_WED(WED_WPDMA_TX_COHERENT_MIB(1)),
++
++ DUMP_STR("WPDMA TX"),
++ DUMP_WPDMA_TX_RING(0),
++ DUMP_WPDMA_TX_RING(1),
++
++ DUMP_STR("WED WDMA RX"),
++ DUMP_WED(WED_WDMA_RX_MIB(0)),
++ DUMP_WED_RING(WED_WDMA_RING_RX(0)),
++ DUMP_WED(WED_WDMA_RX_THRES(0)),
++ DUMP_WED(WED_WDMA_RX_RECYCLE_MIB(0)),
++ DUMP_WED(WED_WDMA_RX_PROCESSED_MIB(0)),
++
++ DUMP_WED(WED_WDMA_RX_MIB(1)),
++ DUMP_WED_RING(WED_WDMA_RING_RX(1)),
++ DUMP_WED(WED_WDMA_RX_THRES(1)),
++ DUMP_WED(WED_WDMA_RX_RECYCLE_MIB(1)),
++ DUMP_WED(WED_WDMA_RX_PROCESSED_MIB(1)),
++
++ DUMP_STR("WDMA RX"),
++ DUMP_WDMA(WDMA_GLO_CFG),
++ DUMP_WDMA_RING(WDMA_RING_RX(0)),
++ DUMP_WDMA_RING(WDMA_RING_RX(1)),
++ };
++ 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_txinfo);
++
++
++static int
++mtk_wed_reg_set(void *data, u64 val)
++{
++ struct mtk_wed_hw *hw = data;
++
++ regmap_write(hw->regs, hw->debugfs_reg, val);
++
++ return 0;
++}
++
++static int
++mtk_wed_reg_get(void *data, u64 *val)
++{
++ struct mtk_wed_hw *hw = data;
++ unsigned int regval;
++ int ret;
++
++ ret = regmap_read(hw->regs, hw->debugfs_reg, &regval);
++ if (ret)
++ return ret;
++
++ *val = regval;
++
++ return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mtk_wed_reg_get, mtk_wed_reg_set,
++ "0x%08llx\n");
++
++void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw)
++{
++ struct dentry *dir;
++
++ snprintf(hw->dirname, sizeof(hw->dirname), "wed%d", hw->index);
++ dir = debugfs_create_dir(hw->dirname, NULL);
++ if (!dir)
++ return;
++
++ hw->debugfs_dir = dir;
++ 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);
++}
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_wed_ops.c
+@@ -0,0 +1,8 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#include <linux/kernel.h>
++#include <linux/soc/mediatek/mtk_wed.h>
++
++const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
++EXPORT_SYMBOL_GPL(mtk_soc_wed_ops);
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -0,0 +1,251 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
++
++#ifndef __MTK_WED_REGS_H
++#define __MTK_WED_REGS_H
++
++#define MTK_WDMA_DESC_CTRL_LEN1 GENMASK(14, 0)
++#define MTK_WDMA_DESC_CTRL_LAST_SEG1 BIT(15)
++#define MTK_WDMA_DESC_CTRL_BURST BIT(16)
++#define MTK_WDMA_DESC_CTRL_LEN0 GENMASK(29, 16)
++#define MTK_WDMA_DESC_CTRL_LAST_SEG0 BIT(30)
++#define MTK_WDMA_DESC_CTRL_DMA_DONE BIT(31)
++
++struct mtk_wdma_desc {
++ __le32 buf0;
++ __le32 ctrl;
++ __le32 buf1;
++ __le32 info;
++} __packed __aligned(4);
++
++#define MTK_WED_RESET 0x008
++#define MTK_WED_RESET_TX_BM BIT(0)
++#define MTK_WED_RESET_TX_FREE_AGENT BIT(4)
++#define MTK_WED_RESET_WPDMA_TX_DRV BIT(8)
++#define MTK_WED_RESET_WPDMA_RX_DRV BIT(9)
++#define MTK_WED_RESET_WPDMA_INT_AGENT BIT(11)
++#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_WED BIT(31)
++
++#define MTK_WED_CTRL 0x00c
++#define MTK_WED_CTRL_WPDMA_INT_AGENT_EN BIT(0)
++#define MTK_WED_CTRL_WPDMA_INT_AGENT_BUSY BIT(1)
++#define MTK_WED_CTRL_WDMA_INT_AGENT_EN BIT(2)
++#define MTK_WED_CTRL_WDMA_INT_AGENT_BUSY BIT(3)
++#define MTK_WED_CTRL_WED_TX_BM_EN BIT(8)
++#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_FINAL_DIDX_READ BIT(24)
++#define MTK_WED_CTRL_MIB_READ_CLEAR BIT(28)
++
++#define MTK_WED_EXT_INT_STATUS 0x020
++#define MTK_WED_EXT_INT_STATUS_TF_LEN_ERR BIT(0)
++#define MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD BIT(1)
++#define MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID BIT(4)
++#define MTK_WED_EXT_INT_STATUS_TX_FBUF_LO_TH BIT(8)
++#define MTK_WED_EXT_INT_STATUS_TX_FBUF_HI_TH BIT(9)
++#define MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH BIT(12)
++#define MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH BIT(13)
++#define MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR BIT(16)
++#define MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR BIT(17)
++#define MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT BIT(18)
++#define MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN BIT(19)
++#define MTK_WED_EXT_INT_STATUS_RX_DRV_BM_DMAD_COHERENT BIT(20)
++#define MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR BIT(21)
++#define MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR BIT(22)
++#define MTK_WED_EXT_INT_STATUS_RX_DRV_DMA_RECYCLE BIT(24)
++#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 | \
++ MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR | \
++ MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR | \
++ MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN | \
++ MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR | \
++ MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR)
++
++#define MTK_WED_EXT_INT_MASK 0x028
++
++#define MTK_WED_STATUS 0x060
++#define MTK_WED_STATUS_TX GENMASK(15, 8)
++
++#define MTK_WED_TX_BM_CTRL 0x080
++#define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM GENMASK(6, 0)
++#define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM GENMASK(22, 16)
++#define MTK_WED_TX_BM_CTRL_PAUSE BIT(28)
++
++#define MTK_WED_TX_BM_BASE 0x084
++
++#define MTK_WED_TX_BM_TKID 0x088
++#define MTK_WED_TX_BM_TKID_START GENMASK(15, 0)
++#define MTK_WED_TX_BM_TKID_END GENMASK(31, 16)
++
++#define MTK_WED_TX_BM_BUF_LEN 0x08c
++
++#define MTK_WED_TX_BM_INTF 0x09c
++#define MTK_WED_TX_BM_INTF_TKID GENMASK(15, 0)
++#define MTK_WED_TX_BM_INTF_TKFIFO_FDEP GENMASK(23, 16)
++#define MTK_WED_TX_BM_INTF_TKID_VALID BIT(28)
++#define MTK_WED_TX_BM_INTF_TKID_READ BIT(29)
++
++#define MTK_WED_TX_BM_DYN_THR 0x0a0
++#define MTK_WED_TX_BM_DYN_THR_LO GENMASK(6, 0)
++#define MTK_WED_TX_BM_DYN_THR_HI GENMASK(22, 16)
++
++#define MTK_WED_INT_STATUS 0x200
++#define MTK_WED_INT_MASK 0x204
++
++#define MTK_WED_GLO_CFG 0x208
++#define MTK_WED_GLO_CFG_TX_DMA_EN BIT(0)
++#define MTK_WED_GLO_CFG_TX_DMA_BUSY BIT(1)
++#define MTK_WED_GLO_CFG_RX_DMA_EN BIT(2)
++#define MTK_WED_GLO_CFG_RX_DMA_BUSY BIT(3)
++#define MTK_WED_GLO_CFG_RX_BT_SIZE GENMASK(5, 4)
++#define MTK_WED_GLO_CFG_TX_WB_DDONE BIT(6)
++#define MTK_WED_GLO_CFG_BIG_ENDIAN BIT(7)
++#define MTK_WED_GLO_CFG_DIS_BT_SIZE_ALIGN BIT(8)
++#define MTK_WED_GLO_CFG_TX_BT_SIZE_LO BIT(9)
++#define MTK_WED_GLO_CFG_MULTI_DMA_EN GENMASK(11, 10)
++#define MTK_WED_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12)
++#define MTK_WED_GLO_CFG_MI_DEPTH_RD GENMASK(21, 13)
++#define MTK_WED_GLO_CFG_TX_BT_SIZE_HI GENMASK(23, 22)
++#define MTK_WED_GLO_CFG_SW_RESET BIT(24)
++#define MTK_WED_GLO_CFG_FIRST_TOKEN_ONLY BIT(26)
++#define MTK_WED_GLO_CFG_OMIT_RX_INFO BIT(27)
++#define MTK_WED_GLO_CFG_OMIT_TX_INFO BIT(28)
++#define MTK_WED_GLO_CFG_BYTE_SWAP BIT(29)
++#define MTK_WED_GLO_CFG_RX_2B_OFFSET BIT(31)
++
++#define MTK_WED_RESET_IDX 0x20c
++#define MTK_WED_RESET_IDX_TX GENMASK(3, 0)
++#define MTK_WED_RESET_IDX_RX GENMASK(17, 16)
++
++#define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4)
++
++#define MTK_WED_RING_TX(_n) (0x300 + (_n) * 0x10)
++
++#define MTK_WED_RING_RX(_n) (0x400 + (_n) * 0x10)
++
++#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)
++
++#define MTK_WED_WPDMA_GLO_CFG 0x508
++#define MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN BIT(0)
++#define MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY BIT(1)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN BIT(2)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_BUSY BIT(3)
++#define MTK_WED_WPDMA_GLO_CFG_RX_BT_SIZE GENMASK(5, 4)
++#define MTK_WED_WPDMA_GLO_CFG_TX_WB_DDONE BIT(6)
++#define MTK_WED_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7)
++#define MTK_WED_WPDMA_GLO_CFG_DIS_BT_SIZE_ALIGN BIT(8)
++#define MTK_WED_WPDMA_GLO_CFG_TX_BT_SIZE_LO BIT(9)
++#define MTK_WED_WPDMA_GLO_CFG_MULTI_DMA_EN GENMASK(11, 10)
++#define MTK_WED_WPDMA_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12)
++#define MTK_WED_WPDMA_GLO_CFG_MI_DEPTH_RD GENMASK(21, 13)
++#define MTK_WED_WPDMA_GLO_CFG_TX_BT_SIZE_HI GENMASK(23, 22)
++#define MTK_WED_WPDMA_GLO_CFG_SW_RESET BIT(24)
++#define MTK_WED_WPDMA_GLO_CFG_FIRST_TOKEN_ONLY BIT(26)
++#define MTK_WED_WPDMA_GLO_CFG_OMIT_RX_INFO BIT(27)
++#define MTK_WED_WPDMA_GLO_CFG_OMIT_TX_INFO BIT(28)
++#define MTK_WED_WPDMA_GLO_CFG_BYTE_SWAP BIT(29)
++#define MTK_WED_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31)
++
++#define MTK_WED_WPDMA_RESET_IDX 0x50c
++#define MTK_WED_WPDMA_RESET_IDX_TX GENMASK(3, 0)
++#define MTK_WED_WPDMA_RESET_IDX_RX GENMASK(17, 16)
++
++#define MTK_WED_WPDMA_INT_CTRL 0x520
++#define MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV BIT(21)
++
++#define MTK_WED_WPDMA_INT_MASK 0x524
++
++#define MTK_WED_PCIE_CFG_BASE 0x560
++
++#define MTK_WED_PCIE_INT_TRIGGER 0x570
++#define MTK_WED_PCIE_INT_TRIGGER_STATUS BIT(16)
++
++#define MTK_WED_WPDMA_CFG_BASE 0x580
++
++#define MTK_WED_WPDMA_TX_MIB(_n) (0x5a0 + (_n) * 4)
++#define MTK_WED_WPDMA_TX_COHERENT_MIB(_n) (0x5d0 + (_n) * 4)
++
++#define MTK_WED_WPDMA_RING_TX(_n) (0x600 + (_n) * 0x10)
++#define MTK_WED_WPDMA_RING_RX(_n) (0x700 + (_n) * 0x10)
++#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_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)
++#define MTK_WED_WDMA_GLO_CFG_TX_WB_DDONE BIT(6)
++#define MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE BIT(13)
++#define MTK_WED_WDMA_GLO_CFG_WCOMPLETE_SEL BIT(16)
++#define MTK_WED_WDMA_GLO_CFG_INIT_PHASE_RXDMA_BYPASS BIT(17)
++#define MTK_WED_WDMA_GLO_CFG_INIT_PHASE_BYPASS BIT(18)
++#define MTK_WED_WDMA_GLO_CFG_FSM_RETURN_IDLE BIT(19)
++#define MTK_WED_WDMA_GLO_CFG_WAIT_COHERENT BIT(20)
++#define MTK_WED_WDMA_GLO_CFG_AXI_W_AFTER_AW BIT(21)
++#define MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY_SINGLE_W BIT(22)
++#define MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY BIT(23)
++#define MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP BIT(24)
++#define MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE BIT(25)
++#define MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE BIT(26)
++#define MTK_WED_WDMA_GLO_CFG_RXDRV_CLKGATE_BYPASS BIT(30)
++
++#define MTK_WED_WDMA_RESET_IDX 0xa08
++#define MTK_WED_WDMA_RESET_IDX_RX GENMASK(17, 16)
++#define MTK_WED_WDMA_RESET_IDX_DRV GENMASK(25, 24)
++
++#define MTK_WED_WDMA_INT_TRIGGER 0xa28
++#define MTK_WED_WDMA_INT_TRIGGER_RX_DONE GENMASK(17, 16)
++
++#define MTK_WED_WDMA_INT_CTRL 0xa2c
++#define MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL GENMASK(17, 16)
++
++#define MTK_WED_WDMA_OFFSET0 0xaa4
++#define MTK_WED_WDMA_OFFSET1 0xaa8
++
++#define MTK_WED_WDMA_RX_MIB(_n) (0xae0 + (_n) * 4)
++#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_RING_OFS_BASE 0x00
++#define MTK_WED_RING_OFS_COUNT 0x04
++#define MTK_WED_RING_OFS_CPU_IDX 0x08
++#define MTK_WED_RING_OFS_DMA_IDX 0x0c
++
++#define MTK_WDMA_RING_RX(_n) (0x100 + (_n) * 0x10)
++
++#define MTK_WDMA_GLO_CFG 0x204
++#define MTK_WDMA_GLO_CFG_RX_INFO_PRERES GENMASK(28, 26)
++
++#define MTK_WDMA_RESET_IDX 0x208
++#define MTK_WDMA_RESET_IDX_TX GENMASK(3, 0)
++#define MTK_WDMA_RESET_IDX_RX GENMASK(17, 16)
++
++#define MTK_WDMA_INT_MASK 0x228
++#define MTK_WDMA_INT_MASK_TX_DONE GENMASK(3, 0)
++#define MTK_WDMA_INT_MASK_RX_DONE GENMASK(17, 16)
++#define MTK_WDMA_INT_MASK_TX_DELAY BIT(28)
++#define MTK_WDMA_INT_MASK_TX_COHERENT BIT(29)
++#define MTK_WDMA_INT_MASK_RX_DELAY BIT(30)
++#define MTK_WDMA_INT_MASK_RX_COHERENT BIT(31)
++
++#define MTK_WDMA_INT_GRP1 0x250
++#define MTK_WDMA_INT_GRP2 0x254
++
++#define MTK_PCIE_MIRROR_MAP(n) ((n) ? 0x4 : 0x0)
++#define MTK_PCIE_MIRROR_MAP_EN BIT(0)
++#define MTK_PCIE_MIRROR_MAP_WED_ID BIT(1)
++
++/* DMA channel mapping */
++#define HIFSYS_DMA_AG_MAP 0x008
++
++#endif
+--- /dev/null
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -0,0 +1,131 @@
++#ifndef __MTK_WED_H
++#define __MTK_WED_H
++
++#include <linux/kernel.h>
++#include <linux/rcupdate.h>
++#include <linux/regmap.h>
++#include <linux/pci.h>
++
++#define MTK_WED_TX_QUEUES 2
++
++struct mtk_wed_hw;
++struct mtk_wdma_desc;
++
++struct mtk_wed_ring {
++ struct mtk_wdma_desc *desc;
++ dma_addr_t desc_phys;
++ int size;
++
++ u32 reg_base;
++ void __iomem *wpdma;
++};
++
++struct mtk_wed_device {
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++ const struct mtk_wed_ops *ops;
++ struct device *dev;
++ struct mtk_wed_hw *hw;
++ bool init_done, running;
++ int wdma_idx;
++ int irq;
++
++ 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 {
++ int size;
++ void **pages;
++ struct mtk_wdma_desc *desc;
++ dma_addr_t desc_phys;
++ } buf_ring;
++
++ /* filled by driver: */
++ struct {
++ struct pci_dev *pci_dev;
++
++ u32 wpdma_phys;
++
++ u16 token_start;
++ unsigned int nbuf;
++
++ 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);
++ } wlan;
++#endif
++};
++
++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 (*txfree_ring_setup)(struct mtk_wed_device *dev,
++ void __iomem *regs);
++ void (*detach)(struct mtk_wed_device *dev);
++
++ void (*stop)(struct mtk_wed_device *dev);
++ void (*start)(struct mtk_wed_device *dev, u32 irq_mask);
++ void (*reset_dma)(struct mtk_wed_device *dev);
++
++ u32 (*reg_read)(struct mtk_wed_device *dev, u32 reg);
++ void (*reg_write)(struct mtk_wed_device *dev, u32 reg, u32 val);
++
++ u32 (*irq_get)(struct mtk_wed_device *dev, u32 mask);
++ void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
++};
++
++extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
++
++static inline int
++mtk_wed_device_attach(struct mtk_wed_device *dev)
++{
++ int ret = -ENODEV;
++
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++ rcu_read_lock();
++ dev->ops = rcu_dereference(mtk_soc_wed_ops);
++ if (dev->ops)
++ ret = dev->ops->attach(dev);
++ else
++ rcu_read_unlock();
++
++ if (ret)
++ dev->ops = NULL;
++#endif
++
++ return ret;
++}
++
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++#define mtk_wed_device_active(_dev) !!(_dev)->ops
++#define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev)
++#define mtk_wed_device_start(_dev, _mask) (_dev)->ops->start(_dev, _mask)
++#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) \
++ (_dev)->ops->tx_ring_setup(_dev, _ring, _regs)
++#define mtk_wed_device_txfree_ring_setup(_dev, _regs) \
++ (_dev)->ops->txfree_ring_setup(_dev, _regs)
++#define mtk_wed_device_reg_read(_dev, _reg) \
++ (_dev)->ops->reg_read(_dev, _reg)
++#define mtk_wed_device_reg_write(_dev, _reg, _val) \
++ (_dev)->ops->reg_write(_dev, _reg, _val)
++#define mtk_wed_device_irq_get(_dev, _mask) \
++ (_dev)->ops->irq_get(_dev, _mask)
++#define mtk_wed_device_irq_set_mask(_dev, _mask) \
++ (_dev)->ops->irq_set_mask(_dev, _mask)
++#else
++static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
++{
++ return false;
++}
++#define mtk_wed_device_detach(_dev) do {} while (0)
++#define mtk_wed_device_start(_dev, _mask) do {} while (0)
++#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) -ENODEV
++#define mtk_wed_device_txfree_ring_setup(_dev, _ring, _regs) -ENODEV
++#define mtk_wed_device_reg_read(_dev, _reg) 0
++#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)
++#endif
++
++#endif
diff --git a/target/linux/generic/backport-6.1/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch b/target/linux/generic/backport-6.1/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch
new file mode 100644
index 0000000000..50d65b1eb6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch
@@ -0,0 +1,269 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 5 Feb 2022 18:29:22 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: implement flow offloading
+ to WED devices
+
+This allows hardware flow offloading from Ethernet to WLAN on MT7622 SoC
+
+Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -329,6 +329,24 @@ int mtk_foe_entry_set_pppoe(struct mtk_f
+ return 0;
+ }
+
++int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
++ int bss, int wcid)
++{
++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++ u32 *ib2 = mtk_foe_entry_ib2(entry);
++
++ *ib2 &= ~MTK_FOE_IB2_PORT_MG;
++ *ib2 |= MTK_FOE_IB2_WDMA_WINFO;
++ if (wdma_idx)
++ *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX;
++
++ l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) |
++ FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) |
++ FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq);
++
++ return 0;
++}
++
+ static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry)
+ {
+ return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -48,9 +48,9 @@ enum {
+ #define MTK_FOE_IB2_DEST_PORT GENMASK(7, 5)
+ #define MTK_FOE_IB2_MULTICAST BIT(8)
+
+-#define MTK_FOE_IB2_WHNAT_QID2 GENMASK(13, 12)
+-#define MTK_FOE_IB2_WHNAT_DEVIDX BIT(16)
+-#define MTK_FOE_IB2_WHNAT_NAT BIT(17)
++#define MTK_FOE_IB2_WDMA_QID2 GENMASK(13, 12)
++#define MTK_FOE_IB2_WDMA_DEVIDX BIT(16)
++#define MTK_FOE_IB2_WDMA_WINFO BIT(17)
+
+ #define MTK_FOE_IB2_PORT_MG GENMASK(17, 12)
+
+@@ -58,9 +58,9 @@ enum {
+
+ #define MTK_FOE_IB2_DSCP GENMASK(31, 24)
+
+-#define MTK_FOE_VLAN2_WHNAT_BSS GEMMASK(5, 0)
+-#define MTK_FOE_VLAN2_WHNAT_WCID GENMASK(13, 6)
+-#define MTK_FOE_VLAN2_WHNAT_RING GENMASK(15, 14)
++#define MTK_FOE_VLAN2_WINFO_BSS GENMASK(5, 0)
++#define MTK_FOE_VLAN2_WINFO_WCID GENMASK(13, 6)
++#define MTK_FOE_VLAN2_WINFO_RING GENMASK(15, 14)
+
+ enum {
+ MTK_FOE_STATE_INVALID,
+@@ -281,6 +281,8 @@ int mtk_foe_entry_set_ipv6_tuple(struct
+ int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
+ int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
+ int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
++int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
++ int bss, int wcid);
+ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
+ u16 timestamp);
+ int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -10,6 +10,7 @@
+ #include <net/pkt_cls.h>
+ #include <net/dsa.h>
+ #include "mtk_eth_soc.h"
++#include "mtk_wed.h"
+
+ struct mtk_flow_data {
+ struct ethhdr eth;
+@@ -39,6 +40,7 @@ struct mtk_flow_entry {
+ struct rhash_head node;
+ unsigned long cookie;
+ u16 hash;
++ s8 wed_index;
+ };
+
+ static const struct rhashtable_params mtk_flow_ht_params = {
+@@ -80,6 +82,35 @@ mtk_flow_offload_mangle_eth(const struct
+ memcpy(dest, src, act->mangle.mask ? 2 : 4);
+ }
+
++static int
++mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)
++{
++ struct net_device_path_ctx ctx = {
++ .dev = dev,
++ .daddr = addr,
++ };
++ struct net_device_path path = {};
++
++ if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
++ return -1;
++
++ if (!dev->netdev_ops->ndo_fill_forward_path)
++ return -1;
++
++ if (dev->netdev_ops->ndo_fill_forward_path(&ctx, &path))
++ return -1;
++
++ if (path.type != DEV_PATH_MTK_WDMA)
++ return -1;
++
++ info->wdma_idx = path.mtk_wdma.wdma_idx;
++ info->queue = path.mtk_wdma.queue;
++ info->bss = path.mtk_wdma.bss;
++ info->wcid = path.mtk_wdma.wcid;
++
++ return 0;
++}
++
+
+ static int
+ mtk_flow_mangle_ports(const struct flow_action_entry *act,
+@@ -149,10 +180,20 @@ mtk_flow_get_dsa_port(struct net_device
+
+ static int
+ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
+- struct net_device *dev)
++ struct net_device *dev, const u8 *dest_mac,
++ int *wed_index)
+ {
++ struct mtk_wdma_info info = {};
+ int pse_port, dsa_port;
+
++ if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
++ mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss,
++ info.wcid);
++ pse_port = 3;
++ *wed_index = info.wdma_idx;
++ goto out;
++ }
++
+ dsa_port = mtk_flow_get_dsa_port(&dev);
+ if (dsa_port >= 0)
+ mtk_foe_entry_set_dsa(foe, dsa_port);
+@@ -164,6 +205,7 @@ mtk_flow_set_output_device(struct mtk_et
+ else
+ return -EOPNOTSUPP;
+
++out:
+ mtk_foe_entry_set_pse_port(foe, pse_port);
+
+ return 0;
+@@ -179,6 +221,7 @@ mtk_flow_offload_replace(struct mtk_eth
+ struct net_device *odev = NULL;
+ struct mtk_flow_entry *entry;
+ int offload_type = 0;
++ int wed_index = -1;
+ u16 addr_type = 0;
+ u32 timestamp;
+ u8 l4proto = 0;
+@@ -326,10 +369,14 @@ mtk_flow_offload_replace(struct mtk_eth
+ if (data.pppoe.num == 1)
+ mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
+
+- err = mtk_flow_set_output_device(eth, &foe, odev);
++ err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest,
++ &wed_index);
+ if (err)
+ return err;
+
++ if (wed_index >= 0 && (err = mtk_wed_flow_add(wed_index)) < 0)
++ return err;
++
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+@@ -343,6 +390,7 @@ mtk_flow_offload_replace(struct mtk_eth
+ }
+
+ entry->hash = hash;
++ entry->wed_index = wed_index;
+ err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
+ mtk_flow_ht_params);
+ if (err < 0)
+@@ -353,6 +401,8 @@ clear_flow:
+ mtk_foe_entry_clear(&eth->ppe, hash);
+ free:
+ kfree(entry);
++ if (wed_index >= 0)
++ mtk_wed_flow_remove(wed_index);
+ return err;
+ }
+
+@@ -369,6 +419,8 @@ mtk_flow_offload_destroy(struct mtk_eth
+ mtk_foe_entry_clear(&eth->ppe, entry->hash);
+ rhashtable_remove_fast(&eth->flow_table, &entry->node,
+ mtk_flow_ht_params);
++ if (entry->wed_index >= 0)
++ mtk_wed_flow_remove(entry->wed_index);
+ kfree(entry);
+
+ return 0;
+--- a/drivers/net/ethernet/mediatek/mtk_wed.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
+@@ -7,6 +7,7 @@
+ #include <linux/soc/mediatek/mtk_wed.h>
+ #include <linux/debugfs.h>
+ #include <linux/regmap.h>
++#include <linux/netdevice.h>
+
+ struct mtk_eth;
+
+@@ -27,6 +28,12 @@ struct mtk_wed_hw {
+ int index;
+ };
+
++struct mtk_wdma_info {
++ u8 wdma_idx;
++ u8 queue;
++ u16 wcid;
++ u8 bss;
++};
+
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ static inline void
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -872,6 +872,7 @@ enum net_device_path_type {
+ DEV_PATH_BRIDGE,
+ DEV_PATH_PPPOE,
+ DEV_PATH_DSA,
++ DEV_PATH_MTK_WDMA,
+ };
+
+ struct net_device_path {
+@@ -897,6 +898,12 @@ struct net_device_path {
+ int port;
+ u16 proto;
+ } dsa;
++ struct {
++ u8 wdma_idx;
++ u8 queue;
++ u16 wcid;
++ u8 bss;
++ } mtk_wdma;
+ };
+ };
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -763,6 +763,10 @@ int dev_fill_forward_path(const struct n
+ if (WARN_ON_ONCE(last_dev == ctx.dev))
+ return -1;
+ }
++
++ if (!ctx.dev)
++ return ret;
++
+ path = dev_fwd_path(stack);
+ if (!path)
+ return -1;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-04-arm64-dts-mediatek-mt7622-introduce-nodes-for-Wirele.patch b/target/linux/generic/backport-6.1/702-v5.19-04-arm64-dts-mediatek-mt7622-introduce-nodes-for-Wirele.patch
new file mode 100644
index 0000000000..2c6e3fd3cd
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-04-arm64-dts-mediatek-mt7622-introduce-nodes-for-Wirele.patch
@@ -0,0 +1,62 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 5 Feb 2022 18:36:36 +0100
+Subject: [PATCH] arm64: dts: mediatek: mt7622: introduce nodes for
+ Wireless Ethernet Dispatch
+
+Introduce wed0 and wed1 nodes in order to enable offloading forwarding
+between ethernet and wireless devices on the mt7622 chipset.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi
++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
+@@ -894,6 +894,11 @@
+ };
+ };
+
++ hifsys: syscon@1af00000 {
++ compatible = "mediatek,mt7622-hifsys", "syscon";
++ reg = <0 0x1af00000 0 0x70>;
++ };
++
+ ethsys: syscon@1b000000 {
+ compatible = "mediatek,mt7622-ethsys",
+ "syscon";
+@@ -912,6 +917,26 @@
+ #dma-cells = <1>;
+ };
+
++ pcie_mirror: pcie-mirror@10000400 {
++ compatible = "mediatek,mt7622-pcie-mirror",
++ "syscon";
++ reg = <0 0x10000400 0 0x10>;
++ };
++
++ wed0: wed@1020a000 {
++ compatible = "mediatek,mt7622-wed",
++ "syscon";
++ reg = <0 0x1020a000 0 0x1000>;
++ interrupts = <GIC_SPI 214 IRQ_TYPE_LEVEL_LOW>;
++ };
++
++ wed1: wed@1020b000 {
++ compatible = "mediatek,mt7622-wed",
++ "syscon";
++ reg = <0 0x1020b000 0 0x1000>;
++ interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_LOW>;
++ };
++
+ eth: ethernet@1b100000 {
+ compatible = "mediatek,mt7622-eth",
+ "mediatek,mt2701-eth",
+@@ -939,6 +964,9 @@
+ mediatek,ethsys = <&ethsys>;
+ mediatek,sgmiisys = <&sgmiisys>;
+ mediatek,cci-control = <&cci_control2>;
++ mediatek,wed = <&wed0>, <&wed1>;
++ mediatek,pcie-mirror = <&pcie_mirror>;
++ mediatek,hifsys = <&hifsys>;
+ dma-coherent;
+ #address-cells = <1>;
+ #size-cells = <0>;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-05-net-ethernet-mtk_eth_soc-add-ipv6-flow-offload-suppo.patch b/target/linux/generic/backport-6.1/702-v5.19-05-net-ethernet-mtk_eth_soc-add-ipv6-flow-offload-suppo.patch
new file mode 100644
index 0000000000..9adb067015
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-05-net-ethernet-mtk_eth_soc-add-ipv6-flow-offload-suppo.patch
@@ -0,0 +1,79 @@
+From: David Bentham <db260179@gmail.com>
+Date: Mon, 21 Feb 2022 15:36:16 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add ipv6 flow offload
+ support
+
+Add the missing IPv6 flow offloading support for routing only.
+Hardware flow offloading is done by the packet processing engine (PPE)
+of the Ethernet MAC and as it doesn't support mangling of IPv6 packets,
+IPv6 NAT cannot be supported.
+
+Signed-off-by: David Bentham <db260179@gmail.com>
+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
+@@ -6,6 +6,7 @@
+ #include <linux/if_ether.h>
+ #include <linux/rhashtable.h>
+ #include <linux/ip.h>
++#include <linux/ipv6.h>
+ #include <net/flow_offload.h>
+ #include <net/pkt_cls.h>
+ #include <net/dsa.h>
+@@ -20,6 +21,11 @@ struct mtk_flow_data {
+ __be32 src_addr;
+ __be32 dst_addr;
+ } v4;
++
++ struct {
++ struct in6_addr src_addr;
++ struct in6_addr dst_addr;
++ } v6;
+ };
+
+ __be16 src_port;
+@@ -65,6 +71,14 @@ mtk_flow_set_ipv4_addr(struct mtk_foe_en
+ data->v4.dst_addr, data->dst_port);
+ }
+
++static int
++mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data)
++{
++ return mtk_foe_entry_set_ipv6_tuple(foe,
++ data->v6.src_addr.s6_addr32, data->src_port,
++ data->v6.dst_addr.s6_addr32, data->dst_port);
++}
++
+ static void
+ mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
+ {
+@@ -296,6 +310,9 @@ mtk_flow_offload_replace(struct mtk_eth
+ case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
+ offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
+ break;
++ case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
++ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
++ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+@@ -331,6 +348,17 @@ mtk_flow_offload_replace(struct mtk_eth
+ mtk_flow_set_ipv4_addr(&foe, &data, false);
+ }
+
++ if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
++ struct flow_match_ipv6_addrs addrs;
++
++ flow_rule_match_ipv6_addrs(rule, &addrs);
++
++ data.v6.src_addr = addrs.key->src;
++ data.v6.dst_addr = addrs.key->dst;
++
++ mtk_flow_set_ipv6_addr(&foe, &data);
++ }
++
+ flow_action_for_each(i, act, &rule->action) {
+ if (act->id != FLOW_ACTION_MANGLE)
+ continue;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-06-net-ethernet-mtk_eth_soc-support-TC_SETUP_BLOCK-for-.patch b/target/linux/generic/backport-6.1/702-v5.19-06-net-ethernet-mtk_eth_soc-support-TC_SETUP_BLOCK-for-.patch
new file mode 100644
index 0000000000..1950d81ebb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-06-net-ethernet-mtk_eth_soc-support-TC_SETUP_BLOCK-for-.patch
@@ -0,0 +1,29 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 21 Feb 2022 15:37:21 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: support TC_SETUP_BLOCK for
+ PPE offload
+
+This allows offload entries to be created from user space
+
+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
+@@ -564,10 +564,13 @@ mtk_eth_setup_tc_block(struct net_device
+ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data)
+ {
+- if (type == TC_SETUP_FT)
++ switch (type) {
++ case TC_SETUP_BLOCK:
++ case TC_SETUP_FT:
+ return mtk_eth_setup_tc_block(dev, type_data);
+-
+- return -EOPNOTSUPP;
++ default:
++ return -EOPNOTSUPP;
++ }
+ }
+
+ int mtk_eth_offload_init(struct mtk_eth *eth)
diff --git a/target/linux/generic/backport-6.1/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch b/target/linux/generic/backport-6.1/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch
new file mode 100644
index 0000000000..f18a816b70
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch
@@ -0,0 +1,159 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 21 Feb 2022 15:38:20 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: allocate struct mtk_ppe
+ separately
+
+Preparation for adding more data to it, which will increase its size.
+
+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
+@@ -2335,7 +2335,7 @@ static int mtk_open(struct net_device *d
+ return err;
+ }
+
+- if (eth->soc->offload_version && mtk_ppe_start(&eth->ppe) == 0)
++ if (eth->soc->offload_version && mtk_ppe_start(eth->ppe) == 0)
+ gdm_config = MTK_GDMA_TO_PPE;
+
+ mtk_gdm_config(eth, gdm_config);
+@@ -2409,7 +2409,7 @@ static int mtk_stop(struct net_device *d
+ mtk_dma_free(eth);
+
+ if (eth->soc->offload_version)
+- mtk_ppe_stop(&eth->ppe);
++ mtk_ppe_stop(eth->ppe);
+
+ return 0;
+ }
+@@ -3301,10 +3301,11 @@ static int mtk_probe(struct platform_dev
+ }
+
+ if (eth->soc->offload_version) {
+- err = mtk_ppe_init(&eth->ppe, eth->dev,
+- eth->base + MTK_ETH_PPE_BASE, 2);
+- if (err)
++ eth->ppe = mtk_ppe_init(eth->dev, eth->base + MTK_ETH_PPE_BASE, 2);
++ if (!eth->ppe) {
++ err = -ENOMEM;
+ goto err_free_dev;
++ }
+
+ err = mtk_eth_offload_init(eth);
+ if (err)
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -983,7 +983,7 @@ struct mtk_eth {
+ u32 rx_dma_l4_valid;
+ int ip_align;
+
+- struct mtk_ppe ppe;
++ struct mtk_ppe *ppe;
+ struct rhashtable flow_table;
+ };
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -384,10 +384,15 @@ int mtk_foe_entry_commit(struct mtk_ppe
+ return hash;
+ }
+
+-int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
++struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base,
+ int version)
+ {
+ struct mtk_foe_entry *foe;
++ struct mtk_ppe *ppe;
++
++ ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL);
++ if (!ppe)
++ return NULL;
+
+ /* need to allocate a separate device, since it PPE DMA access is
+ * not coherent.
+@@ -399,13 +404,13 @@ int mtk_ppe_init(struct mtk_ppe *ppe, st
+ foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
+ &ppe->foe_phys, GFP_KERNEL);
+ if (!foe)
+- return -ENOMEM;
++ return NULL;
+
+ ppe->foe_table = foe;
+
+ mtk_ppe_debugfs_init(ppe);
+
+- return 0;
++ return ppe;
+ }
+
+ static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -246,8 +246,7 @@ struct mtk_ppe {
+ void *acct_table;
+ };
+
+-int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
+- int version);
++struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, int version);
+ int mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -411,7 +411,7 @@ mtk_flow_offload_replace(struct mtk_eth
+
+ entry->cookie = f->cookie;
+ timestamp = mtk_eth_timestamp(eth);
+- hash = mtk_foe_entry_commit(&eth->ppe, &foe, timestamp);
++ hash = mtk_foe_entry_commit(eth->ppe, &foe, timestamp);
+ if (hash < 0) {
+ err = hash;
+ goto free;
+@@ -426,7 +426,7 @@ mtk_flow_offload_replace(struct mtk_eth
+
+ return 0;
+ clear_flow:
+- mtk_foe_entry_clear(&eth->ppe, hash);
++ mtk_foe_entry_clear(eth->ppe, hash);
+ free:
+ kfree(entry);
+ if (wed_index >= 0)
+@@ -444,7 +444,7 @@ mtk_flow_offload_destroy(struct mtk_eth
+ if (!entry)
+ return -ENOENT;
+
+- mtk_foe_entry_clear(&eth->ppe, entry->hash);
++ mtk_foe_entry_clear(eth->ppe, entry->hash);
+ rhashtable_remove_fast(&eth->flow_table, &entry->node,
+ mtk_flow_ht_params);
+ if (entry->wed_index >= 0)
+@@ -466,7 +466,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
+ if (!entry)
+ return -ENOENT;
+
+- timestamp = mtk_foe_entry_timestamp(&eth->ppe, entry->hash);
++ timestamp = mtk_foe_entry_timestamp(eth->ppe, entry->hash);
+ if (timestamp < 0)
+ return -ETIMEDOUT;
+
+@@ -522,7 +522,7 @@ mtk_eth_setup_tc_block(struct net_device
+ struct flow_block_cb *block_cb;
+ flow_setup_cb_t *cb;
+
+- if (!eth->ppe.foe_table)
++ if (!eth->ppe || !eth->ppe->foe_table)
+ return -EOPNOTSUPP;
+
+ if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+@@ -575,7 +575,7 @@ int mtk_eth_setup_tc(struct net_device *
+
+ int mtk_eth_offload_init(struct mtk_eth *eth)
+ {
+- if (!eth->ppe.foe_table)
++ if (!eth->ppe || !eth->ppe->foe_table)
+ return 0;
+
+ return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);
diff --git a/target/linux/generic/backport-6.1/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch b/target/linux/generic/backport-6.1/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch
new file mode 100644
index 0000000000..2609cbedec
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch
@@ -0,0 +1,424 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 21 Feb 2022 15:39:18 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rework hardware flow table
+ management
+
+The hardware was designed to handle flow detection and creation of flow entries
+by itself, relying on the software primarily for filling in egress routing
+information.
+When there is a hash collision between multiple flows, this allows the hardware
+to maintain the entry for the most active flow.
+Additionally, the hardware only keeps offloading active for entries with at
+least 30 packets per second.
+
+With this rework, the code no longer creates a hardware entries directly.
+Instead, the hardware entry is only created when the PPE reports a matching
+unbound flow with the minimum target rate.
+In order to reduce CPU overhead, looking for flows belonging to a hash entry
+is rate limited to once every 100ms.
+
+This rework is also used as preparation for emulating bridge offload by
+managing L4 offload entries on demand.
+
+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
+@@ -21,6 +21,7 @@
+ #include <linux/pinctrl/devinfo.h>
+ #include <linux/phylink.h>
+ #include <linux/jhash.h>
++#include <linux/bitfield.h>
+ #include <net/dsa.h>
+
+ #include "mtk_eth_soc.h"
+@@ -1293,7 +1294,7 @@ static int mtk_poll_rx(struct napi_struc
+ struct net_device *netdev;
+ unsigned int pktlen;
+ dma_addr_t dma_addr;
+- u32 hash;
++ u32 hash, reason;
+ int mac;
+
+ ring = mtk_get_rx_ring(eth);
+@@ -1372,6 +1373,11 @@ static int mtk_poll_rx(struct napi_struc
+ skb_set_hash(skb, hash, PKT_HASH_TYPE_L4);
+ }
+
++ reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4);
++ if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
++ mtk_ppe_check_skb(eth->ppe, skb,
++ trxd.rxd4 & MTK_RXD4_FOE_ENTRY);
++
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
+ (trxd.rxd2 & RX_DMA_VTAG))
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+@@ -3301,7 +3307,7 @@ static int mtk_probe(struct platform_dev
+ }
+
+ if (eth->soc->offload_version) {
+- eth->ppe = mtk_ppe_init(eth->dev, eth->base + MTK_ETH_PPE_BASE, 2);
++ eth->ppe = mtk_ppe_init(eth, eth->base + MTK_ETH_PPE_BASE, 2);
+ if (!eth->ppe) {
+ err = -ENOMEM;
+ goto err_free_dev;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -6,9 +6,12 @@
+ #include <linux/iopoll.h>
+ #include <linux/etherdevice.h>
+ #include <linux/platform_device.h>
++#include "mtk_eth_soc.h"
+ #include "mtk_ppe.h"
+ #include "mtk_ppe_regs.h"
+
++static DEFINE_SPINLOCK(ppe_lock);
++
+ static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
+ {
+ writel(val, ppe->base + reg);
+@@ -41,6 +44,11 @@ static u32 ppe_clear(struct mtk_ppe *ppe
+ return ppe_m32(ppe, reg, val, 0);
+ }
+
++static u32 mtk_eth_timestamp(struct mtk_eth *eth)
++{
++ return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
++}
++
+ static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
+ {
+ int ret;
+@@ -353,26 +361,59 @@ static inline bool mtk_foe_entry_usable(
+ FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
+ }
+
+-int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
+- u16 timestamp)
++static bool
++mtk_flow_entry_match(struct mtk_flow_entry *entry, struct mtk_foe_entry *data)
++{
++ int type, len;
++
++ if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP)
++ return false;
++
++ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
++ if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++ len = offsetof(struct mtk_foe_entry, ipv6._rsv);
++ else
++ len = offsetof(struct mtk_foe_entry, ipv4.ib2);
++
++ return !memcmp(&entry->data.data, &data->data, len - 4);
++}
++
++static void
++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+ struct mtk_foe_entry *hwe;
+- u32 hash;
++ struct mtk_foe_entry foe;
+
++ spin_lock_bh(&ppe_lock);
++ if (entry->hash == 0xffff)
++ goto out;
++
++ hwe = &ppe->foe_table[entry->hash];
++ memcpy(&foe, hwe, sizeof(foe));
++ if (!mtk_flow_entry_match(entry, &foe)) {
++ entry->hash = 0xffff;
++ goto out;
++ }
++
++ entry->data.ib1 = foe.ib1;
++
++out:
++ spin_unlock_bh(&ppe_lock);
++}
++
++static void
++__mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
++ u16 hash)
++{
++ struct mtk_foe_entry *hwe;
++ u16 timestamp;
++
++ timestamp = mtk_eth_timestamp(ppe->eth);
+ timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP;
+ entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
+ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
+
+- hash = mtk_ppe_hash_entry(entry);
+ hwe = &ppe->foe_table[hash];
+- if (!mtk_foe_entry_usable(hwe)) {
+- hwe++;
+- hash++;
+-
+- if (!mtk_foe_entry_usable(hwe))
+- return -ENOSPC;
+- }
+-
+ memcpy(&hwe->data, &entry->data, sizeof(hwe->data));
+ wmb();
+ hwe->ib1 = entry->ib1;
+@@ -380,13 +421,77 @@ int mtk_foe_entry_commit(struct mtk_ppe
+ dma_wmb();
+
+ mtk_ppe_cache_clear(ppe);
++}
+
+- return hash;
++void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++{
++ spin_lock_bh(&ppe_lock);
++ hlist_del_init(&entry->list);
++ if (entry->hash != 0xffff) {
++ ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
++ ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
++ MTK_FOE_STATE_BIND);
++ dma_wmb();
++ }
++ entry->hash = 0xffff;
++ spin_unlock_bh(&ppe_lock);
++}
++
++int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++{
++ u32 hash = mtk_ppe_hash_entry(&entry->data);
++
++ entry->hash = 0xffff;
++ spin_lock_bh(&ppe_lock);
++ hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]);
++ spin_unlock_bh(&ppe_lock);
++
++ return 0;
++}
++
++void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
++{
++ struct hlist_head *head = &ppe->foe_flow[hash / 2];
++ struct mtk_flow_entry *entry;
++ struct mtk_foe_entry *hwe = &ppe->foe_table[hash];
++ bool found = false;
++
++ if (hlist_empty(head))
++ return;
++
++ spin_lock_bh(&ppe_lock);
++ hlist_for_each_entry(entry, head, list) {
++ if (found || !mtk_flow_entry_match(entry, hwe)) {
++ if (entry->hash != 0xffff)
++ entry->hash = 0xffff;
++ continue;
++ }
++
++ entry->hash = hash;
++ __mtk_foe_entry_commit(ppe, &entry->data, hash);
++ found = true;
++ }
++ spin_unlock_bh(&ppe_lock);
++}
++
++int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++{
++ u16 now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
++ u16 timestamp;
++
++ mtk_flow_entry_update(ppe, entry);
++ timestamp = entry->data.ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
++
++ if (timestamp > now)
++ return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
++ else
++ return now - timestamp;
+ }
+
+-struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base,
++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+ int version)
+ {
++ struct device *dev = eth->dev;
+ struct mtk_foe_entry *foe;
+ struct mtk_ppe *ppe;
+
+@@ -398,6 +503,7 @@ struct mtk_ppe *mtk_ppe_init(struct devi
+ * not coherent.
+ */
+ ppe->base = base;
++ ppe->eth = eth;
+ ppe->dev = dev;
+ ppe->version = version;
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -235,7 +235,17 @@ enum {
+ MTK_PPE_CPU_REASON_INVALID = 0x1f,
+ };
+
++struct mtk_flow_entry {
++ struct rhash_head node;
++ struct hlist_node list;
++ unsigned long cookie;
++ struct mtk_foe_entry data;
++ u16 hash;
++ s8 wed_index;
++};
++
+ struct mtk_ppe {
++ struct mtk_eth *eth;
+ struct device *dev;
+ void __iomem *base;
+ int version;
+@@ -243,18 +253,33 @@ struct mtk_ppe {
+ struct mtk_foe_entry *foe_table;
+ dma_addr_t foe_phys;
+
++ u16 foe_check_time[MTK_PPE_ENTRIES];
++ struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2];
++
+ void *acct_table;
+ };
+
+-struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, int version);
++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version);
+ int mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
+
++void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
++
+ static inline void
+-mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash)
++mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
+ {
+- ppe->foe_table[hash].ib1 = 0;
+- dma_wmb();
++ u16 now, diff;
++
++ if (!ppe)
++ return;
++
++ now = (u16)jiffies;
++ diff = now - ppe->foe_check_time[hash];
++ if (diff < HZ / 10)
++ return;
++
++ ppe->foe_check_time[hash] = now;
++ __mtk_ppe_check_skb(ppe, skb, hash);
+ }
+
+ static inline int
+@@ -282,8 +307,9 @@ int mtk_foe_entry_set_vlan(struct mtk_fo
+ int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
+ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+ int bss, int wcid);
+-int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
+- u16 timestamp);
++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);
+ int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
+
+ #endif
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -42,13 +42,6 @@ struct mtk_flow_data {
+ } pppoe;
+ };
+
+-struct mtk_flow_entry {
+- struct rhash_head node;
+- unsigned long cookie;
+- u16 hash;
+- s8 wed_index;
+-};
+-
+ static const struct rhashtable_params mtk_flow_ht_params = {
+ .head_offset = offsetof(struct mtk_flow_entry, node),
+ .key_offset = offsetof(struct mtk_flow_entry, cookie),
+@@ -56,12 +49,6 @@ static const struct rhashtable_params mt
+ .automatic_shrinking = true,
+ };
+
+-static u32
+-mtk_eth_timestamp(struct mtk_eth *eth)
+-{
+- return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
+-}
+-
+ static int
+ mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data,
+ bool egress)
+@@ -237,10 +224,8 @@ mtk_flow_offload_replace(struct mtk_eth
+ int offload_type = 0;
+ int wed_index = -1;
+ u16 addr_type = 0;
+- u32 timestamp;
+ u8 l4proto = 0;
+ int err = 0;
+- int hash;
+ int i;
+
+ if (rhashtable_lookup(&eth->flow_table, &f->cookie, mtk_flow_ht_params))
+@@ -410,23 +395,21 @@ mtk_flow_offload_replace(struct mtk_eth
+ return -ENOMEM;
+
+ entry->cookie = f->cookie;
+- timestamp = mtk_eth_timestamp(eth);
+- hash = mtk_foe_entry_commit(eth->ppe, &foe, timestamp);
+- if (hash < 0) {
+- err = hash;
++ memcpy(&entry->data, &foe, sizeof(entry->data));
++ entry->wed_index = wed_index;
++
++ if (mtk_foe_entry_commit(eth->ppe, entry) < 0)
+ goto free;
+- }
+
+- entry->hash = hash;
+- entry->wed_index = wed_index;
+ err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
+ mtk_flow_ht_params);
+ if (err < 0)
+- goto clear_flow;
++ goto clear;
+
+ return 0;
+-clear_flow:
+- mtk_foe_entry_clear(eth->ppe, hash);
++
++clear:
++ mtk_foe_entry_clear(eth->ppe, entry);
+ free:
+ kfree(entry);
+ if (wed_index >= 0)
+@@ -444,7 +427,7 @@ mtk_flow_offload_destroy(struct mtk_eth
+ if (!entry)
+ return -ENOENT;
+
+- mtk_foe_entry_clear(eth->ppe, entry->hash);
++ mtk_foe_entry_clear(eth->ppe, entry);
+ rhashtable_remove_fast(&eth->flow_table, &entry->node,
+ mtk_flow_ht_params);
+ if (entry->wed_index >= 0)
+@@ -458,7 +441,6 @@ static int
+ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
+ {
+ struct mtk_flow_entry *entry;
+- int timestamp;
+ u32 idle;
+
+ entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
+@@ -466,11 +448,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
+ if (!entry)
+ return -ENOENT;
+
+- timestamp = mtk_foe_entry_timestamp(eth->ppe, entry->hash);
+- if (timestamp < 0)
+- return -ETIMEDOUT;
+-
+- idle = mtk_eth_timestamp(eth) - timestamp;
++ idle = mtk_foe_entry_idle_time(eth->ppe, entry);
+ f->stats.lastused = jiffies - idle * HZ;
+
+ return 0;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-09-net-ethernet-mtk_eth_soc-remove-bridge-flow-offload-.patch b/target/linux/generic/backport-6.1/702-v5.19-09-net-ethernet-mtk_eth_soc-remove-bridge-flow-offload-.patch
new file mode 100644
index 0000000000..2ff0b341f9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-09-net-ethernet-mtk_eth_soc-remove-bridge-flow-offload-.patch
@@ -0,0 +1,44 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 21 Feb 2022 15:55:19 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: remove bridge flow offload
+ type entry support
+
+According to MediaTek, this feature is not supported in current hardware
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -84,13 +84,6 @@ static u32 mtk_ppe_hash_entry(struct mtk
+ u32 hash;
+
+ switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) {
+- case MTK_PPE_PKT_TYPE_BRIDGE:
+- hv1 = e->bridge.src_mac_lo;
+- hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16);
+- hv2 = e->bridge.src_mac_hi >> 16;
+- hv2 ^= e->bridge.dest_mac_lo;
+- hv3 = e->bridge.dest_mac_hi;
+- break;
+ case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
+ case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
+ hv1 = e->ipv4.orig.ports;
+@@ -572,7 +565,6 @@ int mtk_ppe_start(struct mtk_ppe *ppe)
+ MTK_PPE_FLOW_CFG_IP4_NAT |
+ MTK_PPE_FLOW_CFG_IP4_NAPT |
+ MTK_PPE_FLOW_CFG_IP4_DSLITE |
+- MTK_PPE_FLOW_CFG_L2_BRIDGE |
+ MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
+ ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -32,7 +32,6 @@ static const char *mtk_foe_pkt_type_str(
+ static const char * const type_str[] = {
+ [MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
+ [MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
+- [MTK_PPE_PKT_TYPE_BRIDGE] = "L2",
+ [MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
+ [MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
+ [MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
diff --git a/target/linux/generic/backport-6.1/702-v5.19-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch b/target/linux/generic/backport-6.1/702-v5.19-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch
new file mode 100644
index 0000000000..209c65e66a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch
@@ -0,0 +1,553 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 23 Feb 2022 10:56:34 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: support creating mac
+ address based offload entries
+
+This will be used to implement a limited form of bridge offloading.
+Since the hardware does not support flow table entries with just source
+and destination MAC address, the driver has to emulate it.
+
+The hardware automatically creates entries entries for incoming flows, even
+when they are bridged instead of routed, and reports when packets for these
+flows have reached the minimum PPS rate for offloading.
+
+After this happens, we look up the L2 flow offload entry based on the MAC
+header and fill in the output routing information in the flow table.
+The dynamically created per-flow entries are automatically removed when
+either the hardware flowtable entry expires, is replaced, or if the offload
+rule they belong to is removed
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -6,12 +6,22 @@
+ #include <linux/iopoll.h>
+ #include <linux/etherdevice.h>
+ #include <linux/platform_device.h>
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <net/dsa.h>
+ #include "mtk_eth_soc.h"
+ #include "mtk_ppe.h"
+ #include "mtk_ppe_regs.h"
+
+ static DEFINE_SPINLOCK(ppe_lock);
+
++static const struct rhashtable_params mtk_flow_l2_ht_params = {
++ .head_offset = offsetof(struct mtk_flow_entry, l2_node),
++ .key_offset = offsetof(struct mtk_flow_entry, data.bridge),
++ .key_len = offsetof(struct mtk_foe_bridge, key_end),
++ .automatic_shrinking = true,
++};
++
+ static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
+ {
+ writel(val, ppe->base + reg);
+@@ -123,6 +133,9 @@ mtk_foe_entry_l2(struct mtk_foe_entry *e
+ {
+ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
+
++ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
++ return &entry->bridge.l2;
++
+ if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
+ return &entry->ipv6.l2;
+
+@@ -134,6 +147,9 @@ mtk_foe_entry_ib2(struct mtk_foe_entry *
+ {
+ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
+
++ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
++ return &entry->bridge.ib2;
++
+ if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
+ return &entry->ipv6.ib2;
+
+@@ -168,7 +184,12 @@ int mtk_foe_entry_prepare(struct mtk_foe
+ if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
+ entry->ipv6.ports = ports_pad;
+
+- if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
++ if (type == MTK_PPE_PKT_TYPE_BRIDGE) {
++ ether_addr_copy(entry->bridge.src_mac, src_mac);
++ ether_addr_copy(entry->bridge.dest_mac, dest_mac);
++ entry->bridge.ib2 = val;
++ l2 = &entry->bridge.l2;
++ } else if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
+ entry->ipv6.ib2 = val;
+ l2 = &entry->ipv6.l2;
+ } else {
+@@ -372,12 +393,96 @@ mtk_flow_entry_match(struct mtk_flow_ent
+ }
+
+ static void
++__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++{
++ struct hlist_head *head;
++ struct hlist_node *tmp;
++
++ if (entry->type == MTK_FLOW_TYPE_L2) {
++ rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node,
++ mtk_flow_l2_ht_params);
++
++ head = &entry->l2_flows;
++ hlist_for_each_entry_safe(entry, tmp, head, l2_data.list)
++ __mtk_foe_entry_clear(ppe, entry);
++ return;
++ }
++
++ hlist_del_init(&entry->list);
++ if (entry->hash != 0xffff) {
++ ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
++ ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
++ MTK_FOE_STATE_BIND);
++ dma_wmb();
++ }
++ entry->hash = 0xffff;
++
++ if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW)
++ return;
++
++ hlist_del_init(&entry->l2_data.list);
++ kfree(entry);
++}
++
++static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1)
++{
++ u16 timestamp;
++ u16 now;
++
++ now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
++ timestamp = ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
++
++ if (timestamp > now)
++ return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
++ else
++ return now - timestamp;
++}
++
++static void
++mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++{
++ struct mtk_flow_entry *cur;
++ struct mtk_foe_entry *hwe;
++ struct hlist_node *tmp;
++ int idle;
++
++ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
++ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) {
++ int cur_idle;
++ u32 ib1;
++
++ hwe = &ppe->foe_table[cur->hash];
++ ib1 = READ_ONCE(hwe->ib1);
++
++ if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
++ cur->hash = 0xffff;
++ __mtk_foe_entry_clear(ppe, cur);
++ continue;
++ }
++
++ cur_idle = __mtk_foe_entry_idle_time(ppe, ib1);
++ if (cur_idle >= idle)
++ continue;
++
++ idle = cur_idle;
++ entry->data.ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
++ entry->data.ib1 |= hwe->ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
++ }
++}
++
++static void
+ mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+ struct mtk_foe_entry *hwe;
+ struct mtk_foe_entry foe;
+
+ spin_lock_bh(&ppe_lock);
++
++ if (entry->type == MTK_FLOW_TYPE_L2) {
++ mtk_flow_entry_update_l2(ppe, entry);
++ goto out;
++ }
++
+ if (entry->hash == 0xffff)
+ goto out;
+
+@@ -419,21 +524,28 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+ void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+ spin_lock_bh(&ppe_lock);
+- hlist_del_init(&entry->list);
+- if (entry->hash != 0xffff) {
+- ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
+- ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
+- MTK_FOE_STATE_BIND);
+- dma_wmb();
+- }
+- entry->hash = 0xffff;
++ __mtk_foe_entry_clear(ppe, entry);
+ spin_unlock_bh(&ppe_lock);
+ }
+
++static int
++mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++{
++ entry->type = MTK_FLOW_TYPE_L2;
++
++ return rhashtable_insert_fast(&ppe->l2_flows, &entry->l2_node,
++ mtk_flow_l2_ht_params);
++}
++
+ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+- u32 hash = mtk_ppe_hash_entry(&entry->data);
++ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
++ u32 hash;
++
++ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
++ return mtk_foe_entry_commit_l2(ppe, entry);
+
++ hash = mtk_ppe_hash_entry(&entry->data);
+ entry->hash = 0xffff;
+ spin_lock_bh(&ppe_lock);
+ hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]);
+@@ -442,18 +554,72 @@ int mtk_foe_entry_commit(struct mtk_ppe
+ return 0;
+ }
+
++static void
++mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
++ u16 hash)
++{
++ struct mtk_flow_entry *flow_info;
++ struct mtk_foe_entry foe, *hwe;
++ struct mtk_foe_mac_info *l2;
++ u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP;
++ int type;
++
++ flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end),
++ GFP_ATOMIC);
++ if (!flow_info)
++ return;
++
++ flow_info->l2_data.base_flow = entry;
++ flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
++ flow_info->hash = hash;
++ hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / 2]);
++ hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
++
++ hwe = &ppe->foe_table[hash];
++ memcpy(&foe, hwe, sizeof(foe));
++ foe.ib1 &= ib1_mask;
++ foe.ib1 |= entry->data.ib1 & ~ib1_mask;
++
++ l2 = mtk_foe_entry_l2(&foe);
++ memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
++
++ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, foe.ib1);
++ if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
++ memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
++ else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
++ l2->etype = ETH_P_IPV6;
++
++ *mtk_foe_entry_ib2(&foe) = entry->data.bridge.ib2;
++
++ __mtk_foe_entry_commit(ppe, &foe, hash);
++}
++
+ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
+ {
+ struct hlist_head *head = &ppe->foe_flow[hash / 2];
+- struct mtk_flow_entry *entry;
+ struct mtk_foe_entry *hwe = &ppe->foe_table[hash];
++ struct mtk_flow_entry *entry;
++ struct mtk_foe_bridge key = {};
++ struct ethhdr *eh;
+ bool found = false;
+-
+- if (hlist_empty(head))
+- return;
++ u8 *tag;
+
+ spin_lock_bh(&ppe_lock);
++
++ if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
++ goto out;
++
+ hlist_for_each_entry(entry, head, list) {
++ if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
++ if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
++ MTK_FOE_STATE_BIND))
++ continue;
++
++ entry->hash = 0xffff;
++ __mtk_foe_entry_clear(ppe, entry);
++ continue;
++ }
++
+ if (found || !mtk_flow_entry_match(entry, hwe)) {
+ if (entry->hash != 0xffff)
+ entry->hash = 0xffff;
+@@ -464,21 +630,50 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+ __mtk_foe_entry_commit(ppe, &entry->data, hash);
+ found = true;
+ }
++
++ if (found)
++ goto out;
++
++ eh = eth_hdr(skb);
++ ether_addr_copy(key.dest_mac, eh->h_dest);
++ ether_addr_copy(key.src_mac, eh->h_source);
++ tag = skb->data - 2;
++ key.vlan = 0;
++ switch (skb->protocol) {
++#if IS_ENABLED(CONFIG_NET_DSA)
++ case htons(ETH_P_XDSA):
++ if (!netdev_uses_dsa(skb->dev) ||
++ skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK)
++ goto out;
++
++ tag += 4;
++ if (get_unaligned_be16(tag) != ETH_P_8021Q)
++ break;
++
++ fallthrough;
++#endif
++ case htons(ETH_P_8021Q):
++ key.vlan = get_unaligned_be16(tag + 2) & VLAN_VID_MASK;
++ break;
++ default:
++ break;
++ }
++
++ entry = rhashtable_lookup_fast(&ppe->l2_flows, &key, mtk_flow_l2_ht_params);
++ if (!entry)
++ goto out;
++
++ mtk_foe_entry_commit_subflow(ppe, entry, hash);
++
++out:
+ spin_unlock_bh(&ppe_lock);
+ }
+
+ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+- u16 now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
+- u16 timestamp;
+-
+ mtk_flow_entry_update(ppe, entry);
+- timestamp = entry->data.ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
+
+- if (timestamp > now)
+- return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
+- else
+- return now - timestamp;
++ return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+ }
+
+ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+@@ -492,6 +687,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+ if (!ppe)
+ return NULL;
+
++ rhashtable_init(&ppe->l2_flows, &mtk_flow_l2_ht_params);
++
+ /* need to allocate a separate device, since it PPE DMA access is
+ * not coherent.
+ */
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -6,6 +6,7 @@
+
+ #include <linux/kernel.h>
+ #include <linux/bitfield.h>
++#include <linux/rhashtable.h>
+
+ #define MTK_ETH_PPE_BASE 0xc00
+
+@@ -84,19 +85,16 @@ struct mtk_foe_mac_info {
+ u16 src_mac_lo;
+ };
+
++/* software-only entry type */
+ struct mtk_foe_bridge {
+- u32 dest_mac_hi;
+-
+- u16 src_mac_lo;
+- u16 dest_mac_lo;
++ u8 dest_mac[ETH_ALEN];
++ u8 src_mac[ETH_ALEN];
++ u16 vlan;
+
+- u32 src_mac_hi;
++ struct {} key_end;
+
+ u32 ib2;
+
+- u32 _rsv[5];
+-
+- u32 udf_tsid;
+ struct mtk_foe_mac_info l2;
+ };
+
+@@ -235,13 +233,33 @@ enum {
+ MTK_PPE_CPU_REASON_INVALID = 0x1f,
+ };
+
++enum {
++ MTK_FLOW_TYPE_L4,
++ MTK_FLOW_TYPE_L2,
++ MTK_FLOW_TYPE_L2_SUBFLOW,
++};
++
+ struct mtk_flow_entry {
++ union {
++ struct hlist_node list;
++ struct {
++ struct rhash_head l2_node;
++ struct hlist_head l2_flows;
++ };
++ };
++ u8 type;
++ s8 wed_index;
++ u16 hash;
++ union {
++ struct mtk_foe_entry data;
++ struct {
++ struct mtk_flow_entry *base_flow;
++ struct hlist_node list;
++ struct {} end;
++ } l2_data;
++ };
+ struct rhash_head node;
+- struct hlist_node list;
+ unsigned long cookie;
+- struct mtk_foe_entry data;
+- u16 hash;
+- s8 wed_index;
+ };
+
+ struct mtk_ppe {
+@@ -256,6 +274,8 @@ struct mtk_ppe {
+ u16 foe_check_time[MTK_PPE_ENTRIES];
+ struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2];
+
++ struct rhashtable l2_flows;
++
+ void *acct_table;
+ };
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -31,6 +31,8 @@ struct mtk_flow_data {
+ __be16 src_port;
+ __be16 dst_port;
+
++ u16 vlan_in;
++
+ struct {
+ u16 id;
+ __be16 proto;
+@@ -257,9 +259,45 @@ mtk_flow_offload_replace(struct mtk_eth
+ return -EOPNOTSUPP;
+ }
+
++ switch (addr_type) {
++ case 0:
++ offload_type = MTK_PPE_PKT_TYPE_BRIDGE;
++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
++ struct flow_match_eth_addrs match;
++
++ flow_rule_match_eth_addrs(rule, &match);
++ memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN);
++ memcpy(data.eth.h_source, match.key->src, ETH_ALEN);
++ } else {
++ return -EOPNOTSUPP;
++ }
++
++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
++ struct flow_match_vlan match;
++
++ flow_rule_match_vlan(rule, &match);
++
++ if (match.key->vlan_tpid != cpu_to_be16(ETH_P_8021Q))
++ return -EOPNOTSUPP;
++
++ data.vlan_in = match.key->vlan_id;
++ }
++ break;
++ case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
++ offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
++ break;
++ case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
++ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
+ flow_action_for_each(i, act, &rule->action) {
+ switch (act->id) {
+ case FLOW_ACTION_MANGLE:
++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
++ return -EOPNOTSUPP;
+ if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
+ mtk_flow_offload_mangle_eth(act, &data.eth);
+ break;
+@@ -291,17 +329,6 @@ mtk_flow_offload_replace(struct mtk_eth
+ }
+ }
+
+- switch (addr_type) {
+- case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
+- offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
+- break;
+- case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
+- offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
+- break;
+- default:
+- return -EOPNOTSUPP;
+- }
+-
+ if (!is_valid_ether_addr(data.eth.h_source) ||
+ !is_valid_ether_addr(data.eth.h_dest))
+ return -EINVAL;
+@@ -315,10 +342,13 @@ mtk_flow_offload_replace(struct mtk_eth
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+ struct flow_match_ports ports;
+
++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
++ return -EOPNOTSUPP;
++
+ flow_rule_match_ports(rule, &ports);
+ data.src_port = ports.key->src;
+ data.dst_port = ports.key->dst;
+- } else {
++ } else if (offload_type != MTK_PPE_PKT_TYPE_BRIDGE) {
+ return -EOPNOTSUPP;
+ }
+
+@@ -348,6 +378,9 @@ mtk_flow_offload_replace(struct mtk_eth
+ if (act->id != FLOW_ACTION_MANGLE)
+ continue;
+
++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
++ return -EOPNOTSUPP;
++
+ switch (act->mangle.htype) {
+ case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
+ case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
+@@ -373,6 +406,9 @@ mtk_flow_offload_replace(struct mtk_eth
+ return err;
+ }
+
++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
++ foe.bridge.vlan = data.vlan_in;
++
+ if (data.vlan.num == 1) {
+ if (data.vlan.proto != htons(ETH_P_8021Q))
+ return -EOPNOTSUPP;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-11-net-ethernet-mtk_eth_soc-wed-fix-sparse-endian-warni.patch b/target/linux/generic/backport-6.1/702-v5.19-11-net-ethernet-mtk_eth_soc-wed-fix-sparse-endian-warni.patch
new file mode 100644
index 0000000000..8f3dfe8239
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-11-net-ethernet-mtk_eth_soc-wed-fix-sparse-endian-warni.patch
@@ -0,0 +1,56 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 8 Apr 2022 10:59:45 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc/wed: fix sparse endian warnings
+
+Descriptor fields are little-endian
+
+Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)")
+Reported-by: kernel test robot <lkp@intel.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+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
+@@ -144,16 +144,17 @@ mtk_wed_buffer_alloc(struct mtk_wed_devi
+
+ for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) {
+ u32 txd_size;
++ u32 ctrl;
+
+ txd_size = dev->wlan.init_buf(buf, buf_phys, token++);
+
+- desc->buf0 = buf_phys;
+- desc->buf1 = buf_phys + txd_size;
+- desc->ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0,
+- txd_size) |
+- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
+- MTK_WED_BUF_SIZE - txd_size) |
+- MTK_WDMA_DESC_CTRL_LAST_SEG1;
++ desc->buf0 = cpu_to_le32(buf_phys);
++ desc->buf1 = cpu_to_le32(buf_phys + txd_size);
++ ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
++ MTK_WED_BUF_SIZE - txd_size) |
++ MTK_WDMA_DESC_CTRL_LAST_SEG1;
++ desc->ctrl = cpu_to_le32(ctrl);
+ desc->info = 0;
+ desc++;
+
+@@ -184,12 +185,14 @@ mtk_wed_free_buffer(struct mtk_wed_devic
+
+ for (i = 0, page_idx = 0; i < dev->buf_ring.size; i += MTK_WED_BUF_PER_PAGE) {
+ void *page = page_list[page_idx++];
++ dma_addr_t buf_addr;
+
+ if (!page)
+ break;
+
+- dma_unmap_page(dev->hw->dev, desc[i].buf0,
+- PAGE_SIZE, DMA_BIDIRECTIONAL);
++ buf_addr = le32_to_cpu(desc[i].buf0);
++ dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE,
++ DMA_BIDIRECTIONAL);
+ __free_page(page);
+ }
+
diff --git a/target/linux/generic/backport-6.1/702-v5.19-12-net-ethernet-mtk_eth_soc-fix-return-value-check-in-m.patch b/target/linux/generic/backport-6.1/702-v5.19-12-net-ethernet-mtk_eth_soc-fix-return-value-check-in-m.patch
new file mode 100644
index 0000000000..4ec8fe74bc
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-12-net-ethernet-mtk_eth_soc-fix-return-value-check-in-m.patch
@@ -0,0 +1,25 @@
+From: Yang Yingliang <yangyingliang@huawei.com>
+Date: Fri, 8 Apr 2022 11:22:46 +0800
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix return value check in
+ mtk_wed_add_hw()
+
+If syscon_regmap_lookup_by_phandle() fails, it never return NULL pointer,
+change the check to IS_ERR().
+
+Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)")
+Reported-by: Hulk Robot <hulkci@huawei.com>
+Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
+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
+@@ -816,7 +816,7 @@ void mtk_wed_add_hw(struct device_node *
+ return;
+
+ regs = syscon_regmap_lookup_by_phandle(np, NULL);
+- if (!regs)
++ if (IS_ERR(regs))
+ return;
+
+ rcu_assign_pointer(mtk_soc_wed_ops, &wed_ops);
diff --git a/target/linux/generic/backport-6.1/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch b/target/linux/generic/backport-6.1/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch
new file mode 100644
index 0000000000..a7c5f08f10
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch
@@ -0,0 +1,35 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 11 Apr 2022 12:13:25 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: use standard property for
+ cci-control-port
+
+Rely on standard cci-control-port property to identify CCI port
+reference.
+Update mt7622 dts binding.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi
++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
+@@ -963,7 +963,7 @@
+ power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>;
+ mediatek,ethsys = <&ethsys>;
+ mediatek,sgmiisys = <&sgmiisys>;
+- mediatek,cci-control = <&cci_control2>;
++ cci-control-port = <&cci_control2>;
+ mediatek,wed = <&wed0>, <&wed1>;
+ mediatek,pcie-mirror = <&pcie_mirror>;
+ mediatek,hifsys = <&hifsys>;
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3188,7 +3188,7 @@ static int mtk_probe(struct platform_dev
+ struct regmap *cci;
+
+ cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+- "mediatek,cci-control");
++ "cci-control-port");
+ /* enable CPU/bus coherency */
+ if (!IS_ERR(cci))
+ regmap_write(cci, 0, 3);
diff --git a/target/linux/generic/backport-6.1/702-v5.19-14-net-ethernet-mtk_eth_soc-use-after-free-in-__mtk_ppe.patch b/target/linux/generic/backport-6.1/702-v5.19-14-net-ethernet-mtk_eth_soc-use-after-free-in-__mtk_ppe.patch
new file mode 100644
index 0000000000..656b3a159e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-14-net-ethernet-mtk_eth_soc-use-after-free-in-__mtk_ppe.patch
@@ -0,0 +1,33 @@
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Tue, 12 Apr 2022 12:24:19 +0300
+Subject: [PATCH] net: ethernet: mtk_eth_soc: use after free in
+ __mtk_ppe_check_skb()
+
+The __mtk_foe_entry_clear() function frees "entry" so we have to use
+the _safe() version of hlist_for_each_entry() to prevent a use after
+free.
+
+Fixes: 33fc42de3327 ("net: ethernet: mtk_eth_soc: support creating mac address based offload entries")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -600,6 +600,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+ struct mtk_foe_entry *hwe = &ppe->foe_table[hash];
+ struct mtk_flow_entry *entry;
+ struct mtk_foe_bridge key = {};
++ struct hlist_node *n;
+ struct ethhdr *eh;
+ bool found = false;
+ u8 *tag;
+@@ -609,7 +610,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+ if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
+ goto out;
+
+- hlist_for_each_entry(entry, head, list) {
++ hlist_for_each_entry_safe(entry, n, head, list) {
+ if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
+ if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
+ MTK_FOE_STATE_BIND))
diff --git a/target/linux/generic/backport-6.1/702-v5.19-15-net-ethernet-mtk_eth_soc-add-check-for-allocation-fa.patch b/target/linux/generic/backport-6.1/702-v5.19-15-net-ethernet-mtk_eth_soc-add-check-for-allocation-fa.patch
new file mode 100644
index 0000000000..714163c86b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-15-net-ethernet-mtk_eth_soc-add-check-for-allocation-fa.patch
@@ -0,0 +1,22 @@
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Thu, 21 Apr 2022 18:49:02 +0300
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add check for allocation failure
+
+Check if the kzalloc() failed.
+
+Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+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
+@@ -827,6 +827,8 @@ void mtk_wed_add_hw(struct device_node *
+ goto unlock;
+
+ hw = kzalloc(sizeof(*hw), GFP_KERNEL);
++ if (!hw)
++ goto unlock;
+ hw->node = np;
+ hw->regs = regs;
+ hw->eth = eth;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-16-eth-mtk_eth_soc-silence-the-GCC-12-array-bounds-warn.patch b/target/linux/generic/backport-6.1/702-v5.19-16-eth-mtk_eth_soc-silence-the-GCC-12-array-bounds-warn.patch
new file mode 100644
index 0000000000..aa98745ac6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-16-eth-mtk_eth_soc-silence-the-GCC-12-array-bounds-warn.patch
@@ -0,0 +1,26 @@
+From: Jakub Kicinski <kuba@kernel.org>
+Date: Fri, 20 May 2022 12:56:03 -0700
+Subject: [PATCH] eth: mtk_eth_soc: silence the GCC 12 array-bounds warning
+
+GCC 12 gets upset because in mtk_foe_entry_commit_subflow()
+this driver allocates a partial structure. The writes are
+within bounds.
+
+Silence these warnings for now, our build bot runs GCC 12
+so we won't allow any new instances.
+
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -11,3 +11,8 @@ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) +
+ endif
+ obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o
+ obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o
++
++# FIXME: temporarily silence -Warray-bounds on non W=1+ builds
++ifndef KBUILD_EXTRA_WARN
++CFLAGS_mtk_ppe.o += -Wno-array-bounds
++endif
diff --git a/target/linux/generic/backport-6.1/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch b/target/linux/generic/backport-6.1/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch
new file mode 100644
index 0000000000..9b6321f1da
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch
@@ -0,0 +1,52 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:26 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on GFP_KERNEL for
+ dma_alloc_coherent whenever possible
+
+Rely on GFP_KERNEL for dma descriptors mappings in mtk_tx_alloc(),
+mtk_rx_alloc() and mtk_init_fq_dma() since they are run in non-irq
+context.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -846,7 +846,7 @@ static int mtk_init_fq_dma(struct mtk_et
+ eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
+ cnt * sizeof(struct mtk_tx_dma),
+ &eth->phy_scratch_ring,
+- GFP_ATOMIC);
++ GFP_KERNEL);
+ if (unlikely(!eth->scratch_ring))
+ return -ENOMEM;
+
+@@ -1624,7 +1624,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ goto no_tx_mem;
+
+ ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
+- &ring->phys, GFP_ATOMIC);
++ &ring->phys, GFP_KERNEL);
+ if (!ring->dma)
+ goto no_tx_mem;
+
+@@ -1642,8 +1642,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ */
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+ ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
+- &ring->phys_pdma,
+- GFP_ATOMIC);
++ &ring->phys_pdma, GFP_KERNEL);
+ if (!ring->dma_pdma)
+ goto no_tx_mem;
+
+@@ -1758,7 +1757,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+
+ ring->dma = dma_alloc_coherent(eth->dma_dev,
+ rx_dma_size * sizeof(*ring->dma),
+- &ring->phys, GFP_ATOMIC);
++ &ring->phys, GFP_KERNEL);
+ if (!ring->dma)
+ return -ENOMEM;
+
diff --git a/target/linux/generic/backport-6.1/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch b/target/linux/generic/backport-6.1/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch
new file mode 100644
index 0000000000..8e16ea2556
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch
@@ -0,0 +1,206 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:27 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: move tx dma desc configuration in
+ mtk_tx_set_dma_desc
+
+Move tx dma descriptor configuration in mtk_tx_set_dma_desc routine.
+This is a preliminary patch to introduce mt7986 ethernet support since
+it relies on a different tx dma descriptor layout.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -972,18 +972,51 @@ static void setup_tx_buf(struct mtk_eth
+ }
+ }
+
++static void mtk_tx_set_dma_desc(struct net_device *dev, struct mtk_tx_dma *desc,
++ struct mtk_tx_dma_desc_info *info)
++{
++ struct mtk_mac *mac = netdev_priv(dev);
++ u32 data;
++
++ WRITE_ONCE(desc->txd1, info->addr);
++
++ data = TX_DMA_SWC | TX_DMA_PLEN0(info->size);
++ if (info->last)
++ data |= TX_DMA_LS0;
++ WRITE_ONCE(desc->txd3, data);
++
++ data = (mac->id + 1) << TX_DMA_FPORT_SHIFT; /* forward port */
++ if (info->first) {
++ if (info->gso)
++ data |= TX_DMA_TSO;
++ /* tx checksum offload */
++ if (info->csum)
++ data |= TX_DMA_CHKSUM;
++ /* vlan header offload */
++ if (info->vlan)
++ data |= TX_DMA_INS_VLAN | info->vlan_tci;
++ }
++ WRITE_ONCE(desc->txd4, data);
++}
++
+ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ int tx_num, struct mtk_tx_ring *ring, bool gso)
+ {
++ struct mtk_tx_dma_desc_info txd_info = {
++ .size = skb_headlen(skb),
++ .gso = gso,
++ .csum = skb->ip_summed == CHECKSUM_PARTIAL,
++ .vlan = skb_vlan_tag_present(skb),
++ .vlan_tci = skb_vlan_tag_get(skb),
++ .first = true,
++ .last = !skb_is_nonlinear(skb),
++ };
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_dma *itxd, *txd;
+ struct mtk_tx_dma *itxd_pdma, *txd_pdma;
+ struct mtk_tx_buf *itx_buf, *tx_buf;
+- dma_addr_t mapped_addr;
+- unsigned int nr_frags;
+ int i, n_desc = 1;
+- u32 txd4 = 0, fport;
+ int k = 0;
+
+ itxd = ring->next_free;
+@@ -991,49 +1024,32 @@ static int mtk_tx_map(struct sk_buff *sk
+ if (itxd == ring->last_free)
+ return -ENOMEM;
+
+- /* set the forward port */
+- fport = (mac->id + 1) << TX_DMA_FPORT_SHIFT;
+- txd4 |= fport;
+-
+ itx_buf = mtk_desc_to_tx_buf(ring, itxd);
+ memset(itx_buf, 0, sizeof(*itx_buf));
+
+- if (gso)
+- txd4 |= TX_DMA_TSO;
+-
+- /* TX Checksum offload */
+- if (skb->ip_summed == CHECKSUM_PARTIAL)
+- txd4 |= TX_DMA_CHKSUM;
+-
+- /* VLAN header offload */
+- if (skb_vlan_tag_present(skb))
+- txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb);
+-
+- mapped_addr = dma_map_single(eth->dma_dev, skb->data,
+- skb_headlen(skb), DMA_TO_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
++ txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size,
++ DMA_TO_DEVICE);
++ if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr)))
+ return -ENOMEM;
+
+- WRITE_ONCE(itxd->txd1, mapped_addr);
++ mtk_tx_set_dma_desc(dev, itxd, &txd_info);
++
+ itx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
+ itx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 :
+ MTK_TX_FLAGS_FPORT1;
+- setup_tx_buf(eth, itx_buf, itxd_pdma, mapped_addr, skb_headlen(skb),
++ setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size,
+ k++);
+
+ /* TX SG offload */
+ txd = itxd;
+ txd_pdma = qdma_to_pdma(ring, txd);
+- nr_frags = skb_shinfo(skb)->nr_frags;
+
+- for (i = 0; i < nr_frags; i++) {
++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ unsigned int offset = 0;
+ int frag_size = skb_frag_size(frag);
+
+ while (frag_size) {
+- bool last_frag = false;
+- unsigned int frag_map_size;
+ bool new_desc = true;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA) ||
+@@ -1048,23 +1064,17 @@ static int mtk_tx_map(struct sk_buff *sk
+ new_desc = false;
+ }
+
+-
+- frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN);
+- mapped_addr = skb_frag_dma_map(eth->dma_dev, frag, offset,
+- frag_map_size,
+- DMA_TO_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
++ memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
++ txd_info.size = min(frag_size, MTK_TX_DMA_BUF_LEN);
++ 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,
++ offset, txd_info.size,
++ DMA_TO_DEVICE);
++ if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr)))
+ goto err_dma;
+
+- if (i == nr_frags - 1 &&
+- (frag_size - frag_map_size) == 0)
+- last_frag = true;
+-
+- WRITE_ONCE(txd->txd1, mapped_addr);
+- WRITE_ONCE(txd->txd3, (TX_DMA_SWC |
+- TX_DMA_PLEN0(frag_map_size) |
+- last_frag * TX_DMA_LS0));
+- WRITE_ONCE(txd->txd4, fport);
++ mtk_tx_set_dma_desc(dev, txd, &txd_info);
+
+ tx_buf = mtk_desc_to_tx_buf(ring, txd);
+ if (new_desc)
+@@ -1074,20 +1084,17 @@ static int mtk_tx_map(struct sk_buff *sk
+ tx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 :
+ MTK_TX_FLAGS_FPORT1;
+
+- setup_tx_buf(eth, tx_buf, txd_pdma, mapped_addr,
+- frag_map_size, k++);
++ setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr,
++ txd_info.size, k++);
+
+- frag_size -= frag_map_size;
+- offset += frag_map_size;
++ frag_size -= txd_info.size;
++ offset += txd_info.size;
+ }
+ }
+
+ /* store skb to cleanup */
+ itx_buf->skb = skb;
+
+- WRITE_ONCE(itxd->txd4, txd4);
+- WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
+- (!nr_frags * TX_DMA_LS0)));
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+ if (k & 0x1)
+ txd_pdma->txd2 |= TX_DMA_LS0;
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -843,6 +843,17 @@ enum mkt_eth_capabilities {
+ MTK_MUX_U3_GMAC2_TO_QPHY | \
+ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA)
+
++struct mtk_tx_dma_desc_info {
++ dma_addr_t addr;
++ u32 size;
++ u16 vlan_tci;
++ u8 gso:1;
++ u8 csum:1;
++ u8 vlan:1;
++ u8 first:1;
++ u8 last:1;
++};
++
+ /* struct mtk_eth_data - This is the structure holding all differences
+ * among various plaforms
+ * @ana_rgc3: The offset for register ANA_RGC3 related to
diff --git a/target/linux/generic/backport-6.1/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch b/target/linux/generic/backport-6.1/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch
new file mode 100644
index 0000000000..f5206bba00
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch
@@ -0,0 +1,167 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:28 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add txd_size to mtk_soc_data
+
+In order to remove mtk_tx_dma size dependency, introduce txd_size in
+mtk_soc_data data structure. Rely on txd_size in mtk_init_fq_dma() and
+mtk_dma_free() routines.
+This is a preliminary patch to add mt7986 ethernet support.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -838,20 +838,20 @@ static void *mtk_max_lro_buf_alloc(gfp_t
+ /* the qdma core needs scratch memory to be setup */
+ static int mtk_init_fq_dma(struct mtk_eth *eth)
+ {
++ const struct mtk_soc_data *soc = eth->soc;
+ dma_addr_t phy_ring_tail;
+ int cnt = MTK_DMA_SIZE;
+ dma_addr_t dma_addr;
+ int i;
+
+ eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
+- cnt * sizeof(struct mtk_tx_dma),
++ cnt * soc->txrx.txd_size,
+ &eth->phy_scratch_ring,
+ GFP_KERNEL);
+ if (unlikely(!eth->scratch_ring))
+ return -ENOMEM;
+
+- eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE,
+- GFP_KERNEL);
++ eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL);
+ if (unlikely(!eth->scratch_head))
+ return -ENOMEM;
+
+@@ -861,16 +861,19 @@ static int mtk_init_fq_dma(struct mtk_et
+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
+ return -ENOMEM;
+
+- phy_ring_tail = eth->phy_scratch_ring +
+- (sizeof(struct mtk_tx_dma) * (cnt - 1));
++ phy_ring_tail = eth->phy_scratch_ring + soc->txrx.txd_size * (cnt - 1);
+
+ for (i = 0; i < cnt; i++) {
+- eth->scratch_ring[i].txd1 =
+- (dma_addr + (i * MTK_QDMA_PAGE_SIZE));
++ struct mtk_tx_dma *txd;
++
++ txd = (void *)eth->scratch_ring + i * soc->txrx.txd_size;
++ txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
+ if (i < cnt - 1)
+- eth->scratch_ring[i].txd2 = (eth->phy_scratch_ring +
+- ((i + 1) * sizeof(struct mtk_tx_dma)));
+- eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE);
++ txd->txd2 = eth->phy_scratch_ring +
++ (i + 1) * soc->txrx.txd_size;
++
++ txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
++ txd->txd4 = 0;
+ }
+
+ mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD);
+@@ -2170,6 +2173,7 @@ static int mtk_dma_init(struct mtk_eth *
+
+ static void mtk_dma_free(struct mtk_eth *eth)
+ {
++ const struct mtk_soc_data *soc = eth->soc;
+ int i;
+
+ for (i = 0; i < MTK_MAC_COUNT; i++)
+@@ -2177,9 +2181,8 @@ 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 * sizeof(struct mtk_tx_dma),
+- eth->scratch_ring,
+- eth->phy_scratch_ring);
++ MTK_DMA_SIZE * soc->txrx.txd_size,
++ eth->scratch_ring, eth->phy_scratch_ring);
+ eth->scratch_ring = NULL;
+ eth->phy_scratch_ring = 0;
+ }
+@@ -3391,6 +3394,9 @@ static const struct mtk_soc_data mt2701_
+ .hw_features = MTK_HW_FEATURES,
+ .required_clks = MT7623_CLKS_BITMAP,
+ .required_pctl = true,
++ .txrx = {
++ .txd_size = sizeof(struct mtk_tx_dma),
++ },
+ };
+
+ static const struct mtk_soc_data mt7621_data = {
+@@ -3399,6 +3405,9 @@ static const struct mtk_soc_data mt7621_
+ .required_clks = MT7621_CLKS_BITMAP,
+ .required_pctl = false,
+ .offload_version = 2,
++ .txrx = {
++ .txd_size = sizeof(struct mtk_tx_dma),
++ },
+ };
+
+ static const struct mtk_soc_data mt7622_data = {
+@@ -3408,6 +3417,9 @@ static const struct mtk_soc_data mt7622_
+ .required_clks = MT7622_CLKS_BITMAP,
+ .required_pctl = false,
+ .offload_version = 2,
++ .txrx = {
++ .txd_size = sizeof(struct mtk_tx_dma),
++ },
+ };
+
+ static const struct mtk_soc_data mt7623_data = {
+@@ -3416,6 +3428,9 @@ static const struct mtk_soc_data mt7623_
+ .required_clks = MT7623_CLKS_BITMAP,
+ .required_pctl = true,
+ .offload_version = 2,
++ .txrx = {
++ .txd_size = sizeof(struct mtk_tx_dma),
++ },
+ };
+
+ static const struct mtk_soc_data mt7629_data = {
+@@ -3424,6 +3439,9 @@ static const struct mtk_soc_data mt7629_
+ .hw_features = MTK_HW_FEATURES,
+ .required_clks = MT7629_CLKS_BITMAP,
+ .required_pctl = false,
++ .txrx = {
++ .txd_size = sizeof(struct mtk_tx_dma),
++ },
+ };
+
+ static const struct mtk_soc_data rt5350_data = {
+@@ -3431,6 +3449,9 @@ static const struct mtk_soc_data rt5350_
+ .hw_features = MTK_HW_FEATURES_MT7628,
+ .required_clks = MT7628_CLKS_BITMAP,
+ .required_pctl = false,
++ .txrx = {
++ .txd_size = sizeof(struct mtk_tx_dma),
++ },
+ };
+
+ const struct of_device_id of_mtk_match[] = {
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -864,6 +864,7 @@ struct mtk_tx_dma_desc_info {
+ * the target SoC
+ * @required_pctl A bool value to show whether the SoC requires
+ * the extra setup for those pins used by GMAC.
++ * @txd_size Tx DMA descriptor size.
+ */
+ struct mtk_soc_data {
+ u32 ana_rgc3;
+@@ -872,6 +873,9 @@ struct mtk_soc_data {
+ bool required_pctl;
+ u8 offload_version;
+ netdev_features_t hw_features;
++ struct {
++ u32 txd_size;
++ } txrx;
+ };
+
+ /* currently no SoC has more than 2 macs */
diff --git a/target/linux/generic/backport-6.1/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch b/target/linux/generic/backport-6.1/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch
new file mode 100644
index 0000000000..ebe1ee3d59
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch
@@ -0,0 +1,78 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:29 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on txd_size in
+ mtk_tx_alloc/mtk_tx_clean
+
+This is a preliminary patch to add mt7986 ethernet support.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1625,8 +1625,10 @@ static int mtk_napi_rx(struct napi_struc
+
+ static int mtk_tx_alloc(struct mtk_eth *eth)
+ {
++ const struct mtk_soc_data *soc = eth->soc;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+- int i, sz = sizeof(*ring->dma);
++ int i, sz = soc->txrx.txd_size;
++ struct mtk_tx_dma *txd;
+
+ ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf),
+ GFP_KERNEL);
+@@ -1642,8 +1644,10 @@ static int mtk_tx_alloc(struct mtk_eth *
+ int next = (i + 1) % MTK_DMA_SIZE;
+ u32 next_ptr = ring->phys + next * sz;
+
+- ring->dma[i].txd2 = next_ptr;
+- ring->dma[i].txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
++ txd = (void *)ring->dma + i * sz;
++ txd->txd2 = next_ptr;
++ txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
++ txd->txd4 = 0;
+ }
+
+ /* On MT7688 (PDMA only) this driver uses the ring->dma structs
+@@ -1665,7 +1669,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ ring->dma_size = MTK_DMA_SIZE;
+ atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
+ ring->next_free = &ring->dma[0];
+- ring->last_free = &ring->dma[MTK_DMA_SIZE - 1];
++ ring->last_free = (void *)txd;
+ ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz));
+ ring->thresh = MAX_SKB_FRAGS;
+
+@@ -1698,6 +1702,7 @@ no_tx_mem:
+
+ static void mtk_tx_clean(struct mtk_eth *eth)
+ {
++ const struct mtk_soc_data *soc = eth->soc;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ int i;
+
+@@ -1710,17 +1715,15 @@ static void mtk_tx_clean(struct mtk_eth
+
+ if (ring->dma) {
+ dma_free_coherent(eth->dma_dev,
+- MTK_DMA_SIZE * sizeof(*ring->dma),
+- ring->dma,
+- ring->phys);
++ MTK_DMA_SIZE * soc->txrx.txd_size,
++ ring->dma, ring->phys);
+ ring->dma = NULL;
+ }
+
+ if (ring->dma_pdma) {
+ dma_free_coherent(eth->dma_dev,
+- MTK_DMA_SIZE * sizeof(*ring->dma_pdma),
+- ring->dma_pdma,
+- ring->phys_pdma);
++ MTK_DMA_SIZE * soc->txrx.txd_size,
++ ring->dma_pdma, ring->phys_pdma);
+ ring->dma_pdma = NULL;
+ }
+ }
diff --git a/target/linux/generic/backport-6.1/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch b/target/linux/generic/backport-6.1/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch
new file mode 100644
index 0000000000..053412e749
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch
@@ -0,0 +1,109 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:30 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on txd_size in
+ mtk_desc_to_tx_buf
+
+This is a preliminary patch to add mt7986 ethernet support.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -891,10 +891,11 @@ static inline void *mtk_qdma_phys_to_vir
+ return ret + (desc - ring->phys);
+ }
+
+-static inline struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring,
+- struct mtk_tx_dma *txd)
++static struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring,
++ struct mtk_tx_dma *txd,
++ u32 txd_size)
+ {
+- int idx = txd - ring->dma;
++ int idx = ((void *)txd - (void *)ring->dma) / txd_size;
+
+ return &ring->buf[idx];
+ }
+@@ -1016,6 +1017,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ };
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
++ const struct mtk_soc_data *soc = eth->soc;
+ struct mtk_tx_dma *itxd, *txd;
+ struct mtk_tx_dma *itxd_pdma, *txd_pdma;
+ struct mtk_tx_buf *itx_buf, *tx_buf;
+@@ -1027,7 +1029,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ if (itxd == ring->last_free)
+ return -ENOMEM;
+
+- itx_buf = mtk_desc_to_tx_buf(ring, itxd);
++ itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
+ memset(itx_buf, 0, sizeof(*itx_buf));
+
+ txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size,
+@@ -1055,7 +1057,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ while (frag_size) {
+ bool new_desc = true;
+
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA) ||
++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) ||
+ (i & 0x1)) {
+ txd = mtk_qdma_phys_to_virt(ring, txd->txd2);
+ txd_pdma = qdma_to_pdma(ring, txd);
+@@ -1079,7 +1081,8 @@ static int mtk_tx_map(struct sk_buff *sk
+
+ mtk_tx_set_dma_desc(dev, txd, &txd_info);
+
+- tx_buf = mtk_desc_to_tx_buf(ring, txd);
++ tx_buf = mtk_desc_to_tx_buf(ring, txd,
++ soc->txrx.txd_size);
+ if (new_desc)
+ memset(tx_buf, 0, sizeof(*tx_buf));
+ tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC;
+@@ -1098,7 +1101,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ /* store skb to cleanup */
+ itx_buf->skb = skb;
+
+- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
+ if (k & 0x1)
+ txd_pdma->txd2 |= TX_DMA_LS0;
+ else
+@@ -1116,7 +1119,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ */
+ wmb();
+
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
+ if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) ||
+ !netdev_xmit_more())
+ mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR);
+@@ -1130,13 +1133,13 @@ static int mtk_tx_map(struct sk_buff *sk
+
+ err_dma:
+ do {
+- tx_buf = mtk_desc_to_tx_buf(ring, itxd);
++ tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
+
+ /* unmap dma */
+ mtk_tx_unmap(eth, tx_buf, false);
+
+ itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
+- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA))
+ itxd_pdma->txd2 = TX_DMA_DESP2_DEF;
+
+ itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
+@@ -1450,7 +1453,8 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ if ((desc->txd3 & TX_DMA_OWNER_CPU) == 0)
+ break;
+
+- tx_buf = mtk_desc_to_tx_buf(ring, desc);
++ tx_buf = mtk_desc_to_tx_buf(ring, desc,
++ eth->soc->txrx.txd_size);
+ if (tx_buf->flags & MTK_TX_FLAGS_FPORT1)
+ mac = 1;
+
diff --git a/target/linux/generic/backport-6.1/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch b/target/linux/generic/backport-6.1/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch
new file mode 100644
index 0000000000..251d583f29
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch
@@ -0,0 +1,39 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:31 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on txd_size in txd_to_idx
+
+This is a preliminary patch to add mt7986 ethernet support.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -906,9 +906,10 @@ static struct mtk_tx_dma *qdma_to_pdma(s
+ return ring->dma_pdma - ring->dma + dma;
+ }
+
+-static int txd_to_idx(struct mtk_tx_ring *ring, struct mtk_tx_dma *dma)
++static int txd_to_idx(struct mtk_tx_ring *ring, struct mtk_tx_dma *dma,
++ u32 txd_size)
+ {
+- return ((void *)dma - (void *)ring->dma) / sizeof(*dma);
++ return ((void *)dma - (void *)ring->dma) / txd_size;
+ }
+
+ static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf,
+@@ -1124,8 +1125,10 @@ static int mtk_tx_map(struct sk_buff *sk
+ !netdev_xmit_more())
+ mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR);
+ } else {
+- int next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd),
+- ring->dma_size);
++ int next_idx;
++
++ next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->txrx.txd_size),
++ ring->dma_size);
+ mtk_w32(eth, next_idx, MT7628_TX_CTX_IDX0);
+ }
+
diff --git a/target/linux/generic/backport-6.1/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch b/target/linux/generic/backport-6.1/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch
new file mode 100644
index 0000000000..ec206f28d6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch
@@ -0,0 +1,102 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:32 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add rxd_size to mtk_soc_data
+
+Similar to tx counterpart, introduce rxd_size in mtk_soc_data data
+structure.
+This is a preliminary patch to add mt7986 ethernet support.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1776,7 +1776,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+ }
+
+ ring->dma = dma_alloc_coherent(eth->dma_dev,
+- rx_dma_size * sizeof(*ring->dma),
++ rx_dma_size * eth->soc->txrx.rxd_size,
+ &ring->phys, GFP_KERNEL);
+ if (!ring->dma)
+ return -ENOMEM;
+@@ -1834,9 +1834,8 @@ static void mtk_rx_clean(struct mtk_eth
+
+ if (ring->dma) {
+ dma_free_coherent(eth->dma_dev,
+- ring->dma_size * sizeof(*ring->dma),
+- ring->dma,
+- ring->phys);
++ ring->dma_size * eth->soc->txrx.rxd_size,
++ ring->dma, ring->phys);
+ ring->dma = NULL;
+ }
+ }
+@@ -3406,6 +3405,7 @@ static const struct mtk_soc_data mt2701_
+ .required_pctl = true,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
++ .rxd_size = sizeof(struct mtk_rx_dma),
+ },
+ };
+
+@@ -3417,6 +3417,7 @@ static const struct mtk_soc_data mt7621_
+ .offload_version = 2,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
++ .rxd_size = sizeof(struct mtk_rx_dma),
+ },
+ };
+
+@@ -3429,6 +3430,7 @@ static const struct mtk_soc_data mt7622_
+ .offload_version = 2,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
++ .rxd_size = sizeof(struct mtk_rx_dma),
+ },
+ };
+
+@@ -3440,6 +3442,7 @@ static const struct mtk_soc_data mt7623_
+ .offload_version = 2,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
++ .rxd_size = sizeof(struct mtk_rx_dma),
+ },
+ };
+
+@@ -3451,6 +3454,7 @@ static const struct mtk_soc_data mt7629_
+ .required_pctl = false,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
++ .rxd_size = sizeof(struct mtk_rx_dma),
+ },
+ };
+
+@@ -3461,6 +3465,7 @@ static const struct mtk_soc_data rt5350_
+ .required_pctl = false,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
++ .rxd_size = sizeof(struct mtk_rx_dma),
+ },
+ };
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -865,6 +865,7 @@ struct mtk_tx_dma_desc_info {
+ * @required_pctl A bool value to show whether the SoC requires
+ * the extra setup for those pins used by GMAC.
+ * @txd_size Tx DMA descriptor size.
++ * @rxd_size Rx DMA descriptor size.
+ */
+ struct mtk_soc_data {
+ u32 ana_rgc3;
+@@ -875,6 +876,7 @@ struct mtk_soc_data {
+ netdev_features_t hw_features;
+ struct {
+ u32 txd_size;
++ u32 rxd_size;
+ } txrx;
+ };
+
diff --git a/target/linux/generic/backport-6.1/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch b/target/linux/generic/backport-6.1/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch
new file mode 100644
index 0000000000..eb92b8c7a2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch
@@ -0,0 +1,46 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:33 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on txd_size field in
+ mtk_poll_tx/mtk_poll_rx
+
+This is a preliminary to ad mt7986 ethernet support.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1265,9 +1265,12 @@ static struct mtk_rx_ring *mtk_get_rx_ri
+ return &eth->rx_ring[0];
+
+ for (i = 0; i < MTK_MAX_RX_RING_NUM; i++) {
++ struct mtk_rx_dma *rxd;
++
+ ring = &eth->rx_ring[i];
+ idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
+- if (ring->dma[idx].rxd2 & RX_DMA_DONE) {
++ rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size;
++ if (rxd->rxd2 & RX_DMA_DONE) {
+ ring->calc_idx_update = true;
+ return ring;
+ }
+@@ -1318,7 +1321,7 @@ static int mtk_poll_rx(struct napi_struc
+ goto rx_done;
+
+ idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
+- rxd = &ring->dma[idx];
++ rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size;
+ data = ring->data[idx];
+
+ if (!mtk_rx_get_desc(&trxd, rxd))
+@@ -1510,7 +1513,7 @@ static int mtk_poll_tx_pdma(struct mtk_e
+
+ mtk_tx_unmap(eth, tx_buf, true);
+
+- desc = &ring->dma[cpu];
++ desc = (void *)ring->dma + cpu * eth->soc->txrx.txd_size;
+ ring->last_free = desc;
+ atomic_inc(&ring->free_count);
+
diff --git a/target/linux/generic/backport-6.1/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch b/target/linux/generic/backport-6.1/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch
new file mode 100644
index 0000000000..456eec247c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch
@@ -0,0 +1,68 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:34 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on rxd_size field in
+ mtk_rx_alloc/mtk_rx_clean
+
+Remove mtk_rx_dma structure layout dependency in mtk_rx_alloc/mtk_rx_clean.
+Initialize to 0 rxd3 and rxd4 in mtk_rx_alloc.
+This is a preliminary patch to add mt7986 ethernet support.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1785,18 +1785,25 @@ static int mtk_rx_alloc(struct mtk_eth *
+ return -ENOMEM;
+
+ for (i = 0; i < rx_dma_size; i++) {
++ struct mtk_rx_dma *rxd;
++
+ dma_addr_t dma_addr = dma_map_single(eth->dma_dev,
+ ring->data[i] + NET_SKB_PAD + eth->ip_align,
+ ring->buf_size,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
+ return -ENOMEM;
+- ring->dma[i].rxd1 = (unsigned int)dma_addr;
++
++ rxd = (void *)ring->dma + i * eth->soc->txrx.rxd_size;
++ rxd->rxd1 = (unsigned int)dma_addr;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+- ring->dma[i].rxd2 = RX_DMA_LSO;
++ rxd->rxd2 = RX_DMA_LSO;
+ else
+- ring->dma[i].rxd2 = RX_DMA_PLEN0(ring->buf_size);
++ rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size);
++
++ rxd->rxd3 = 0;
++ rxd->rxd4 = 0;
+ }
+ ring->dma_size = rx_dma_size;
+ ring->calc_idx_update = false;
+@@ -1821,14 +1828,17 @@ static void mtk_rx_clean(struct mtk_eth
+
+ if (ring->data && ring->dma) {
+ for (i = 0; i < ring->dma_size; i++) {
++ struct mtk_rx_dma *rxd;
++
+ if (!ring->data[i])
+ continue;
+- if (!ring->dma[i].rxd1)
++
++ rxd = (void *)ring->dma + i * eth->soc->txrx.rxd_size;
++ if (!rxd->rxd1)
+ continue;
+- dma_unmap_single(eth->dma_dev,
+- ring->dma[i].rxd1,
+- ring->buf_size,
+- DMA_FROM_DEVICE);
++
++ dma_unmap_single(eth->dma_dev, rxd->rxd1,
++ ring->buf_size, DMA_FROM_DEVICE);
+ skb_free_frag(ring->data[i]);
+ }
+ kfree(ring->data);
diff --git a/target/linux/generic/backport-6.1/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch b/target/linux/generic/backport-6.1/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch
new file mode 100644
index 0000000000..272f782877
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch
@@ -0,0 +1,814 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:35 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce device register map
+
+Introduce reg_map structure to add the capability to support different
+register definitions. Move register definitions in mtk_regmap structure.
+This is a preliminary patch to introduce mt7986 ethernet support.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -34,6 +34,59 @@ MODULE_PARM_DESC(msg_level, "Message lev
+ #define MTK_ETHTOOL_STAT(x) { #x, \
+ offsetof(struct mtk_hw_stats, x) / sizeof(u64) }
+
++static const struct mtk_reg_map mtk_reg_map = {
++ .tx_irq_mask = 0x1a1c,
++ .tx_irq_status = 0x1a18,
++ .pdma = {
++ .rx_ptr = 0x0900,
++ .rx_cnt_cfg = 0x0904,
++ .pcrx_ptr = 0x0908,
++ .glo_cfg = 0x0a04,
++ .rst_idx = 0x0a08,
++ .delay_irq = 0x0a0c,
++ .irq_status = 0x0a20,
++ .irq_mask = 0x0a28,
++ .int_grp = 0x0a50,
++ },
++ .qdma = {
++ .qtx_cfg = 0x1800,
++ .rx_ptr = 0x1900,
++ .rx_cnt_cfg = 0x1904,
++ .qcrx_ptr = 0x1908,
++ .glo_cfg = 0x1a04,
++ .rst_idx = 0x1a08,
++ .delay_irq = 0x1a0c,
++ .fc_th = 0x1a10,
++ .int_grp = 0x1a20,
++ .hred = 0x1a44,
++ .ctx_ptr = 0x1b00,
++ .dtx_ptr = 0x1b04,
++ .crx_ptr = 0x1b10,
++ .drx_ptr = 0x1b14,
++ .fq_head = 0x1b20,
++ .fq_tail = 0x1b24,
++ .fq_count = 0x1b28,
++ .fq_blen = 0x1b2c,
++ },
++ .gdm1_cnt = 0x2400,
++};
++
++static const struct mtk_reg_map mt7628_reg_map = {
++ .tx_irq_mask = 0x0a28,
++ .tx_irq_status = 0x0a20,
++ .pdma = {
++ .rx_ptr = 0x0900,
++ .rx_cnt_cfg = 0x0904,
++ .pcrx_ptr = 0x0908,
++ .glo_cfg = 0x0a04,
++ .rst_idx = 0x0a08,
++ .delay_irq = 0x0a0c,
++ .irq_status = 0x0a20,
++ .irq_mask = 0x0a28,
++ .int_grp = 0x0a50,
++ },
++};
++
+ /* strings used by ethtool */
+ static const struct mtk_ethtool_stats {
+ char str[ETH_GSTRING_LEN];
+@@ -619,8 +672,8 @@ static inline void mtk_tx_irq_disable(st
+ u32 val;
+
+ spin_lock_irqsave(&eth->tx_irq_lock, flags);
+- val = mtk_r32(eth, eth->tx_int_mask_reg);
+- mtk_w32(eth, val & ~mask, eth->tx_int_mask_reg);
++ val = mtk_r32(eth, eth->soc->reg_map->tx_irq_mask);
++ mtk_w32(eth, val & ~mask, eth->soc->reg_map->tx_irq_mask);
+ spin_unlock_irqrestore(&eth->tx_irq_lock, flags);
+ }
+
+@@ -630,8 +683,8 @@ static inline void mtk_tx_irq_enable(str
+ u32 val;
+
+ spin_lock_irqsave(&eth->tx_irq_lock, flags);
+- val = mtk_r32(eth, eth->tx_int_mask_reg);
+- mtk_w32(eth, val | mask, eth->tx_int_mask_reg);
++ val = mtk_r32(eth, eth->soc->reg_map->tx_irq_mask);
++ mtk_w32(eth, val | mask, eth->soc->reg_map->tx_irq_mask);
+ spin_unlock_irqrestore(&eth->tx_irq_lock, flags);
+ }
+
+@@ -641,8 +694,8 @@ static inline void mtk_rx_irq_disable(st
+ u32 val;
+
+ spin_lock_irqsave(&eth->rx_irq_lock, flags);
+- val = mtk_r32(eth, MTK_PDMA_INT_MASK);
+- mtk_w32(eth, val & ~mask, MTK_PDMA_INT_MASK);
++ val = mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask);
++ mtk_w32(eth, val & ~mask, eth->soc->reg_map->pdma.irq_mask);
+ spin_unlock_irqrestore(&eth->rx_irq_lock, flags);
+ }
+
+@@ -652,8 +705,8 @@ static inline void mtk_rx_irq_enable(str
+ u32 val;
+
+ spin_lock_irqsave(&eth->rx_irq_lock, flags);
+- val = mtk_r32(eth, MTK_PDMA_INT_MASK);
+- mtk_w32(eth, val | mask, MTK_PDMA_INT_MASK);
++ val = mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask);
++ mtk_w32(eth, val | mask, eth->soc->reg_map->pdma.irq_mask);
+ spin_unlock_irqrestore(&eth->rx_irq_lock, flags);
+ }
+
+@@ -704,39 +757,39 @@ void mtk_stats_update_mac(struct mtk_mac
+ hw_stats->rx_checksum_errors +=
+ mtk_r32(mac->hw, MT7628_SDM_CS_ERR);
+ } else {
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ unsigned int offs = hw_stats->reg_offset;
+ u64 stats;
+
+- hw_stats->rx_bytes += mtk_r32(mac->hw,
+- MTK_GDM1_RX_GBCNT_L + offs);
+- stats = mtk_r32(mac->hw, MTK_GDM1_RX_GBCNT_H + offs);
++ hw_stats->rx_bytes += mtk_r32(mac->hw, reg_map->gdm1_cnt + offs);
++ stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x4 + offs);
+ if (stats)
+ hw_stats->rx_bytes += (stats << 32);
+ hw_stats->rx_packets +=
+- mtk_r32(mac->hw, MTK_GDM1_RX_GPCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x8 + offs);
+ hw_stats->rx_overflow +=
+- mtk_r32(mac->hw, MTK_GDM1_RX_OERCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x10 + offs);
+ hw_stats->rx_fcs_errors +=
+- mtk_r32(mac->hw, MTK_GDM1_RX_FERCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x14 + offs);
+ hw_stats->rx_short_errors +=
+- mtk_r32(mac->hw, MTK_GDM1_RX_SERCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x18 + offs);
+ hw_stats->rx_long_errors +=
+- mtk_r32(mac->hw, MTK_GDM1_RX_LENCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x1c + offs);
+ hw_stats->rx_checksum_errors +=
+- mtk_r32(mac->hw, MTK_GDM1_RX_CERCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x20 + offs);
+ hw_stats->rx_flow_control_packets +=
+- mtk_r32(mac->hw, MTK_GDM1_RX_FCCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x24 + offs);
+ hw_stats->tx_skip +=
+- mtk_r32(mac->hw, MTK_GDM1_TX_SKIPCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x28 + offs);
+ hw_stats->tx_collisions +=
+- mtk_r32(mac->hw, MTK_GDM1_TX_COLCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x2c + offs);
+ hw_stats->tx_bytes +=
+- mtk_r32(mac->hw, MTK_GDM1_TX_GBCNT_L + offs);
+- stats = mtk_r32(mac->hw, MTK_GDM1_TX_GBCNT_H + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x30 + offs);
++ stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x34 + offs);
+ if (stats)
+ hw_stats->tx_bytes += (stats << 32);
+ hw_stats->tx_packets +=
+- mtk_r32(mac->hw, MTK_GDM1_TX_GPCNT + offs);
++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x38 + offs);
+ }
+
+ u64_stats_update_end(&hw_stats->syncp);
+@@ -876,10 +929,10 @@ static int mtk_init_fq_dma(struct mtk_et
+ txd->txd4 = 0;
+ }
+
+- mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD);
+- mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
+- mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
+- mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
++ mtk_w32(eth, eth->phy_scratch_ring, soc->reg_map->qdma.fq_head);
++ mtk_w32(eth, phy_ring_tail, soc->reg_map->qdma.fq_tail);
++ mtk_w32(eth, (cnt << 16) | cnt, soc->reg_map->qdma.fq_count);
++ mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, soc->reg_map->qdma.fq_blen);
+
+ return 0;
+ }
+@@ -1123,7 +1176,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
+ if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) ||
+ !netdev_xmit_more())
+- mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR);
++ mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr);
+ } else {
+ int next_idx;
+
+@@ -1440,6 +1493,7 @@ rx_done:
+ static int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget,
+ unsigned int *done, unsigned int *bytes)
+ {
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ struct mtk_tx_dma *desc;
+ struct sk_buff *skb;
+@@ -1447,7 +1501,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ u32 cpu, dma;
+
+ cpu = ring->last_free_ptr;
+- dma = mtk_r32(eth, MTK_QTX_DRX_PTR);
++ dma = mtk_r32(eth, reg_map->qdma.drx_ptr);
+
+ desc = mtk_qdma_phys_to_virt(ring, cpu);
+
+@@ -1482,7 +1536,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ }
+
+ ring->last_free_ptr = cpu;
+- mtk_w32(eth, cpu, MTK_QTX_CRX_PTR);
++ mtk_w32(eth, cpu, reg_map->qdma.crx_ptr);
+
+ return budget;
+ }
+@@ -1575,24 +1629,25 @@ static void mtk_handle_status_irq(struct
+ static int mtk_napi_tx(struct napi_struct *napi, int budget)
+ {
+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi);
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ int tx_done = 0;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+ mtk_handle_status_irq(eth);
+- mtk_w32(eth, MTK_TX_DONE_INT, eth->tx_int_status_reg);
++ mtk_w32(eth, MTK_TX_DONE_INT, reg_map->tx_irq_status);
+ tx_done = mtk_poll_tx(eth, budget);
+
+ if (unlikely(netif_msg_intr(eth))) {
+ dev_info(eth->dev,
+ "done tx %d, intr 0x%08x/0x%x\n", tx_done,
+- mtk_r32(eth, eth->tx_int_status_reg),
+- mtk_r32(eth, eth->tx_int_mask_reg));
++ mtk_r32(eth, reg_map->tx_irq_status),
++ mtk_r32(eth, reg_map->tx_irq_mask));
+ }
+
+ if (tx_done == budget)
+ return budget;
+
+- if (mtk_r32(eth, eth->tx_int_status_reg) & MTK_TX_DONE_INT)
++ if (mtk_r32(eth, reg_map->tx_irq_status) & MTK_TX_DONE_INT)
+ return budget;
+
+ if (napi_complete_done(napi, tx_done))
+@@ -1604,6 +1659,7 @@ static int mtk_napi_tx(struct napi_struc
+ static int mtk_napi_rx(struct napi_struct *napi, int budget)
+ {
+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ int rx_done_total = 0;
+
+ mtk_handle_status_irq(eth);
+@@ -1611,21 +1667,21 @@ static int mtk_napi_rx(struct napi_struc
+ do {
+ int rx_done;
+
+- mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_STATUS);
++ mtk_w32(eth, MTK_RX_DONE_INT, reg_map->pdma.irq_status);
+ rx_done = mtk_poll_rx(napi, budget - rx_done_total, eth);
+ rx_done_total += rx_done;
+
+ if (unlikely(netif_msg_intr(eth))) {
+ dev_info(eth->dev,
+ "done rx %d, intr 0x%08x/0x%x\n", rx_done,
+- mtk_r32(eth, MTK_PDMA_INT_STATUS),
+- mtk_r32(eth, MTK_PDMA_INT_MASK));
++ mtk_r32(eth, reg_map->pdma.irq_status),
++ mtk_r32(eth, reg_map->pdma.irq_mask));
+ }
+
+ if (rx_done_total == budget)
+ return budget;
+
+- } while (mtk_r32(eth, MTK_PDMA_INT_STATUS) & MTK_RX_DONE_INT);
++ } while (mtk_r32(eth, reg_map->pdma.irq_status) & MTK_RX_DONE_INT);
+
+ if (napi_complete_done(napi, rx_done_total))
+ mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
+@@ -1688,20 +1744,20 @@ static int mtk_tx_alloc(struct mtk_eth *
+ */
+ wmb();
+
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+- mtk_w32(eth, ring->phys, MTK_QTX_CTX_PTR);
+- mtk_w32(eth, ring->phys, MTK_QTX_DTX_PTR);
++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
++ 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),
+- MTK_QTX_CRX_PTR);
+- mtk_w32(eth, ring->last_free_ptr, MTK_QTX_DRX_PTR);
++ 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,
+- MTK_QTX_CFG(0));
++ 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, 0, MT7628_TX_CTX_IDX0);
+- mtk_w32(eth, MT7628_PST_DTX_IDX0, MTK_PDMA_RST_IDX);
++ mtk_w32(eth, MT7628_PST_DTX_IDX0, soc->reg_map->pdma.rst_idx);
+ }
+
+ return 0;
+@@ -1740,6 +1796,7 @@ static void mtk_tx_clean(struct mtk_eth
+
+ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
+ {
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ struct mtk_rx_ring *ring;
+ int rx_data_len, rx_dma_size;
+ int i;
+@@ -1808,16 +1865,18 @@ static int mtk_rx_alloc(struct mtk_eth *
+ ring->dma_size = rx_dma_size;
+ ring->calc_idx_update = false;
+ ring->calc_idx = rx_dma_size - 1;
+- ring->crx_idx_reg = MTK_PRX_CRX_IDX_CFG(ring_no);
++ ring->crx_idx_reg = reg_map->pdma.pcrx_ptr + ring_no * MTK_QRX_OFFSET;
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+- mtk_w32(eth, ring->phys, MTK_PRX_BASE_PTR_CFG(ring_no) + offset);
+- mtk_w32(eth, rx_dma_size, MTK_PRX_MAX_CNT_CFG(ring_no) + offset);
++ mtk_w32(eth, ring->phys,
++ reg_map->pdma.rx_ptr + ring_no * MTK_QRX_OFFSET + offset);
++ mtk_w32(eth, rx_dma_size,
++ reg_map->pdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET + offset);
+ mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg + offset);
+- mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), MTK_PDMA_RST_IDX + offset);
++ mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), reg_map->pdma.rst_idx + offset);
+
+ return 0;
+ }
+@@ -2126,9 +2185,9 @@ static int mtk_dma_busy_wait(struct mtk_
+ u32 val;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+- reg = MTK_QDMA_GLO_CFG;
++ reg = eth->soc->reg_map->qdma.glo_cfg;
+ else
+- reg = MTK_PDMA_GLO_CFG;
++ reg = eth->soc->reg_map->pdma.glo_cfg;
+
+ ret = readx_poll_timeout_atomic(__raw_readl, eth->base + reg, val,
+ !(val & (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY)),
+@@ -2186,8 +2245,8 @@ static int mtk_dma_init(struct mtk_eth *
+ * automatically
+ */
+ mtk_w32(eth, FC_THRES_DROP_MODE | FC_THRES_DROP_EN |
+- FC_THRES_MIN, MTK_QDMA_FC_THRES);
+- mtk_w32(eth, 0x0, MTK_QDMA_HRED2);
++ FC_THRES_MIN, eth->soc->reg_map->qdma.fc_th);
++ mtk_w32(eth, 0x0, eth->soc->reg_map->qdma.hred);
+ }
+
+ return 0;
+@@ -2261,13 +2320,14 @@ static irqreturn_t mtk_handle_irq_tx(int
+ static irqreturn_t mtk_handle_irq(int irq, void *_eth)
+ {
+ struct mtk_eth *eth = _eth;
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+
+- if (mtk_r32(eth, MTK_PDMA_INT_MASK) & MTK_RX_DONE_INT) {
+- if (mtk_r32(eth, MTK_PDMA_INT_STATUS) & MTK_RX_DONE_INT)
++ if (mtk_r32(eth, reg_map->pdma.irq_mask) & MTK_RX_DONE_INT) {
++ if (mtk_r32(eth, reg_map->pdma.irq_status) & MTK_RX_DONE_INT)
+ mtk_handle_irq_rx(irq, _eth);
+ }
+- if (mtk_r32(eth, eth->tx_int_mask_reg) & MTK_TX_DONE_INT) {
+- if (mtk_r32(eth, eth->tx_int_status_reg) & MTK_TX_DONE_INT)
++ if (mtk_r32(eth, reg_map->tx_irq_mask) & MTK_TX_DONE_INT) {
++ if (mtk_r32(eth, reg_map->tx_irq_status) & MTK_TX_DONE_INT)
+ mtk_handle_irq_tx(irq, _eth);
+ }
+
+@@ -2291,6 +2351,7 @@ static void mtk_poll_controller(struct n
+ static int mtk_start_dma(struct mtk_eth *eth)
+ {
+ u32 rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0;
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ int err;
+
+ err = mtk_dma_init(eth);
+@@ -2305,16 +2366,15 @@ static int mtk_start_dma(struct mtk_eth
+ MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO |
+ MTK_RX_DMA_EN | MTK_RX_2B_OFFSET |
+ MTK_RX_BT_32DWORDS,
+- MTK_QDMA_GLO_CFG);
+-
++ reg_map->qdma.glo_cfg);
+ mtk_w32(eth,
+ MTK_RX_DMA_EN | rx_2b_offset |
+ MTK_RX_BT_32DWORDS | MTK_MULTI_EN,
+- MTK_PDMA_GLO_CFG);
++ reg_map->pdma.glo_cfg);
+ } else {
+ mtk_w32(eth, MTK_TX_WB_DDONE | MTK_TX_DMA_EN | MTK_RX_DMA_EN |
+ MTK_MULTI_EN | MTK_PDMA_SIZE_8DWORDS,
+- MTK_PDMA_GLO_CFG);
++ reg_map->pdma.glo_cfg);
+ }
+
+ return 0;
+@@ -2440,8 +2500,8 @@ static int mtk_stop(struct net_device *d
+ cancel_work_sync(&eth->tx_dim.work);
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+- mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
+- mtk_stop_dma(eth, MTK_PDMA_GLO_CFG);
++ mtk_stop_dma(eth, eth->soc->reg_map->qdma.glo_cfg);
++ mtk_stop_dma(eth, eth->soc->reg_map->pdma.glo_cfg);
+
+ mtk_dma_free(eth);
+
+@@ -2495,6 +2555,7 @@ static void mtk_dim_rx(struct work_struc
+ {
+ struct dim *dim = container_of(work, struct dim, work);
+ struct mtk_eth *eth = container_of(dim, struct mtk_eth, rx_dim);
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ struct dim_cq_moder cur_profile;
+ u32 val, cur;
+
+@@ -2502,7 +2563,7 @@ static void mtk_dim_rx(struct work_struc
+ dim->profile_ix);
+ spin_lock_bh(&eth->dim_lock);
+
+- val = mtk_r32(eth, MTK_PDMA_DELAY_INT);
++ val = mtk_r32(eth, reg_map->pdma.delay_irq);
+ val &= MTK_PDMA_DELAY_TX_MASK;
+ val |= MTK_PDMA_DELAY_RX_EN;
+
+@@ -2512,9 +2573,9 @@ static void mtk_dim_rx(struct work_struc
+ cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK);
+ val |= cur << MTK_PDMA_DELAY_RX_PINT_SHIFT;
+
+- mtk_w32(eth, val, MTK_PDMA_DELAY_INT);
++ mtk_w32(eth, val, reg_map->pdma.delay_irq);
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+- mtk_w32(eth, val, MTK_QDMA_DELAY_INT);
++ mtk_w32(eth, val, reg_map->qdma.delay_irq);
+
+ spin_unlock_bh(&eth->dim_lock);
+
+@@ -2525,6 +2586,7 @@ static void mtk_dim_tx(struct work_struc
+ {
+ struct dim *dim = container_of(work, struct dim, work);
+ struct mtk_eth *eth = container_of(dim, struct mtk_eth, tx_dim);
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ struct dim_cq_moder cur_profile;
+ u32 val, cur;
+
+@@ -2532,7 +2594,7 @@ static void mtk_dim_tx(struct work_struc
+ dim->profile_ix);
+ spin_lock_bh(&eth->dim_lock);
+
+- val = mtk_r32(eth, MTK_PDMA_DELAY_INT);
++ val = mtk_r32(eth, reg_map->pdma.delay_irq);
+ val &= MTK_PDMA_DELAY_RX_MASK;
+ val |= MTK_PDMA_DELAY_TX_EN;
+
+@@ -2542,9 +2604,9 @@ static void mtk_dim_tx(struct work_struc
+ cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK);
+ val |= cur << MTK_PDMA_DELAY_TX_PINT_SHIFT;
+
+- mtk_w32(eth, val, MTK_PDMA_DELAY_INT);
++ mtk_w32(eth, val, reg_map->pdma.delay_irq);
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+- mtk_w32(eth, val, MTK_QDMA_DELAY_INT);
++ mtk_w32(eth, val, reg_map->qdma.delay_irq);
+
+ spin_unlock_bh(&eth->dim_lock);
+
+@@ -2555,6 +2617,7 @@ static int mtk_hw_init(struct mtk_eth *e
+ {
+ u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
+ ETHSYS_DMA_AG_MAP_PPE;
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ int i, val, ret;
+
+ if (test_and_set_bit(MTK_HW_INIT, &eth->state))
+@@ -2629,10 +2692,10 @@ static int mtk_hw_init(struct mtk_eth *e
+ mtk_rx_irq_disable(eth, ~0);
+
+ /* FE int grouping */
+- mtk_w32(eth, MTK_TX_DONE_INT, MTK_PDMA_INT_GRP1);
+- mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_GRP2);
+- mtk_w32(eth, MTK_TX_DONE_INT, MTK_QDMA_INT_GRP1);
+- mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2);
++ mtk_w32(eth, MTK_TX_DONE_INT, reg_map->pdma.int_grp);
++ mtk_w32(eth, MTK_RX_DONE_INT, reg_map->pdma.int_grp + 4);
++ mtk_w32(eth, MTK_TX_DONE_INT, reg_map->qdma.int_grp);
++ mtk_w32(eth, MTK_RX_DONE_INT, reg_map->qdma.int_grp + 4);
+ mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
+
+ return 0;
+@@ -3171,14 +3234,6 @@ static int mtk_probe(struct platform_dev
+ if (IS_ERR(eth->base))
+ return PTR_ERR(eth->base);
+
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+- eth->tx_int_mask_reg = MTK_QDMA_INT_MASK;
+- eth->tx_int_status_reg = MTK_QDMA_INT_STATUS;
+- } else {
+- eth->tx_int_mask_reg = MTK_PDMA_INT_MASK;
+- eth->tx_int_status_reg = MTK_PDMA_INT_STATUS;
+- }
+-
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
+ eth->rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA;
+ eth->ip_align = NET_IP_ALIGN;
+@@ -3412,6 +3467,7 @@ static int mtk_remove(struct platform_de
+ }
+
+ static const struct mtk_soc_data mt2701_data = {
++ .reg_map = &mtk_reg_map,
+ .caps = MT7623_CAPS | MTK_HWLRO,
+ .hw_features = MTK_HW_FEATURES,
+ .required_clks = MT7623_CLKS_BITMAP,
+@@ -3423,6 +3479,7 @@ static const struct mtk_soc_data mt2701_
+ };
+
+ static const struct mtk_soc_data mt7621_data = {
++ .reg_map = &mtk_reg_map,
+ .caps = MT7621_CAPS,
+ .hw_features = MTK_HW_FEATURES,
+ .required_clks = MT7621_CLKS_BITMAP,
+@@ -3435,6 +3492,7 @@ static const struct mtk_soc_data mt7621_
+ };
+
+ static const struct mtk_soc_data mt7622_data = {
++ .reg_map = &mtk_reg_map,
+ .ana_rgc3 = 0x2028,
+ .caps = MT7622_CAPS | MTK_HWLRO,
+ .hw_features = MTK_HW_FEATURES,
+@@ -3448,6 +3506,7 @@ static const struct mtk_soc_data mt7622_
+ };
+
+ static const struct mtk_soc_data mt7623_data = {
++ .reg_map = &mtk_reg_map,
+ .caps = MT7623_CAPS | MTK_HWLRO,
+ .hw_features = MTK_HW_FEATURES,
+ .required_clks = MT7623_CLKS_BITMAP,
+@@ -3460,6 +3519,7 @@ static const struct mtk_soc_data mt7623_
+ };
+
+ static const struct mtk_soc_data mt7629_data = {
++ .reg_map = &mtk_reg_map,
+ .ana_rgc3 = 0x128,
+ .caps = MT7629_CAPS | MTK_HWLRO,
+ .hw_features = MTK_HW_FEATURES,
+@@ -3472,6 +3532,7 @@ static const struct mtk_soc_data mt7629_
+ };
+
+ static const struct mtk_soc_data rt5350_data = {
++ .reg_map = &mt7628_reg_map,
+ .caps = MT7628_CAPS,
+ .hw_features = MTK_HW_FEATURES_MT7628,
+ .required_clks = MT7628_CLKS_BITMAP,
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -48,6 +48,8 @@
+ #define MTK_HW_FEATURES_MT7628 (NETIF_F_SG | NETIF_F_RXCSUM)
+ #define NEXT_DESP_IDX(X, Y) (((X) + 1) & ((Y) - 1))
+
++#define MTK_QRX_OFFSET 0x10
++
+ #define MTK_MAX_RX_RING_NUM 4
+ #define MTK_HW_LRO_DMA_SIZE 8
+
+@@ -100,18 +102,6 @@
+ /* Unicast Filter MAC Address Register - High */
+ #define MTK_GDMA_MAC_ADRH(x) (0x50C + (x * 0x1000))
+
+-/* PDMA RX Base Pointer Register */
+-#define MTK_PRX_BASE_PTR0 0x900
+-#define MTK_PRX_BASE_PTR_CFG(x) (MTK_PRX_BASE_PTR0 + (x * 0x10))
+-
+-/* PDMA RX Maximum Count Register */
+-#define MTK_PRX_MAX_CNT0 0x904
+-#define MTK_PRX_MAX_CNT_CFG(x) (MTK_PRX_MAX_CNT0 + (x * 0x10))
+-
+-/* PDMA RX CPU Pointer Register */
+-#define MTK_PRX_CRX_IDX0 0x908
+-#define MTK_PRX_CRX_IDX_CFG(x) (MTK_PRX_CRX_IDX0 + (x * 0x10))
+-
+ /* PDMA HW LRO Control Registers */
+ #define MTK_PDMA_LRO_CTRL_DW0 0x980
+ #define MTK_LRO_EN BIT(0)
+@@ -126,18 +116,19 @@
+ #define MTK_ADMA_MODE BIT(15)
+ #define MTK_LRO_MIN_RXD_SDL (MTK_HW_LRO_SDL_REMAIN_ROOM << 16)
+
+-/* PDMA Global Configuration Register */
+-#define MTK_PDMA_GLO_CFG 0xa04
++#define MTK_RX_DMA_LRO_EN BIT(8)
+ #define MTK_MULTI_EN BIT(10)
+ #define MTK_PDMA_SIZE_8DWORDS (1 << 4)
+
++/* PDMA Global Configuration Register */
++#define MTK_PDMA_LRO_SDL 0x3000
++#define MTK_RX_CFG_SDL_OFFSET 16
++
+ /* PDMA Reset Index Register */
+-#define MTK_PDMA_RST_IDX 0xa08
+ #define MTK_PST_DRX_IDX0 BIT(16)
+ #define MTK_PST_DRX_IDX_CFG(x) (MTK_PST_DRX_IDX0 << (x))
+
+ /* PDMA Delay Interrupt Register */
+-#define MTK_PDMA_DELAY_INT 0xa0c
+ #define MTK_PDMA_DELAY_RX_MASK GENMASK(15, 0)
+ #define MTK_PDMA_DELAY_RX_EN BIT(15)
+ #define MTK_PDMA_DELAY_RX_PINT_SHIFT 8
+@@ -151,19 +142,9 @@
+ #define MTK_PDMA_DELAY_PINT_MASK 0x7f
+ #define MTK_PDMA_DELAY_PTIME_MASK 0xff
+
+-/* PDMA Interrupt Status Register */
+-#define MTK_PDMA_INT_STATUS 0xa20
+-
+-/* PDMA Interrupt Mask Register */
+-#define MTK_PDMA_INT_MASK 0xa28
+-
+ /* PDMA HW LRO Alter Flow Delta Register */
+ #define MTK_PDMA_LRO_ALT_SCORE_DELTA 0xa4c
+
+-/* PDMA Interrupt grouping registers */
+-#define MTK_PDMA_INT_GRP1 0xa50
+-#define MTK_PDMA_INT_GRP2 0xa54
+-
+ /* PDMA HW LRO IP Setting Registers */
+ #define MTK_LRO_RX_RING0_DIP_DW0 0xb04
+ #define MTK_LRO_DIP_DW0_CFG(x) (MTK_LRO_RX_RING0_DIP_DW0 + (x * 0x40))
+@@ -185,26 +166,9 @@
+ #define MTK_RING_MAX_AGG_CNT_H ((MTK_HW_LRO_MAX_AGG_CNT >> 6) & 0x3)
+
+ /* QDMA TX Queue Configuration Registers */
+-#define MTK_QTX_CFG(x) (0x1800 + (x * 0x10))
+ #define QDMA_RES_THRES 4
+
+-/* QDMA TX Queue Scheduler Registers */
+-#define MTK_QTX_SCH(x) (0x1804 + (x * 0x10))
+-
+-/* QDMA RX Base Pointer Register */
+-#define MTK_QRX_BASE_PTR0 0x1900
+-
+-/* QDMA RX Maximum Count Register */
+-#define MTK_QRX_MAX_CNT0 0x1904
+-
+-/* QDMA RX CPU Pointer Register */
+-#define MTK_QRX_CRX_IDX0 0x1908
+-
+-/* QDMA RX DMA Pointer Register */
+-#define MTK_QRX_DRX_IDX0 0x190C
+-
+ /* QDMA Global Configuration Register */
+-#define MTK_QDMA_GLO_CFG 0x1A04
+ #define MTK_RX_2B_OFFSET BIT(31)
+ #define MTK_RX_BT_32DWORDS (3 << 11)
+ #define MTK_NDP_CO_PRO BIT(10)
+@@ -216,20 +180,12 @@
+ #define MTK_TX_DMA_EN BIT(0)
+ #define MTK_DMA_BUSY_TIMEOUT_US 1000000
+
+-/* QDMA Reset Index Register */
+-#define MTK_QDMA_RST_IDX 0x1A08
+-
+-/* QDMA Delay Interrupt Register */
+-#define MTK_QDMA_DELAY_INT 0x1A0C
+-
+ /* QDMA Flow Control Register */
+-#define MTK_QDMA_FC_THRES 0x1A10
+ #define FC_THRES_DROP_MODE BIT(20)
+ #define FC_THRES_DROP_EN (7 << 16)
+ #define FC_THRES_MIN 0x4444
+
+ /* QDMA Interrupt Status Register */
+-#define MTK_QDMA_INT_STATUS 0x1A18
+ #define MTK_RX_DONE_DLY BIT(30)
+ #define MTK_TX_DONE_DLY BIT(28)
+ #define MTK_RX_DONE_INT3 BIT(19)
+@@ -244,55 +200,8 @@
+ #define MTK_TX_DONE_INT MTK_TX_DONE_DLY
+
+ /* QDMA Interrupt grouping registers */
+-#define MTK_QDMA_INT_GRP1 0x1a20
+-#define MTK_QDMA_INT_GRP2 0x1a24
+ #define MTK_RLS_DONE_INT BIT(0)
+
+-/* QDMA Interrupt Status Register */
+-#define MTK_QDMA_INT_MASK 0x1A1C
+-
+-/* QDMA Interrupt Mask Register */
+-#define MTK_QDMA_HRED2 0x1A44
+-
+-/* QDMA TX Forward CPU Pointer Register */
+-#define MTK_QTX_CTX_PTR 0x1B00
+-
+-/* QDMA TX Forward DMA Pointer Register */
+-#define MTK_QTX_DTX_PTR 0x1B04
+-
+-/* QDMA TX Release CPU Pointer Register */
+-#define MTK_QTX_CRX_PTR 0x1B10
+-
+-/* QDMA TX Release DMA Pointer Register */
+-#define MTK_QTX_DRX_PTR 0x1B14
+-
+-/* QDMA FQ Head Pointer Register */
+-#define MTK_QDMA_FQ_HEAD 0x1B20
+-
+-/* QDMA FQ Head Pointer Register */
+-#define MTK_QDMA_FQ_TAIL 0x1B24
+-
+-/* QDMA FQ Free Page Counter Register */
+-#define MTK_QDMA_FQ_CNT 0x1B28
+-
+-/* QDMA FQ Free Page Buffer Length Register */
+-#define MTK_QDMA_FQ_BLEN 0x1B2C
+-
+-/* GMA1 counter / statics register */
+-#define MTK_GDM1_RX_GBCNT_L 0x2400
+-#define MTK_GDM1_RX_GBCNT_H 0x2404
+-#define MTK_GDM1_RX_GPCNT 0x2408
+-#define MTK_GDM1_RX_OERCNT 0x2410
+-#define MTK_GDM1_RX_FERCNT 0x2414
+-#define MTK_GDM1_RX_SERCNT 0x2418
+-#define MTK_GDM1_RX_LENCNT 0x241c
+-#define MTK_GDM1_RX_CERCNT 0x2420
+-#define MTK_GDM1_RX_FCCNT 0x2424
+-#define MTK_GDM1_TX_SKIPCNT 0x2428
+-#define MTK_GDM1_TX_COLCNT 0x242c
+-#define MTK_GDM1_TX_GBCNT_L 0x2430
+-#define MTK_GDM1_TX_GBCNT_H 0x2434
+-#define MTK_GDM1_TX_GPCNT 0x2438
+ #define MTK_STAT_OFFSET 0x40
+
+ #define MTK_WDMA0_BASE 0x2800
+@@ -854,8 +763,46 @@ struct mtk_tx_dma_desc_info {
+ u8 last:1;
+ };
+
++struct mtk_reg_map {
++ u32 tx_irq_mask;
++ u32 tx_irq_status;
++ struct {
++ u32 rx_ptr; /* rx base pointer */
++ u32 rx_cnt_cfg; /* rx max count configuration */
++ u32 pcrx_ptr; /* rx cpu pointer */
++ u32 glo_cfg; /* global configuration */
++ u32 rst_idx; /* reset index */
++ u32 delay_irq; /* delay interrupt */
++ u32 irq_status; /* interrupt status */
++ u32 irq_mask; /* interrupt mask */
++ u32 int_grp;
++ } pdma;
++ struct {
++ u32 qtx_cfg; /* tx queue configuration */
++ u32 rx_ptr; /* rx base pointer */
++ u32 rx_cnt_cfg; /* rx max count configuration */
++ u32 qcrx_ptr; /* rx cpu pointer */
++ u32 glo_cfg; /* global configuration */
++ u32 rst_idx; /* reset index */
++ u32 delay_irq; /* delay interrupt */
++ u32 fc_th; /* flow control */
++ u32 int_grp;
++ u32 hred; /* interrupt mask */
++ u32 ctx_ptr; /* tx acquire cpu pointer */
++ u32 dtx_ptr; /* tx acquire dma pointer */
++ u32 crx_ptr; /* tx release cpu pointer */
++ u32 drx_ptr; /* tx release dma pointer */
++ u32 fq_head; /* fq head pointer */
++ u32 fq_tail; /* fq tail pointer */
++ u32 fq_count; /* fq free page count */
++ u32 fq_blen; /* fq free page buffer length */
++ } qdma;
++ u32 gdm1_cnt;
++};
++
+ /* struct mtk_eth_data - This is the structure holding all differences
+ * among various plaforms
++ * @reg_map Soc register map.
+ * @ana_rgc3: The offset for register ANA_RGC3 related to
+ * sgmiisys syscon
+ * @caps Flags shown the extra capability for the SoC
+@@ -868,6 +815,7 @@ struct mtk_tx_dma_desc_info {
+ * @rxd_size Rx DMA descriptor size.
+ */
+ struct mtk_soc_data {
++ const struct mtk_reg_map *reg_map;
+ u32 ana_rgc3;
+ u32 caps;
+ u32 required_clks;
+@@ -995,8 +943,6 @@ struct mtk_eth {
+ u32 tx_bytes;
+ struct dim tx_dim;
+
+- u32 tx_int_mask_reg;
+- u32 tx_int_status_reg;
+ u32 rx_dma_l4_valid;
+ int ip_align;
+
diff --git a/target/linux/generic/backport-6.1/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch b/target/linux/generic/backport-6.1/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch
new file mode 100644
index 0000000000..4d6c94b13b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch
@@ -0,0 +1,917 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:36 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce MTK_NETSYS_V2 support
+
+Introduce MTK_NETSYS_V2 support. MTK_NETSYS_V2 defines 32B TX/RX DMA
+descriptors.
+This is a preliminary patch to add mt7986 ethernet support.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -863,8 +863,8 @@ static inline int mtk_max_buf_size(int f
+ return buf_size;
+ }
+
+-static inline bool mtk_rx_get_desc(struct mtk_rx_dma *rxd,
+- struct mtk_rx_dma *dma_rxd)
++static bool mtk_rx_get_desc(struct mtk_eth *eth, struct mtk_rx_dma_v2 *rxd,
++ struct mtk_rx_dma_v2 *dma_rxd)
+ {
+ rxd->rxd2 = READ_ONCE(dma_rxd->rxd2);
+ if (!(rxd->rxd2 & RX_DMA_DONE))
+@@ -873,6 +873,10 @@ static inline bool mtk_rx_get_desc(struc
+ rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
+ rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
+ rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ rxd->rxd5 = READ_ONCE(dma_rxd->rxd5);
++ rxd->rxd6 = READ_ONCE(dma_rxd->rxd6);
++ }
+
+ return true;
+ }
+@@ -917,7 +921,7 @@ static int mtk_init_fq_dma(struct mtk_et
+ phy_ring_tail = eth->phy_scratch_ring + soc->txrx.txd_size * (cnt - 1);
+
+ for (i = 0; i < cnt; i++) {
+- struct mtk_tx_dma *txd;
++ struct mtk_tx_dma_v2 *txd;
+
+ txd = (void *)eth->scratch_ring + i * soc->txrx.txd_size;
+ txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
+@@ -927,6 +931,12 @@ static int mtk_init_fq_dma(struct mtk_et
+
+ txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
+ txd->txd4 = 0;
++ if (MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V2)) {
++ txd->txd5 = 0;
++ txd->txd6 = 0;
++ txd->txd7 = 0;
++ txd->txd8 = 0;
++ }
+ }
+
+ mtk_w32(eth, eth->phy_scratch_ring, soc->reg_map->qdma.fq_head);
+@@ -1030,10 +1040,12 @@ static void setup_tx_buf(struct mtk_eth
+ }
+ }
+
+-static void mtk_tx_set_dma_desc(struct net_device *dev, struct mtk_tx_dma *desc,
+- struct mtk_tx_dma_desc_info *info)
++static void mtk_tx_set_dma_desc_v1(struct net_device *dev, void *txd,
++ struct mtk_tx_dma_desc_info *info)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_eth *eth = mac->hw;
++ struct mtk_tx_dma *desc = txd;
+ u32 data;
+
+ WRITE_ONCE(desc->txd1, info->addr);
+@@ -1057,6 +1069,59 @@ static void mtk_tx_set_dma_desc(struct n
+ WRITE_ONCE(desc->txd4, data);
+ }
+
++static void mtk_tx_set_dma_desc_v2(struct net_device *dev, void *txd,
++ struct mtk_tx_dma_desc_info *info)
++{
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_tx_dma_v2 *desc = txd;
++ struct mtk_eth *eth = mac->hw;
++ u32 data;
++
++ WRITE_ONCE(desc->txd1, info->addr);
++
++ data = TX_DMA_PLEN0(info->size);
++ if (info->last)
++ 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);
++
++ data = 0;
++ if (info->first) {
++ if (info->gso)
++ data |= TX_DMA_TSO_V2;
++ /* tx checksum offload */
++ if (info->csum)
++ data |= TX_DMA_CHKSUM_V2;
++ }
++ WRITE_ONCE(desc->txd5, data);
++
++ data = 0;
++ if (info->first && info->vlan)
++ data |= TX_DMA_INS_VLAN_V2 | info->vlan_tci;
++ WRITE_ONCE(desc->txd6, data);
++
++ WRITE_ONCE(desc->txd7, 0);
++ WRITE_ONCE(desc->txd8, 0);
++}
++
++static void mtk_tx_set_dma_desc(struct net_device *dev, void *txd,
++ struct mtk_tx_dma_desc_info *info)
++{
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_eth *eth = mac->hw;
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ mtk_tx_set_dma_desc_v2(dev, txd, info);
++ else
++ mtk_tx_set_dma_desc_v1(dev, txd, info);
++}
++
+ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ int tx_num, struct mtk_tx_ring *ring, bool gso)
+ {
+@@ -1065,6 +1130,7 @@ 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,
+ .vlan_tci = skb_vlan_tag_get(skb),
+ .first = true,
+ .last = !skb_is_nonlinear(skb),
+@@ -1124,7 +1190,9 @@ static int mtk_tx_map(struct sk_buff *sk
+ }
+
+ memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
+- txd_info.size = min(frag_size, MTK_TX_DMA_BUF_LEN);
++ 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.last = i == skb_shinfo(skb)->nr_frags - 1 &&
+ !(frag_size - txd_info.size);
+ txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag,
+@@ -1205,17 +1273,16 @@ err_dma:
+ return -ENOMEM;
+ }
+
+-static inline int mtk_cal_txd_req(struct sk_buff *skb)
++static int mtk_cal_txd_req(struct mtk_eth *eth, struct sk_buff *skb)
+ {
+- int i, nfrags;
++ int i, nfrags = 1;
+ skb_frag_t *frag;
+
+- nfrags = 1;
+ if (skb_is_gso(skb)) {
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ nfrags += DIV_ROUND_UP(skb_frag_size(frag),
+- MTK_TX_DMA_BUF_LEN);
++ eth->soc->txrx.dma_max_len);
+ }
+ } else {
+ nfrags += skb_shinfo(skb)->nr_frags;
+@@ -1267,7 +1334,7 @@ static netdev_tx_t mtk_start_xmit(struct
+ if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
+ goto drop;
+
+- tx_num = mtk_cal_txd_req(skb);
++ tx_num = mtk_cal_txd_req(eth, skb);
+ if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
+ netif_stop_queue(dev);
+ netif_err(eth, tx_queued, dev,
+@@ -1359,7 +1426,7 @@ static int mtk_poll_rx(struct napi_struc
+ int idx;
+ struct sk_buff *skb;
+ u8 *data, *new_data;
+- struct mtk_rx_dma *rxd, trxd;
++ struct mtk_rx_dma_v2 *rxd, trxd;
+ int done = 0, bytes = 0;
+
+ while (done < budget) {
+@@ -1367,7 +1434,7 @@ static int mtk_poll_rx(struct napi_struc
+ unsigned int pktlen;
+ dma_addr_t dma_addr;
+ u32 hash, reason;
+- int mac;
++ int mac = 0;
+
+ ring = mtk_get_rx_ring(eth);
+ if (unlikely(!ring))
+@@ -1377,16 +1444,15 @@ static int mtk_poll_rx(struct napi_struc
+ rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size;
+ data = ring->data[idx];
+
+- if (!mtk_rx_get_desc(&trxd, rxd))
++ if (!mtk_rx_get_desc(eth, &trxd, rxd))
+ break;
+
+ /* find out which mac the packet come from. values start at 1 */
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) ||
+- (trxd.rxd4 & RX_DMA_SPECIAL_TAG))
+- mac = 0;
+- else
+- mac = ((trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
+- RX_DMA_FPORT_MASK) - 1;
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ mac = RX_DMA_GET_SPORT_V2(trxd.rxd5) - 1;
++ else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
++ !(trxd.rxd4 & RX_DMA_SPECIAL_TAG))
++ mac = RX_DMA_GET_SPORT(trxd.rxd4) - 1;
+
+ if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
+ !eth->netdev[mac]))
+@@ -1432,7 +1498,7 @@ static int mtk_poll_rx(struct napi_struc
+ pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
+ skb->dev = netdev;
+ skb_put(skb, pktlen);
+- if (trxd.rxd4 & eth->rx_dma_l4_valid)
++ if (trxd.rxd4 & eth->soc->txrx.rx_dma_l4_valid)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb_checksum_none_assert(skb);
+@@ -1450,10 +1516,25 @@ static int mtk_poll_rx(struct napi_struc
+ mtk_ppe_check_skb(eth->ppe, skb,
+ trxd.rxd4 & MTK_RXD4_FOE_ENTRY);
+
+- if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
+- (trxd.rxd2 & RX_DMA_VTAG))
+- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+- RX_DMA_VID(trxd.rxd3));
++ 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(ETH_P_8021Q),
++ RX_DMA_VID(trxd.rxd3));
++ }
++
++ /* 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);
++ }
++
+ skb_record_rx_queue(skb, 0);
+ napi_gro_receive(napi, skb);
+
+@@ -1465,7 +1546,7 @@ release_desc:
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+ rxd->rxd2 = RX_DMA_LSO;
+ else
+- rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size);
++ rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size);
+
+ ring->calc_idx = idx;
+
+@@ -1667,7 +1748,8 @@ static int mtk_napi_rx(struct napi_struc
+ do {
+ int rx_done;
+
+- mtk_w32(eth, MTK_RX_DONE_INT, reg_map->pdma.irq_status);
++ mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask,
++ reg_map->pdma.irq_status);
+ rx_done = mtk_poll_rx(napi, budget - rx_done_total, eth);
+ rx_done_total += rx_done;
+
+@@ -1681,10 +1763,11 @@ static int mtk_napi_rx(struct napi_struc
+ if (rx_done_total == budget)
+ return budget;
+
+- } while (mtk_r32(eth, reg_map->pdma.irq_status) & MTK_RX_DONE_INT);
++ } while (mtk_r32(eth, reg_map->pdma.irq_status) &
++ eth->soc->txrx.rx_irq_done_mask);
+
+ if (napi_complete_done(napi, rx_done_total))
+- mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
++ mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
+
+ return rx_done_total;
+ }
+@@ -1694,7 +1777,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ const struct mtk_soc_data *soc = eth->soc;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+ int i, sz = soc->txrx.txd_size;
+- struct mtk_tx_dma *txd;
++ struct mtk_tx_dma_v2 *txd;
+
+ ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf),
+ GFP_KERNEL);
+@@ -1714,13 +1797,19 @@ static int mtk_tx_alloc(struct mtk_eth *
+ txd->txd2 = next_ptr;
+ txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
+ txd->txd4 = 0;
++ if (MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V2)) {
++ txd->txd5 = 0;
++ txd->txd6 = 0;
++ txd->txd7 = 0;
++ txd->txd8 = 0;
++ }
+ }
+
+ /* On MT7688 (PDMA only) this driver uses the ring->dma structs
+ * only as the framework. The real HW descriptors are the PDMA
+ * descriptors in ring->dma_pdma.
+ */
+- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
+ ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
+ &ring->phys_pdma, GFP_KERNEL);
+ if (!ring->dma_pdma)
+@@ -1800,13 +1889,11 @@ static int mtk_rx_alloc(struct mtk_eth *
+ struct mtk_rx_ring *ring;
+ int rx_data_len, rx_dma_size;
+ int i;
+- u32 offset = 0;
+
+ if (rx_flag == MTK_RX_FLAGS_QDMA) {
+ if (ring_no)
+ return -EINVAL;
+ ring = &eth->rx_ring_qdma;
+- offset = 0x1000;
+ } else {
+ ring = &eth->rx_ring[ring_no];
+ }
+@@ -1842,7 +1929,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+ return -ENOMEM;
+
+ for (i = 0; i < rx_dma_size; i++) {
+- struct mtk_rx_dma *rxd;
++ struct mtk_rx_dma_v2 *rxd;
+
+ dma_addr_t dma_addr = dma_map_single(eth->dma_dev,
+ ring->data[i] + NET_SKB_PAD + eth->ip_align,
+@@ -1857,26 +1944,47 @@ static int mtk_rx_alloc(struct mtk_eth *
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+ rxd->rxd2 = RX_DMA_LSO;
+ else
+- rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size);
++ rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size);
+
+ rxd->rxd3 = 0;
+ rxd->rxd4 = 0;
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ rxd->rxd5 = 0;
++ rxd->rxd6 = 0;
++ rxd->rxd7 = 0;
++ rxd->rxd8 = 0;
++ }
+ }
+ ring->dma_size = rx_dma_size;
+ ring->calc_idx_update = false;
+ ring->calc_idx = rx_dma_size - 1;
+- ring->crx_idx_reg = reg_map->pdma.pcrx_ptr + ring_no * MTK_QRX_OFFSET;
++ if (rx_flag == MTK_RX_FLAGS_QDMA)
++ ring->crx_idx_reg = reg_map->qdma.qcrx_ptr +
++ ring_no * MTK_QRX_OFFSET;
++ else
++ ring->crx_idx_reg = reg_map->pdma.pcrx_ptr +
++ ring_no * MTK_QRX_OFFSET;
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+- mtk_w32(eth, ring->phys,
+- reg_map->pdma.rx_ptr + ring_no * MTK_QRX_OFFSET + offset);
+- mtk_w32(eth, rx_dma_size,
+- reg_map->pdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET + offset);
+- mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg + offset);
+- mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), reg_map->pdma.rst_idx + offset);
++ if (rx_flag == MTK_RX_FLAGS_QDMA) {
++ mtk_w32(eth, ring->phys,
++ reg_map->qdma.rx_ptr + ring_no * MTK_QRX_OFFSET);
++ mtk_w32(eth, rx_dma_size,
++ reg_map->qdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET);
++ mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no),
++ reg_map->qdma.rst_idx);
++ } else {
++ mtk_w32(eth, ring->phys,
++ reg_map->pdma.rx_ptr + ring_no * MTK_QRX_OFFSET);
++ mtk_w32(eth, rx_dma_size,
++ reg_map->pdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET);
++ mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no),
++ reg_map->pdma.rst_idx);
++ }
++ mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg);
+
+ return 0;
+ }
+@@ -2298,7 +2406,7 @@ static irqreturn_t mtk_handle_irq_rx(int
+ eth->rx_events++;
+ if (likely(napi_schedule_prep(&eth->rx_napi))) {
+ __napi_schedule(&eth->rx_napi);
+- mtk_rx_irq_disable(eth, MTK_RX_DONE_INT);
++ mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
+ }
+
+ return IRQ_HANDLED;
+@@ -2322,8 +2430,10 @@ static irqreturn_t mtk_handle_irq(int ir
+ struct mtk_eth *eth = _eth;
+ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+
+- if (mtk_r32(eth, reg_map->pdma.irq_mask) & MTK_RX_DONE_INT) {
+- if (mtk_r32(eth, reg_map->pdma.irq_status) & MTK_RX_DONE_INT)
++ if (mtk_r32(eth, reg_map->pdma.irq_mask) &
++ eth->soc->txrx.rx_irq_done_mask) {
++ if (mtk_r32(eth, reg_map->pdma.irq_status) &
++ eth->soc->txrx.rx_irq_done_mask)
+ mtk_handle_irq_rx(irq, _eth);
+ }
+ if (mtk_r32(eth, reg_map->tx_irq_mask) & MTK_TX_DONE_INT) {
+@@ -2341,16 +2451,16 @@ static void mtk_poll_controller(struct n
+ struct mtk_eth *eth = mac->hw;
+
+ mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+- mtk_rx_irq_disable(eth, MTK_RX_DONE_INT);
++ mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
+ mtk_handle_irq_rx(eth->irq[2], dev);
+ mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+- mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
++ mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
+ }
+ #endif
+
+ static int mtk_start_dma(struct mtk_eth *eth)
+ {
+- u32 rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0;
++ u32 val, rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0;
+ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ int err;
+
+@@ -2361,12 +2471,19 @@ static int mtk_start_dma(struct mtk_eth
+ }
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
+- mtk_w32(eth,
+- MTK_TX_WB_DDONE | MTK_TX_DMA_EN |
+- MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO |
+- MTK_RX_DMA_EN | MTK_RX_2B_OFFSET |
+- MTK_RX_BT_32DWORDS,
+- reg_map->qdma.glo_cfg);
++ val = mtk_r32(eth, reg_map->qdma.glo_cfg);
++ val |= MTK_TX_DMA_EN | MTK_RX_DMA_EN |
++ MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO |
++ MTK_RX_2B_OFFSET | MTK_TX_WB_DDONE;
++
++ 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;
++ else
++ val |= MTK_RX_BT_32DWORDS;
++ mtk_w32(eth, val, reg_map->qdma.glo_cfg);
++
+ mtk_w32(eth,
+ MTK_RX_DMA_EN | rx_2b_offset |
+ MTK_RX_BT_32DWORDS | MTK_MULTI_EN,
+@@ -2440,7 +2557,7 @@ static int mtk_open(struct net_device *d
+ napi_enable(&eth->tx_napi);
+ napi_enable(&eth->rx_napi);
+ mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+- mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
++ mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
+ refcount_set(&eth->dma_refcnt, 1);
+ }
+ else
+@@ -2492,7 +2609,7 @@ static int mtk_stop(struct net_device *d
+ mtk_gdm_config(eth, MTK_GDMA_DROP_ALL);
+
+ mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+- mtk_rx_irq_disable(eth, MTK_RX_DONE_INT);
++ mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
+ napi_disable(&eth->tx_napi);
+ napi_disable(&eth->rx_napi);
+
+@@ -2652,9 +2769,25 @@ static int mtk_hw_init(struct mtk_eth *e
+ return 0;
+ }
+
+- /* Non-MT7628 handling... */
+- ethsys_reset(eth, RSTCTRL_FE);
+- ethsys_reset(eth, RSTCTRL_PPE);
++ val = RSTCTRL_FE | RSTCTRL_PPE;
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0);
++
++ val |= RSTCTRL_ETH;
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
++ val |= RSTCTRL_PPE1;
++ }
++
++ ethsys_reset(eth, val);
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
++ 0x3ffffff);
++
++ /* Set FE to PDMAv2 if necessary */
++ val = mtk_r32(eth, MTK_FE_GLO_MISC);
++ mtk_w32(eth, val | BIT(4), MTK_FE_GLO_MISC);
++ }
+
+ if (eth->pctl) {
+ /* Set GE2 driving and slew rate */
+@@ -2693,11 +2826,47 @@ static int mtk_hw_init(struct mtk_eth *e
+
+ /* FE int grouping */
+ mtk_w32(eth, MTK_TX_DONE_INT, reg_map->pdma.int_grp);
+- mtk_w32(eth, MTK_RX_DONE_INT, reg_map->pdma.int_grp + 4);
++ mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->pdma.int_grp + 4);
+ mtk_w32(eth, MTK_TX_DONE_INT, reg_map->qdma.int_grp);
+- mtk_w32(eth, MTK_RX_DONE_INT, reg_map->qdma.int_grp + 4);
++ mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4);
+ 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 */
++ mtk_w32(eth, 0x00000300, PSE_DROP_CFG);
++
++ /* PSE Free Queue Flow Control */
++ mtk_w32(eth, 0x01fa01f4, PSE_FQFC_CFG2);
++
++ /* PSE config input queue threshold */
++ mtk_w32(eth, 0x001a000e, PSE_IQ_REV(1));
++ mtk_w32(eth, 0x01ff001a, PSE_IQ_REV(2));
++ mtk_w32(eth, 0x000e01ff, PSE_IQ_REV(3));
++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(4));
++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(5));
++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(6));
++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(7));
++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(8));
++
++ /* PSE config output queue threshold */
++ mtk_w32(eth, 0x000f000a, PSE_OQ_TH(1));
++ mtk_w32(eth, 0x001a000f, PSE_OQ_TH(2));
++ mtk_w32(eth, 0x000f001a, PSE_OQ_TH(3));
++ mtk_w32(eth, 0x01ff000f, PSE_OQ_TH(4));
++ mtk_w32(eth, 0x000f000f, PSE_OQ_TH(5));
++ mtk_w32(eth, 0x0006000f, PSE_OQ_TH(6));
++ mtk_w32(eth, 0x00060006, PSE_OQ_TH(7));
++ mtk_w32(eth, 0x00060006, PSE_OQ_TH(8));
++
++ /* GDM and CDM Threshold */
++ mtk_w32(eth, 0x00000004, MTK_GDM2_THRES);
++ mtk_w32(eth, 0x00000004, MTK_CDMW0_THRES);
++ mtk_w32(eth, 0x00000004, MTK_CDMW1_THRES);
++ mtk_w32(eth, 0x00000004, MTK_CDME0_THRES);
++ mtk_w32(eth, 0x00000004, MTK_CDME1_THRES);
++ mtk_w32(eth, 0x00000004, MTK_CDMM_THRES);
++ }
++
+ return 0;
+
+ err_disable_pm:
+@@ -3234,12 +3403,8 @@ static int mtk_probe(struct platform_dev
+ if (IS_ERR(eth->base))
+ return PTR_ERR(eth->base);
+
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
+- eth->rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA;
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+ eth->ip_align = NET_IP_ALIGN;
+- } else {
+- eth->rx_dma_l4_valid = RX_DMA_L4_VALID;
+- }
+
+ spin_lock_init(&eth->page_lock);
+ spin_lock_init(&eth->tx_irq_lock);
+@@ -3475,6 +3640,10 @@ static const struct mtk_soc_data mt2701_
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
++ .rx_irq_done_mask = MTK_RX_DONE_INT,
++ .rx_dma_l4_valid = RX_DMA_L4_VALID,
++ .dma_max_len = MTK_TX_DMA_BUF_LEN,
++ .dma_len_offset = 16,
+ },
+ };
+
+@@ -3488,6 +3657,10 @@ static const struct mtk_soc_data mt7621_
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
++ .rx_irq_done_mask = MTK_RX_DONE_INT,
++ .rx_dma_l4_valid = RX_DMA_L4_VALID,
++ .dma_max_len = MTK_TX_DMA_BUF_LEN,
++ .dma_len_offset = 16,
+ },
+ };
+
+@@ -3502,6 +3675,10 @@ static const struct mtk_soc_data mt7622_
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
++ .rx_irq_done_mask = MTK_RX_DONE_INT,
++ .rx_dma_l4_valid = RX_DMA_L4_VALID,
++ .dma_max_len = MTK_TX_DMA_BUF_LEN,
++ .dma_len_offset = 16,
+ },
+ };
+
+@@ -3515,6 +3692,10 @@ static const struct mtk_soc_data mt7623_
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
++ .rx_irq_done_mask = MTK_RX_DONE_INT,
++ .rx_dma_l4_valid = RX_DMA_L4_VALID,
++ .dma_max_len = MTK_TX_DMA_BUF_LEN,
++ .dma_len_offset = 16,
+ },
+ };
+
+@@ -3528,6 +3709,10 @@ static const struct mtk_soc_data mt7629_
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
++ .rx_irq_done_mask = MTK_RX_DONE_INT,
++ .rx_dma_l4_valid = RX_DMA_L4_VALID,
++ .dma_max_len = MTK_TX_DMA_BUF_LEN,
++ .dma_len_offset = 16,
+ },
+ };
+
+@@ -3540,6 +3725,10 @@ static const struct mtk_soc_data rt5350_
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
++ .rx_irq_done_mask = MTK_RX_DONE_INT,
++ .rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA,
++ .dma_max_len = MTK_TX_DMA_BUF_LEN,
++ .dma_len_offset = 16,
+ },
+ };
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -23,6 +23,7 @@
+ #define MTK_MAX_RX_LENGTH 1536
+ #define MTK_MAX_RX_LENGTH_2K 2048
+ #define MTK_TX_DMA_BUF_LEN 0x3fff
++#define MTK_TX_DMA_BUF_LEN_V2 0xffff
+ #define MTK_DMA_SIZE 512
+ #define MTK_NAPI_WEIGHT 64
+ #define MTK_MAC_COUNT 2
+@@ -83,6 +84,10 @@
+ #define MTK_CDMQ_IG_CTRL 0x1400
+ #define MTK_CDMQ_STAG_EN BIT(0)
+
++/* CDMP Ingress Control Register */
++#define MTK_CDMP_IG_CTRL 0x400
++#define MTK_CDMP_STAG_EN BIT(0)
++
+ /* CDMP Exgress Control Register */
+ #define MTK_CDMP_EG_CTRL 0x404
+
+@@ -102,13 +107,38 @@
+ /* Unicast Filter MAC Address Register - High */
+ #define MTK_GDMA_MAC_ADRH(x) (0x50C + (x * 0x1000))
+
++/* FE global misc reg*/
++#define MTK_FE_GLO_MISC 0x124
++
++/* PSE Free Queue Flow Control */
++#define PSE_FQFC_CFG1 0x100
++#define PSE_FQFC_CFG2 0x104
++#define PSE_DROP_CFG 0x108
++
++/* PSE Input Queue Reservation Register*/
++#define PSE_IQ_REV(x) (0x140 + (((x) - 1) << 2))
++
++/* PSE Output Queue Threshold Register*/
++#define PSE_OQ_TH(x) (0x160 + (((x) - 1) << 2))
++
++/* GDM and CDM Threshold */
++#define MTK_GDM2_THRES 0x1530
++#define MTK_CDMW0_THRES 0x164c
++#define MTK_CDMW1_THRES 0x1650
++#define MTK_CDME0_THRES 0x1654
++#define MTK_CDME1_THRES 0x1658
++#define MTK_CDMM_THRES 0x165c
++
+ /* PDMA HW LRO Control Registers */
+ #define MTK_PDMA_LRO_CTRL_DW0 0x980
+ #define MTK_LRO_EN BIT(0)
+ #define MTK_L3_CKS_UPD_EN BIT(7)
++#define MTK_L3_CKS_UPD_EN_V2 BIT(19)
+ #define MTK_LRO_ALT_PKT_CNT_MODE BIT(21)
+ #define MTK_LRO_RING_RELINQUISH_REQ (0x7 << 26)
++#define MTK_LRO_RING_RELINQUISH_REQ_V2 (0xf << 24)
+ #define MTK_LRO_RING_RELINQUISH_DONE (0x7 << 29)
++#define MTK_LRO_RING_RELINQUISH_DONE_V2 (0xf << 28)
+
+ #define MTK_PDMA_LRO_CTRL_DW1 0x984
+ #define MTK_PDMA_LRO_CTRL_DW2 0x988
+@@ -180,6 +210,13 @@
+ #define MTK_TX_DMA_EN BIT(0)
+ #define MTK_DMA_BUSY_TIMEOUT_US 1000000
+
++/* QDMA V2 Global Configuration Register */
++#define MTK_CHK_DDONE_EN BIT(28)
++#define MTK_DMAD_WR_WDONE BIT(26)
++#define MTK_WCOMP_EN BIT(24)
++#define MTK_RESV_BUF (0x40 << 16)
++#define MTK_MUTLI_CNT (0x4 << 12)
++
+ /* QDMA Flow Control Register */
+ #define FC_THRES_DROP_MODE BIT(20)
+ #define FC_THRES_DROP_EN (7 << 16)
+@@ -199,11 +236,32 @@
+ #define MTK_RX_DONE_INT MTK_RX_DONE_DLY
+ #define MTK_TX_DONE_INT MTK_TX_DONE_DLY
+
++#define MTK_RX_DONE_INT_V2 BIT(14)
++
+ /* QDMA Interrupt grouping registers */
+ #define MTK_RLS_DONE_INT BIT(0)
+
+ #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
++
++#define MTK_TX_DMA_BUF_SHIFT 8
++
++/* QDMA V2 descriptor txd6 */
++#define TX_DMA_INS_VLAN_V2 BIT(16)
++/* QDMA V2 descriptor txd5 */
++#define TX_DMA_CHKSUM_V2 (0x7 << 28)
++#define TX_DMA_TSO_V2 BIT(31)
++
++/* QDMA V2 descriptor txd4 */
++#define TX_DMA_FPORT_SHIFT_V2 8
++#define TX_DMA_FPORT_MASK_V2 0xf
++#define TX_DMA_SWC_V2 BIT(30)
++
+ #define MTK_WDMA0_BASE 0x2800
+ #define MTK_WDMA1_BASE 0x2c00
+
+@@ -217,10 +275,9 @@
+ /* QDMA descriptor txd3 */
+ #define TX_DMA_OWNER_CPU BIT(31)
+ #define TX_DMA_LS0 BIT(30)
+-#define TX_DMA_PLEN0(_x) (((_x) & MTK_TX_DMA_BUF_LEN) << 16)
+-#define TX_DMA_PLEN1(_x) ((_x) & MTK_TX_DMA_BUF_LEN)
++#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_SDL(_x) (((_x) & 0x3fff) << 16)
+
+ /* PDMA on MT7628 */
+ #define TX_DMA_DONE BIT(31)
+@@ -230,12 +287,14 @@
+ /* QDMA descriptor rxd2 */
+ #define RX_DMA_DONE BIT(31)
+ #define RX_DMA_LSO BIT(30)
+-#define RX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16)
+-#define RX_DMA_GET_PLEN0(_x) (((_x) >> 16) & 0x3fff)
++#define RX_DMA_PREP_PLEN0(x) (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
++#define RX_DMA_GET_PLEN0(x) (((x) >> eth->soc->txrx.dma_len_offset) & eth->soc->txrx.dma_max_len)
+ #define RX_DMA_VTAG BIT(15)
+
+ /* QDMA descriptor rxd3 */
+-#define RX_DMA_VID(_x) ((_x) & 0xfff)
++#define RX_DMA_VID(x) ((x) & VLAN_VID_MASK)
++#define RX_DMA_TCI(x) ((x) & (VLAN_PRIO_MASK | VLAN_VID_MASK))
++#define RX_DMA_VPID(x) (((x) >> 16) & 0xffff)
+
+ /* QDMA descriptor rxd4 */
+ #define MTK_RXD4_FOE_ENTRY GENMASK(13, 0)
+@@ -246,10 +305,15 @@
+ /* QDMA descriptor rxd4 */
+ #define RX_DMA_L4_VALID BIT(24)
+ #define RX_DMA_L4_VALID_PDMA BIT(30) /* when PDMA is used */
+-#define RX_DMA_FPORT_SHIFT 19
+-#define RX_DMA_FPORT_MASK 0x7
+ #define RX_DMA_SPECIAL_TAG BIT(22)
+
++#define RX_DMA_GET_SPORT(x) (((x) >> 19) & 0xf)
++#define RX_DMA_GET_SPORT_V2(x) (((x) >> 26) & 0x7)
++
++/* PDMA V2 descriptor rxd3 */
++#define RX_DMA_VTAG_V2 BIT(0)
++#define RX_DMA_L4_VALID_V2 BIT(2)
++
+ /* PHY Indirect Access Control registers */
+ #define MTK_PHY_IAC 0x10004
+ #define PHY_IAC_ACCESS BIT(31)
+@@ -371,6 +435,16 @@
+ #define ETHSYS_TRGMII_MT7621_DDR_PLL BIT(5)
+
+ /* ethernet reset control register */
++#define ETHSYS_RSTCTRL 0x34
++#define RSTCTRL_FE BIT(6)
++#define RSTCTRL_PPE BIT(31)
++#define RSTCTRL_PPE1 BIT(30)
++#define RSTCTRL_ETH BIT(23)
++
++/* ethernet reset check idle register */
++#define ETHSYS_FE_RST_CHK_IDLE_EN 0x28
++
++/* ethernet reset control register */
+ #define ETHSYS_RSTCTRL 0x34
+ #define RSTCTRL_FE BIT(6)
+ #define RSTCTRL_PPE BIT(31)
+@@ -454,6 +528,17 @@ struct mtk_rx_dma {
+ unsigned int rxd4;
+ } __packed __aligned(4);
+
++struct mtk_rx_dma_v2 {
++ unsigned int rxd1;
++ unsigned int rxd2;
++ unsigned int rxd3;
++ unsigned int rxd4;
++ unsigned int rxd5;
++ unsigned int rxd6;
++ unsigned int rxd7;
++ unsigned int rxd8;
++} __packed __aligned(4);
++
+ struct mtk_tx_dma {
+ unsigned int txd1;
+ unsigned int txd2;
+@@ -461,6 +546,17 @@ struct mtk_tx_dma {
+ unsigned int txd4;
+ } __packed __aligned(4);
+
++struct mtk_tx_dma_v2 {
++ unsigned int txd1;
++ unsigned int txd2;
++ unsigned int txd3;
++ unsigned int txd4;
++ unsigned int txd5;
++ unsigned int txd6;
++ unsigned int txd7;
++ unsigned int txd8;
++} __packed __aligned(4);
++
+ struct mtk_eth;
+ struct mtk_mac;
+
+@@ -647,7 +743,9 @@ enum mkt_eth_capabilities {
+ MTK_SHARED_INT_BIT,
+ MTK_TRGMII_MT7621_CLK_BIT,
+ MTK_QDMA_BIT,
++ MTK_NETSYS_V2_BIT,
+ MTK_SOC_MT7628_BIT,
++ MTK_RSTCTRL_PPE1_BIT,
+
+ /* MUX BITS*/
+ MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
+@@ -679,7 +777,9 @@ enum mkt_eth_capabilities {
+ #define MTK_SHARED_INT BIT(MTK_SHARED_INT_BIT)
+ #define MTK_TRGMII_MT7621_CLK BIT(MTK_TRGMII_MT7621_CLK_BIT)
+ #define MTK_QDMA BIT(MTK_QDMA_BIT)
++#define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT)
+ #define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT)
++#define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT)
+
+ #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \
+ BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
+@@ -756,6 +856,7 @@ struct mtk_tx_dma_desc_info {
+ dma_addr_t addr;
+ u32 size;
+ u16 vlan_tci;
++ u16 qid;
+ u8 gso:1;
+ u8 csum:1;
+ u8 vlan:1;
+@@ -813,6 +914,10 @@ struct mtk_reg_map {
+ * the extra setup for those pins used by GMAC.
+ * @txd_size Tx DMA descriptor size.
+ * @rxd_size Rx DMA descriptor size.
++ * @rx_irq_done_mask Rx irq done register mask.
++ * @rx_dma_l4_valid Rx DMA valid register mask.
++ * @dma_max_len Max DMA tx/rx buffer length.
++ * @dma_len_offset Tx/Rx DMA length field offset.
+ */
+ struct mtk_soc_data {
+ const struct mtk_reg_map *reg_map;
+@@ -825,6 +930,10 @@ struct mtk_soc_data {
+ struct {
+ u32 txd_size;
+ u32 rxd_size;
++ u32 rx_irq_done_mask;
++ u32 rx_dma_l4_valid;
++ u32 dma_max_len;
++ u32 dma_len_offset;
+ } txrx;
+ };
+
+@@ -943,7 +1052,6 @@ struct mtk_eth {
+ u32 tx_bytes;
+ struct dim tx_dim;
+
+- u32 rx_dma_l4_valid;
+ int ip_align;
+
+ struct mtk_ppe *ppe;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch b/target/linux/generic/backport-6.1/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch
new file mode 100644
index 0000000000..1ecb5e71b3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch
@@ -0,0 +1,135 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:37 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: convert ring dma pointer to void
+
+Simplify the code converting {tx,rx} ring dma pointer to void
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -947,18 +947,15 @@ static int mtk_init_fq_dma(struct mtk_et
+ return 0;
+ }
+
+-static inline void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc)
++static void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc)
+ {
+- void *ret = ring->dma;
+-
+- return ret + (desc - ring->phys);
++ return ring->dma + (desc - ring->phys);
+ }
+
+ static struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring,
+- struct mtk_tx_dma *txd,
+- u32 txd_size)
++ void *txd, u32 txd_size)
+ {
+- int idx = ((void *)txd - (void *)ring->dma) / txd_size;
++ int idx = (txd - ring->dma) / txd_size;
+
+ return &ring->buf[idx];
+ }
+@@ -966,13 +963,12 @@ static struct mtk_tx_buf *mtk_desc_to_tx
+ static struct mtk_tx_dma *qdma_to_pdma(struct mtk_tx_ring *ring,
+ struct mtk_tx_dma *dma)
+ {
+- return ring->dma_pdma - ring->dma + dma;
++ return ring->dma_pdma - (struct mtk_tx_dma *)ring->dma + dma;
+ }
+
+-static int txd_to_idx(struct mtk_tx_ring *ring, struct mtk_tx_dma *dma,
+- u32 txd_size)
++static int txd_to_idx(struct mtk_tx_ring *ring, void *dma, u32 txd_size)
+ {
+- return ((void *)dma - (void *)ring->dma) / txd_size;
++ return (dma - ring->dma) / txd_size;
+ }
+
+ static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf,
+@@ -1389,7 +1385,7 @@ static struct mtk_rx_ring *mtk_get_rx_ri
+
+ ring = &eth->rx_ring[i];
+ idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
+- rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size;
++ rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
+ if (rxd->rxd2 & RX_DMA_DONE) {
+ ring->calc_idx_update = true;
+ return ring;
+@@ -1441,7 +1437,7 @@ static int mtk_poll_rx(struct napi_struc
+ goto rx_done;
+
+ idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
+- rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size;
++ rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
+ data = ring->data[idx];
+
+ if (!mtk_rx_get_desc(eth, &trxd, rxd))
+@@ -1648,7 +1644,7 @@ static int mtk_poll_tx_pdma(struct mtk_e
+
+ mtk_tx_unmap(eth, tx_buf, true);
+
+- desc = (void *)ring->dma + cpu * eth->soc->txrx.txd_size;
++ desc = ring->dma + cpu * eth->soc->txrx.txd_size;
+ ring->last_free = desc;
+ atomic_inc(&ring->free_count);
+
+@@ -1793,7 +1789,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ int next = (i + 1) % MTK_DMA_SIZE;
+ u32 next_ptr = ring->phys + next * sz;
+
+- txd = (void *)ring->dma + i * sz;
++ txd = ring->dma + i * sz;
+ txd->txd2 = next_ptr;
+ txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
+ txd->txd4 = 0;
+@@ -1823,7 +1819,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+
+ ring->dma_size = MTK_DMA_SIZE;
+ atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
+- ring->next_free = &ring->dma[0];
++ ring->next_free = ring->dma;
+ ring->last_free = (void *)txd;
+ ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz));
+ ring->thresh = MAX_SKB_FRAGS;
+@@ -1938,7 +1934,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
+ return -ENOMEM;
+
+- rxd = (void *)ring->dma + i * eth->soc->txrx.rxd_size;
++ rxd = ring->dma + i * eth->soc->txrx.rxd_size;
+ rxd->rxd1 = (unsigned int)dma_addr;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+@@ -2000,7 +1996,7 @@ static void mtk_rx_clean(struct mtk_eth
+ if (!ring->data[i])
+ continue;
+
+- rxd = (void *)ring->dma + i * eth->soc->txrx.rxd_size;
++ rxd = ring->dma + i * eth->soc->txrx.rxd_size;
+ if (!rxd->rxd1)
+ continue;
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -689,7 +689,7 @@ struct mtk_tx_buf {
+ * are present
+ */
+ struct mtk_tx_ring {
+- struct mtk_tx_dma *dma;
++ void *dma;
+ struct mtk_tx_buf *buf;
+ dma_addr_t phys;
+ struct mtk_tx_dma *next_free;
+@@ -719,7 +719,7 @@ enum mtk_rx_flags {
+ * @calc_idx: The current head of ring
+ */
+ struct mtk_rx_ring {
+- struct mtk_rx_dma *dma;
++ void *dma;
+ u8 **data;
+ dma_addr_t phys;
+ u16 frag_size;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch b/target/linux/generic/backport-6.1/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch
new file mode 100644
index 0000000000..f7318e68bb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch
@@ -0,0 +1,33 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:38 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: convert scratch_ring pointer to
+ void
+
+Simplify the code converting scratch_ring pointer to void
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -923,7 +923,7 @@ static int mtk_init_fq_dma(struct mtk_et
+ for (i = 0; i < cnt; i++) {
+ struct mtk_tx_dma_v2 *txd;
+
+- txd = (void *)eth->scratch_ring + i * soc->txrx.txd_size;
++ txd = eth->scratch_ring + i * soc->txrx.txd_size;
+ txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
+ if (i < cnt - 1)
+ txd->txd2 = eth->phy_scratch_ring +
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1029,7 +1029,7 @@ struct mtk_eth {
+ struct mtk_rx_ring rx_ring_qdma;
+ struct napi_struct tx_napi;
+ struct napi_struct rx_napi;
+- struct mtk_tx_dma *scratch_ring;
++ void *scratch_ring;
+ dma_addr_t phy_scratch_ring;
+ void *scratch_head;
+ struct clk *clks[MTK_CLK_MAX];
diff --git a/target/linux/generic/backport-6.1/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch b/target/linux/generic/backport-6.1/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch
new file mode 100644
index 0000000000..a4698d7814
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch
@@ -0,0 +1,138 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 20 May 2022 20:11:39 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce support for mt7986
+ chipset
+
+Add support for mt7986-eth driver available on mt7986 soc.
+
+Tested-by: Sam Shih <sam.shih@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_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -87,6 +87,43 @@ static const struct mtk_reg_map mt7628_r
+ },
+ };
+
++static const struct mtk_reg_map mt7986_reg_map = {
++ .tx_irq_mask = 0x461c,
++ .tx_irq_status = 0x4618,
++ .pdma = {
++ .rx_ptr = 0x6100,
++ .rx_cnt_cfg = 0x6104,
++ .pcrx_ptr = 0x6108,
++ .glo_cfg = 0x6204,
++ .rst_idx = 0x6208,
++ .delay_irq = 0x620c,
++ .irq_status = 0x6220,
++ .irq_mask = 0x6228,
++ .int_grp = 0x6250,
++ },
++ .qdma = {
++ .qtx_cfg = 0x4400,
++ .rx_ptr = 0x4500,
++ .rx_cnt_cfg = 0x4504,
++ .qcrx_ptr = 0x4508,
++ .glo_cfg = 0x4604,
++ .rst_idx = 0x4608,
++ .delay_irq = 0x460c,
++ .fc_th = 0x4610,
++ .int_grp = 0x4620,
++ .hred = 0x4644,
++ .ctx_ptr = 0x4700,
++ .dtx_ptr = 0x4704,
++ .crx_ptr = 0x4710,
++ .drx_ptr = 0x4714,
++ .fq_head = 0x4720,
++ .fq_tail = 0x4724,
++ .fq_count = 0x4728,
++ .fq_blen = 0x472c,
++ },
++ .gdm1_cnt = 0x1c00,
++};
++
+ /* strings used by ethtool */
+ static const struct mtk_ethtool_stats {
+ char str[ETH_GSTRING_LEN];
+@@ -110,7 +147,7 @@ static const char * const mtk_clks_sourc
+ "ethif", "sgmiitop", "esw", "gp0", "gp1", "gp2", "fe", "trgpll",
+ "sgmii_tx250m", "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb",
+ "sgmii2_tx250m", "sgmii2_rx250m", "sgmii2_cdr_ref", "sgmii2_cdr_fb",
+- "sgmii_ck", "eth2pll",
++ "sgmii_ck", "eth2pll", "wocpu0", "wocpu1", "netsys0", "netsys1"
+ };
+
+ void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
+@@ -3712,6 +3749,21 @@ static const struct mtk_soc_data mt7629_
+ },
+ };
+
++static const struct mtk_soc_data mt7986_data = {
++ .reg_map = &mt7986_reg_map,
++ .ana_rgc3 = 0x128,
++ .caps = MT7986_CAPS,
++ .required_clks = MT7986_CLKS_BITMAP,
++ .required_pctl = false,
++ .txrx = {
++ .txd_size = sizeof(struct mtk_tx_dma_v2),
++ .rxd_size = sizeof(struct mtk_rx_dma_v2),
++ .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
++ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
++ .dma_len_offset = 8,
++ },
++};
++
+ static const struct mtk_soc_data rt5350_data = {
+ .reg_map = &mt7628_reg_map,
+ .caps = MT7628_CAPS,
+@@ -3734,6 +3786,7 @@ const struct of_device_id of_mtk_match[]
+ { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data},
+ { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data},
+ { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data},
++ { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data},
+ { .compatible = "ralink,rt5350-eth", .data = &rt5350_data},
+ {},
+ };
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -624,6 +624,10 @@ enum mtk_clks_map {
+ MTK_CLK_SGMII2_CDR_FB,
+ MTK_CLK_SGMII_CK,
+ MTK_CLK_ETH2PLL,
++ MTK_CLK_WOCPU0,
++ MTK_CLK_WOCPU1,
++ MTK_CLK_NETSYS0,
++ MTK_CLK_NETSYS1,
+ MTK_CLK_MAX
+ };
+
+@@ -654,6 +658,16 @@ enum mtk_clks_map {
+ BIT(MTK_CLK_SGMII2_CDR_FB) | \
+ BIT(MTK_CLK_SGMII_CK) | \
+ BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP))
++#define MT7986_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \
++ BIT(MTK_CLK_WOCPU1) | BIT(MTK_CLK_WOCPU0) | \
++ BIT(MTK_CLK_SGMII_TX_250M) | \
++ BIT(MTK_CLK_SGMII_RX_250M) | \
++ BIT(MTK_CLK_SGMII_CDR_REF) | \
++ BIT(MTK_CLK_SGMII_CDR_FB) | \
++ BIT(MTK_CLK_SGMII2_TX_250M) | \
++ BIT(MTK_CLK_SGMII2_RX_250M) | \
++ BIT(MTK_CLK_SGMII2_CDR_REF) | \
++ BIT(MTK_CLK_SGMII2_CDR_FB))
+
+ enum mtk_dev_state {
+ MTK_HW_INIT,
+@@ -852,6 +866,10 @@ enum mkt_eth_capabilities {
+ MTK_MUX_U3_GMAC2_TO_QPHY | \
+ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA)
+
++#define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
++ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
++ MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1)
++
+ struct mtk_tx_dma_desc_info {
+ dma_addr_t addr;
+ u32 size;
diff --git a/target/linux/generic/backport-6.1/702-v5.19-31-net-ethernet-mtk_eth_soc-fix-error-code-in-mtk_flow_.patch b/target/linux/generic/backport-6.1/702-v5.19-31-net-ethernet-mtk_eth_soc-fix-error-code-in-mtk_flow_.patch
new file mode 100644
index 0000000000..e490333a9b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-31-net-ethernet-mtk_eth_soc-fix-error-code-in-mtk_flow_.patch
@@ -0,0 +1,25 @@
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Thu, 19 May 2022 17:08:00 +0300
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix error code in
+ mtk_flow_offload_replace()
+
+Preserve the error code from mtk_foe_entry_commit(). Do not return
+success.
+
+Fixes: c4f033d9e03e ("net: ethernet: mtk_eth_soc: rework hardware flow table management")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -434,7 +434,8 @@ mtk_flow_offload_replace(struct mtk_eth
+ memcpy(&entry->data, &foe, sizeof(entry->data));
+ entry->wed_index = wed_index;
+
+- if (mtk_foe_entry_commit(eth->ppe, entry) < 0)
++ err = mtk_foe_entry_commit(eth->ppe, entry);
++ if (err < 0)
+ goto free;
+
+ err = rhashtable_insert_fast(&eth->flow_table, &entry->node,
diff --git a/target/linux/generic/backport-6.1/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch b/target/linux/generic/backport-6.1/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch
new file mode 100644
index 0000000000..d76df75dda
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch
@@ -0,0 +1,47 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 6 Jun 2022 21:49:00 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: enable rx cksum offload for
+ MTK_NETSYS_V2
+
+Enable rx checksum offload for mt7986 chipset.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/c8699805c18f7fd38315fcb8da2787676d83a32c.1654544585.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1463,8 +1463,8 @@ static int mtk_poll_rx(struct napi_struc
+ int done = 0, bytes = 0;
+
+ while (done < budget) {
++ unsigned int pktlen, *rxdcsum;
+ struct net_device *netdev;
+- unsigned int pktlen;
+ dma_addr_t dma_addr;
+ u32 hash, reason;
+ int mac = 0;
+@@ -1531,7 +1531,13 @@ static int mtk_poll_rx(struct napi_struc
+ pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
+ skb->dev = netdev;
+ skb_put(skb, pktlen);
+- if (trxd.rxd4 & eth->soc->txrx.rx_dma_l4_valid)
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ rxdcsum = &trxd.rxd3;
++ else
++ rxdcsum = &trxd.rxd4;
++
++ if (*rxdcsum & eth->soc->txrx.rx_dma_l4_valid)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb_checksum_none_assert(skb);
+@@ -3759,6 +3765,7 @@ static const struct mtk_soc_data mt7986_
+ .txd_size = sizeof(struct mtk_tx_dma_v2),
+ .rxd_size = sizeof(struct mtk_rx_dma_v2),
+ .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
++ .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
+ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+ .dma_len_offset = 8,
+ },
diff --git a/target/linux/generic/backport-6.1/702-v5.19-34-eth-mtk_ppe-fix-up-after-merge.patch b/target/linux/generic/backport-6.1/702-v5.19-34-eth-mtk_ppe-fix-up-after-merge.patch
new file mode 100644
index 0000000000..5303ca48a7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/702-v5.19-34-eth-mtk_ppe-fix-up-after-merge.patch
@@ -0,0 +1,28 @@
+From: Jakub Kicinski <kuba@kernel.org>
+Date: Thu, 19 May 2022 18:25:55 -0700
+Subject: [PATCH] eth: mtk_ppe: fix up after merge
+
+I missed this in the barrage of GCC 12 warnings. Commit cf2df74e202d
+("net: fix dev_fill_forward_path with pppoe + bridge") changed
+the pointer into an array.
+
+Fixes: d7e6f5836038 ("Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net")
+Link: https://lore.kernel.org/r/20220520012555.2262461-1-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -90,10 +90,11 @@ mtk_flow_get_wdma_info(struct net_device
+ {
+ struct net_device_path_ctx ctx = {
+ .dev = dev,
+- .daddr = addr,
+ };
+ struct net_device_path path = {};
+
++ memcpy(ctx.daddr, addr, sizeof(ctx.daddr));
++
+ if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
+ return -1;
+
diff --git a/target/linux/generic/backport-6.1/703-00-v5.16-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch b/target/linux/generic/backport-6.1/703-00-v5.16-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch
new file mode 100644
index 0000000000..919da4d1ef
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-00-v5.16-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch
@@ -0,0 +1,948 @@
+From 4973056cceacc70966396039fae99867dfafd796 Mon Sep 17 00:00:00 2001
+From: Sean Anderson <sean.anderson@seco.com>
+Date: Fri, 22 Oct 2021 18:41:04 -0400
+Subject: [PATCH] net: convert users of bitmap_foo() to linkmode_foo()
+
+This converts instances of
+ bitmap_foo(args..., __ETHTOOL_LINK_MODE_MASK_NBITS)
+to
+ linkmode_foo(args...)
+
+I manually fixed up some lines to prevent them from being excessively
+long. Otherwise, this change was generated with the following semantic
+patch:
+
+// Generated with
+// echo linux/linkmode.h > includes
+// git grep -Flf includes include/ | cut -f 2- -d / | cat includes - \
+// | sort | uniq | tee new_includes | wc -l && mv new_includes includes
+// and repeating until the number stopped going up
+@i@
+@@
+
+(
+ #include <linux/acpi_mdio.h>
+|
+ #include <linux/brcmphy.h>
+|
+ #include <linux/dsa/loop.h>
+|
+ #include <linux/dsa/sja1105.h>
+|
+ #include <linux/ethtool.h>
+|
+ #include <linux/ethtool_netlink.h>
+|
+ #include <linux/fec.h>
+|
+ #include <linux/fs_enet_pd.h>
+|
+ #include <linux/fsl/enetc_mdio.h>
+|
+ #include <linux/fwnode_mdio.h>
+|
+ #include <linux/linkmode.h>
+|
+ #include <linux/lsm_audit.h>
+|
+ #include <linux/mdio-bitbang.h>
+|
+ #include <linux/mdio.h>
+|
+ #include <linux/mdio-mux.h>
+|
+ #include <linux/mii.h>
+|
+ #include <linux/mii_timestamper.h>
+|
+ #include <linux/mlx5/accel.h>
+|
+ #include <linux/mlx5/cq.h>
+|
+ #include <linux/mlx5/device.h>
+|
+ #include <linux/mlx5/driver.h>
+|
+ #include <linux/mlx5/eswitch.h>
+|
+ #include <linux/mlx5/fs.h>
+|
+ #include <linux/mlx5/port.h>
+|
+ #include <linux/mlx5/qp.h>
+|
+ #include <linux/mlx5/rsc_dump.h>
+|
+ #include <linux/mlx5/transobj.h>
+|
+ #include <linux/mlx5/vport.h>
+|
+ #include <linux/of_mdio.h>
+|
+ #include <linux/of_net.h>
+|
+ #include <linux/pcs-lynx.h>
+|
+ #include <linux/pcs/pcs-xpcs.h>
+|
+ #include <linux/phy.h>
+|
+ #include <linux/phy_led_triggers.h>
+|
+ #include <linux/phylink.h>
+|
+ #include <linux/platform_data/bcmgenet.h>
+|
+ #include <linux/platform_data/xilinx-ll-temac.h>
+|
+ #include <linux/pxa168_eth.h>
+|
+ #include <linux/qed/qed_eth_if.h>
+|
+ #include <linux/qed/qed_fcoe_if.h>
+|
+ #include <linux/qed/qed_if.h>
+|
+ #include <linux/qed/qed_iov_if.h>
+|
+ #include <linux/qed/qed_iscsi_if.h>
+|
+ #include <linux/qed/qed_ll2_if.h>
+|
+ #include <linux/qed/qed_nvmetcp_if.h>
+|
+ #include <linux/qed/qed_rdma_if.h>
+|
+ #include <linux/sfp.h>
+|
+ #include <linux/sh_eth.h>
+|
+ #include <linux/smsc911x.h>
+|
+ #include <linux/soc/nxp/lpc32xx-misc.h>
+|
+ #include <linux/stmmac.h>
+|
+ #include <linux/sunrpc/svc_rdma.h>
+|
+ #include <linux/sxgbe_platform.h>
+|
+ #include <net/cfg80211.h>
+|
+ #include <net/dsa.h>
+|
+ #include <net/mac80211.h>
+|
+ #include <net/selftests.h>
+|
+ #include <rdma/ib_addr.h>
+|
+ #include <rdma/ib_cache.h>
+|
+ #include <rdma/ib_cm.h>
+|
+ #include <rdma/ib_hdrs.h>
+|
+ #include <rdma/ib_mad.h>
+|
+ #include <rdma/ib_marshall.h>
+|
+ #include <rdma/ib_pack.h>
+|
+ #include <rdma/ib_pma.h>
+|
+ #include <rdma/ib_sa.h>
+|
+ #include <rdma/ib_smi.h>
+|
+ #include <rdma/ib_umem.h>
+|
+ #include <rdma/ib_umem_odp.h>
+|
+ #include <rdma/ib_verbs.h>
+|
+ #include <rdma/iw_cm.h>
+|
+ #include <rdma/mr_pool.h>
+|
+ #include <rdma/opa_addr.h>
+|
+ #include <rdma/opa_port_info.h>
+|
+ #include <rdma/opa_smi.h>
+|
+ #include <rdma/opa_vnic.h>
+|
+ #include <rdma/rdma_cm.h>
+|
+ #include <rdma/rdma_cm_ib.h>
+|
+ #include <rdma/rdmavt_cq.h>
+|
+ #include <rdma/rdma_vt.h>
+|
+ #include <rdma/rdmavt_qp.h>
+|
+ #include <rdma/rw.h>
+|
+ #include <rdma/tid_rdma_defs.h>
+|
+ #include <rdma/uverbs_ioctl.h>
+|
+ #include <rdma/uverbs_named_ioctl.h>
+|
+ #include <rdma/uverbs_std_types.h>
+|
+ #include <rdma/uverbs_types.h>
+|
+ #include <soc/mscc/ocelot.h>
+|
+ #include <soc/mscc/ocelot_ptp.h>
+|
+ #include <soc/mscc/ocelot_vcap.h>
+|
+ #include <trace/events/ib_mad.h>
+|
+ #include <trace/events/rdma_core.h>
+|
+ #include <trace/events/rdma.h>
+|
+ #include <trace/events/rpcrdma.h>
+|
+ #include <uapi/linux/ethtool.h>
+|
+ #include <uapi/linux/ethtool_netlink.h>
+|
+ #include <uapi/linux/mdio.h>
+|
+ #include <uapi/linux/mii.h>
+)
+
+@depends on i@
+expression list args;
+@@
+
+(
+- bitmap_zero(args, __ETHTOOL_LINK_MODE_MASK_NBITS)
++ linkmode_zero(args)
+|
+- bitmap_copy(args, __ETHTOOL_LINK_MODE_MASK_NBITS)
++ linkmode_copy(args)
+|
+- bitmap_and(args, __ETHTOOL_LINK_MODE_MASK_NBITS)
++ linkmode_and(args)
+|
+- bitmap_or(args, __ETHTOOL_LINK_MODE_MASK_NBITS)
++ linkmode_or(args)
+|
+- bitmap_empty(args, ETHTOOL_LINK_MODE_MASK_NBITS)
++ linkmode_empty(args)
+|
+- bitmap_andnot(args, __ETHTOOL_LINK_MODE_MASK_NBITS)
++ linkmode_andnot(args)
+|
+- bitmap_equal(args, __ETHTOOL_LINK_MODE_MASK_NBITS)
++ linkmode_equal(args)
+|
+- bitmap_intersects(args, __ETHTOOL_LINK_MODE_MASK_NBITS)
++ linkmode_intersects(args)
+|
+- bitmap_subset(args, __ETHTOOL_LINK_MODE_MASK_NBITS)
++ linkmode_subset(args)
+)
+
+Add missing linux/mii.h include to mellanox. -DaveM
+
+Signed-off-by: Sean Anderson <sean.anderson@seco.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/b53/b53_common.c | 6 ++----
+ drivers/net/dsa/bcm_sf2.c | 8 +++----
+ drivers/net/dsa/hirschmann/hellcreek.c | 6 ++----
+ drivers/net/dsa/lantiq_gswip.c | 14 ++++++-------
+ drivers/net/dsa/microchip/ksz8795.c | 8 +++----
+ drivers/net/dsa/mv88e6xxx/chip.c | 5 ++---
+ drivers/net/dsa/ocelot/felix_vsc9959.c | 8 +++----
+ drivers/net/dsa/ocelot/seville_vsc9953.c | 8 +++----
+ drivers/net/dsa/qca/ar9331.c | 10 ++++-----
+ drivers/net/dsa/sja1105/sja1105_main.c | 7 +++----
+ drivers/net/dsa/xrs700x/xrs700x.c | 8 +++----
+ drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 8 +++----
+ drivers/net/ethernet/atheros/ag71xx.c | 8 +++----
+ drivers/net/ethernet/cadence/macb_main.c | 11 +++++-----
+ .../net/ethernet/freescale/enetc/enetc_pf.c | 8 +++----
+ .../net/ethernet/huawei/hinic/hinic_ethtool.c | 10 ++++-----
+ .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 5 ++---
+ drivers/net/ethernet/marvell/mvneta.c | 10 ++++-----
+ .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 7 +++----
+ .../marvell/octeontx2/nic/otx2_ethtool.c | 5 ++---
+ drivers/net/ethernet/marvell/pxa168_eth.c | 3 +--
+ .../net/ethernet/mellanox/mlx4/en_ethtool.c | 21 +++++++------------
+ .../microchip/sparx5/sparx5_phylink.c | 7 +++----
+ drivers/net/ethernet/mscc/ocelot_net.c | 7 +++----
+ .../ethernet/pensando/ionic/ionic_ethtool.c | 3 +--
+ .../net/ethernet/xilinx/xilinx_axienet_main.c | 8 +++----
+ drivers/net/pcs/pcs-xpcs.c | 2 +-
+ drivers/net/phy/sfp-bus.c | 2 +-
+ net/ethtool/ioctl.c | 7 +++----
+ 29 files changed, 87 insertions(+), 133 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -1349,10 +1349,8 @@ void b53_phylink_validate(struct dsa_swi
+ phylink_set(mask, 100baseT_Full);
+ }
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+
+ phylink_helper_basex_speed(state);
+ }
+--- a/drivers/net/dsa/bcm_sf2.c
++++ b/drivers/net/dsa/bcm_sf2.c
+@@ -686,7 +686,7 @@ static void bcm_sf2_sw_validate(struct d
+ state->interface != PHY_INTERFACE_MODE_GMII &&
+ state->interface != PHY_INTERFACE_MODE_INTERNAL &&
+ state->interface != PHY_INTERFACE_MODE_MOCA) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ if (port != core_readl(priv, CORE_IMP0_PRT_ID))
+ dev_err(ds->dev,
+ "Unsupported interface: %d for port %d\n",
+@@ -714,10 +714,8 @@ static void bcm_sf2_sw_validate(struct d
+ phylink_set(mask, 100baseT_Half);
+ phylink_set(mask, 100baseT_Full);
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
+--- a/drivers/net/dsa/hirschmann/hellcreek.c
++++ b/drivers/net/dsa/hirschmann/hellcreek.c
+@@ -1476,10 +1476,8 @@ static void hellcreek_phylink_validate(s
+ else
+ phylink_set(mask, 1000baseT_Full);
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static int
+--- a/drivers/net/dsa/lantiq_gswip.c
++++ b/drivers/net/dsa/lantiq_gswip.c
+@@ -1452,10 +1452,8 @@ static void gswip_phylink_set_capab(unsi
+ phylink_set(mask, 100baseT_Half);
+ phylink_set(mask, 100baseT_Full);
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static void gswip_xrx200_phylink_validate(struct dsa_switch *ds, int port,
+@@ -1483,7 +1481,7 @@ static void gswip_xrx200_phylink_validat
+ goto unsupported;
+ break;
+ default:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ dev_err(ds->dev, "Unsupported port: %i\n", port);
+ return;
+ }
+@@ -1493,7 +1491,7 @@ static void gswip_xrx200_phylink_validat
+ return;
+
+ unsupported:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ dev_err(ds->dev, "Unsupported interface '%s' for port %d\n",
+ phy_modes(state->interface), port);
+ }
+@@ -1523,7 +1521,7 @@ static void gswip_xrx300_phylink_validat
+ goto unsupported;
+ break;
+ default:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ dev_err(ds->dev, "Unsupported port: %i\n", port);
+ return;
+ }
+@@ -1533,7 +1531,7 @@ static void gswip_xrx300_phylink_validat
+ return;
+
+ unsupported:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ dev_err(ds->dev, "Unsupported interface '%s' for port %d\n",
+ phy_modes(state->interface), port);
+ }
+--- a/drivers/net/dsa/microchip/ksz8795.c
++++ b/drivers/net/dsa/microchip/ksz8795.c
+@@ -1542,15 +1542,13 @@ static void ksz8_validate(struct dsa_swi
+ phylink_set(mask, 100baseT_Half);
+ phylink_set(mask, 100baseT_Full);
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+
+ return;
+
+ unsupported:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ dev_err(ds->dev, "Unsupported interface: %s, port: %d\n",
+ phy_modes(state->interface), port);
+ }
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -683,9 +683,8 @@ static void mv88e6xxx_validate(struct ds
+ if (chip->info->ops->phylink_validate)
+ chip->info->ops->phylink_validate(chip, port, mask, state);
+
+- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+
+ /* We can only operate at 2500BaseX or 1000BaseX. If requested
+ * to advertise both, only report advertising at 2500BaseX.
+--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
++++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
+@@ -944,7 +944,7 @@ static void vsc9959_phylink_validate(str
+
+ if (state->interface != PHY_INTERFACE_MODE_NA &&
+ state->interface != ocelot_port->phy_mode) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+
+@@ -966,10 +966,8 @@ static void vsc9959_phylink_validate(str
+ phylink_set(mask, 2500baseX_Full);
+ }
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static int vsc9959_prevalidate_phy_mode(struct ocelot *ocelot, int port,
+--- a/drivers/net/dsa/ocelot/seville_vsc9953.c
++++ b/drivers/net/dsa/ocelot/seville_vsc9953.c
+@@ -1000,7 +1000,7 @@ static void vsc9953_phylink_validate(str
+
+ if (state->interface != PHY_INTERFACE_MODE_NA &&
+ state->interface != ocelot_port->phy_mode) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+
+@@ -1019,10 +1019,8 @@ static void vsc9953_phylink_validate(str
+ phylink_set(mask, 2500baseX_Full);
+ }
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static int vsc9953_prevalidate_phy_mode(struct ocelot *ocelot, int port,
+--- a/drivers/net/dsa/qca/ar9331.c
++++ b/drivers/net/dsa/qca/ar9331.c
+@@ -522,7 +522,7 @@ static void ar9331_sw_phylink_validate(s
+ goto unsupported;
+ break;
+ default:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ dev_err(ds->dev, "Unsupported port: %i\n", port);
+ return;
+ }
+@@ -536,15 +536,13 @@ static void ar9331_sw_phylink_validate(s
+ phylink_set(mask, 100baseT_Half);
+ phylink_set(mask, 100baseT_Full);
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+
+ return;
+
+ unsupported:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ dev_err(ds->dev, "Unsupported interface: %d, port: %d\n",
+ state->interface, port);
+ }
+--- a/drivers/net/dsa/sja1105/sja1105_main.c
++++ b/drivers/net/dsa/sja1105/sja1105_main.c
+@@ -1360,7 +1360,7 @@ static void sja1105_phylink_validate(str
+ */
+ if (state->interface != PHY_INTERFACE_MODE_NA &&
+ sja1105_phy_mode_mismatch(priv, port, state->interface)) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+
+@@ -1380,9 +1380,8 @@ static void sja1105_phylink_validate(str
+ phylink_set(mask, 2500baseX_Full);
+ }
+
+- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static int
+--- a/drivers/net/dsa/xrs700x/xrs700x.c
++++ b/drivers/net/dsa/xrs700x/xrs700x.c
+@@ -457,7 +457,7 @@ static void xrs700x_phylink_validate(str
+ phylink_set(mask, 1000baseT_Full);
+ break;
+ default:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ dev_err(ds->dev, "Unsupported port: %i\n", port);
+ return;
+ }
+@@ -468,10 +468,8 @@ static void xrs700x_phylink_validate(str
+ phylink_set(mask, 10baseT_Full);
+ phylink_set(mask, 100baseT_Full);
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static void xrs700x_mac_link_up(struct dsa_switch *ds, int port,
+--- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
++++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
+@@ -369,9 +369,8 @@ static int xgbe_set_link_ksettings(struc
+ __ETHTOOL_LINK_MODE_MASK_NBITS, cmd->link_modes.advertising,
+ __ETHTOOL_LINK_MODE_MASK_NBITS, lks->link_modes.supported);
+
+- bitmap_and(advertising,
+- cmd->link_modes.advertising, lks->link_modes.supported,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(advertising, cmd->link_modes.advertising,
++ lks->link_modes.supported);
+
+ if ((cmd->base.autoneg == AUTONEG_ENABLE) &&
+ bitmap_empty(advertising, __ETHTOOL_LINK_MODE_MASK_NBITS)) {
+@@ -384,8 +383,7 @@ static int xgbe_set_link_ksettings(struc
+ pdata->phy.autoneg = cmd->base.autoneg;
+ pdata->phy.speed = speed;
+ pdata->phy.duplex = cmd->base.duplex;
+- bitmap_copy(lks->link_modes.advertising, advertising,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_copy(lks->link_modes.advertising, advertising);
+
+ if (cmd->base.autoneg == AUTONEG_ENABLE)
+ XGBE_SET_ADV(lks, Autoneg);
+--- a/drivers/net/ethernet/atheros/ag71xx.c
++++ b/drivers/net/ethernet/atheros/ag71xx.c
+@@ -1082,14 +1082,12 @@ static void ag71xx_mac_validate(struct p
+ phylink_set(mask, 1000baseX_Full);
+ }
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+
+ return;
+ unsupported:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ }
+
+ static void ag71xx_mac_pcs_get_state(struct phylink_config *config,
+--- a/drivers/net/ethernet/cadence/macb_main.c
++++ b/drivers/net/ethernet/cadence/macb_main.c
+@@ -523,21 +523,21 @@ static void macb_validate(struct phylink
+ state->interface != PHY_INTERFACE_MODE_SGMII &&
+ state->interface != PHY_INTERFACE_MODE_10GBASER &&
+ !phy_interface_mode_is_rgmii(state->interface)) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+
+ if (!macb_is_gem(bp) &&
+ (state->interface == PHY_INTERFACE_MODE_GMII ||
+ phy_interface_mode_is_rgmii(state->interface))) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+
+ if (state->interface == PHY_INTERFACE_MODE_10GBASER &&
+ !(bp->caps & MACB_CAPS_HIGH_SPEED &&
+ bp->caps & MACB_CAPS_PCS)) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+
+@@ -576,9 +576,8 @@ static void macb_validate(struct phylink
+ phylink_set(mask, 1000baseT_Half);
+ }
+ out:
+- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static void macb_usx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
++++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+@@ -965,7 +965,7 @@ static void enetc_pl_mac_validate(struct
+ state->interface != PHY_INTERFACE_MODE_2500BASEX &&
+ state->interface != PHY_INTERFACE_MODE_USXGMII &&
+ !phy_interface_mode_is_rgmii(state->interface)) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+
+@@ -988,10 +988,8 @@ static void enetc_pl_mac_validate(struct
+ phylink_set(mask, 2500baseX_Full);
+ }
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static void enetc_pl_mac_config(struct phylink_config *config,
+--- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c
++++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c
+@@ -322,12 +322,10 @@ static int hinic_get_link_ksettings(stru
+ }
+ }
+
+- bitmap_copy(link_ksettings->link_modes.supported,
+- (unsigned long *)&settings.supported,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_copy(link_ksettings->link_modes.advertising,
+- (unsigned long *)&settings.advertising,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_copy(link_ksettings->link_modes.supported,
++ (unsigned long *)&settings.supported);
++ linkmode_copy(link_ksettings->link_modes.advertising,
++ (unsigned long *)&settings.advertising);
+
+ return 0;
+ }
+--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
++++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+@@ -467,9 +467,8 @@ static int ixgbe_set_link_ksettings(stru
+ * this function does not support duplex forcing, but can
+ * limit the advertising of the adapter to the specified speed
+ */
+- if (!bitmap_subset(cmd->link_modes.advertising,
+- cmd->link_modes.supported,
+- __ETHTOOL_LINK_MODE_MASK_NBITS))
++ if (!linkmode_subset(cmd->link_modes.advertising,
++ cmd->link_modes.supported))
+ return -EINVAL;
+
+ /* only allow one speed at a time if no autoneg */
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3835,14 +3835,14 @@ static void mvneta_validate(struct phyli
+ */
+ if (phy_interface_mode_is_8023z(state->interface)) {
+ if (!phylink_test(state->advertising, Autoneg)) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+ } else if (state->interface != PHY_INTERFACE_MODE_NA &&
+ state->interface != PHY_INTERFACE_MODE_QSGMII &&
+ state->interface != PHY_INTERFACE_MODE_SGMII &&
+ !phy_interface_mode_is_rgmii(state->interface)) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+
+@@ -3871,10 +3871,8 @@ static void mvneta_validate(struct phyli
+ phylink_set(mask, 100baseT_Full);
+ }
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+
+ /* We can only operate at 2500BaseX or 1000BaseX. If requested
+ * to advertise both, only report advertising at 2500BaseX.
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+@@ -6347,15 +6347,14 @@ static void mvpp2_phylink_validate(struc
+ goto empty_set;
+ }
+
+- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+
+ phylink_helper_basex_speed(state);
+ return;
+
+ empty_set:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ }
+
+ static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode,
+--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
+@@ -1168,9 +1168,8 @@ static int otx2_set_link_ksettings(struc
+ otx2_get_link_ksettings(netdev, &cur_ks);
+
+ /* Check requested modes against supported modes by hardware */
+- if (!bitmap_subset(cmd->link_modes.advertising,
+- cur_ks.link_modes.supported,
+- __ETHTOOL_LINK_MODE_MASK_NBITS))
++ if (!linkmode_subset(cmd->link_modes.advertising,
++ cur_ks.link_modes.supported))
+ return -EINVAL;
+
+ mutex_lock(&mbox->lock);
+--- a/drivers/net/ethernet/marvell/pxa168_eth.c
++++ b/drivers/net/ethernet/marvell/pxa168_eth.c
+@@ -977,8 +977,7 @@ static int pxa168_init_phy(struct net_de
+ cmd.base.phy_address = pep->phy_addr;
+ cmd.base.speed = pep->phy_speed;
+ cmd.base.duplex = pep->phy_duplex;
+- bitmap_copy(cmd.link_modes.advertising, PHY_BASIC_FEATURES,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_copy(cmd.link_modes.advertising, PHY_BASIC_FEATURES);
+ cmd.base.autoneg = AUTONEG_ENABLE;
+
+ if (cmd.base.speed != 0)
+--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
++++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+@@ -39,6 +39,7 @@
+ #include <linux/in.h>
+ #include <net/ip.h>
+ #include <linux/bitmap.h>
++#include <linux/mii.h>
+
+ #include "mlx4_en.h"
+ #include "en_port.h"
+@@ -643,10 +644,8 @@ static unsigned long *ptys2ethtool_link_
+ unsigned int i; \
+ cfg = &ptys2ethtool_map[reg_]; \
+ cfg->speed = speed_; \
+- bitmap_zero(cfg->supported, \
+- __ETHTOOL_LINK_MODE_MASK_NBITS); \
+- bitmap_zero(cfg->advertised, \
+- __ETHTOOL_LINK_MODE_MASK_NBITS); \
++ linkmode_zero(cfg->supported); \
++ linkmode_zero(cfg->advertised); \
+ for (i = 0 ; i < ARRAY_SIZE(modes) ; ++i) { \
+ __set_bit(modes[i], cfg->supported); \
+ __set_bit(modes[i], cfg->advertised); \
+@@ -702,10 +701,8 @@ static void ptys2ethtool_update_link_mod
+ int i;
+ for (i = 0; i < MLX4_LINK_MODES_SZ; i++) {
+ if (eth_proto & MLX4_PROT_MASK(i))
+- bitmap_or(link_modes, link_modes,
+- ptys2ethtool_link_mode(&ptys2ethtool_map[i],
+- report),
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_or(link_modes, link_modes,
++ ptys2ethtool_link_mode(&ptys2ethtool_map[i], report));
+ }
+ }
+
+@@ -716,11 +713,9 @@ static u32 ethtool2ptys_link_modes(const
+ u32 ptys_modes = 0;
+
+ for (i = 0; i < MLX4_LINK_MODES_SZ; i++) {
+- if (bitmap_intersects(
+- ptys2ethtool_link_mode(&ptys2ethtool_map[i],
+- report),
+- link_modes,
+- __ETHTOOL_LINK_MODE_MASK_NBITS))
++ ulong *map_mode = ptys2ethtool_link_mode(&ptys2ethtool_map[i],
++ report);
++ if (linkmode_intersects(map_mode, link_modes))
+ ptys_modes |= 1 << i;
+ }
+ return ptys_modes;
+--- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
+@@ -92,12 +92,11 @@ static void sparx5_phylink_validate(stru
+ }
+ break;
+ default:
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static void sparx5_phylink_mac_config(struct phylink_config *config,
+--- a/drivers/net/ethernet/mscc/ocelot_net.c
++++ b/drivers/net/ethernet/mscc/ocelot_net.c
+@@ -1509,7 +1509,7 @@ static void vsc7514_phylink_validate(str
+
+ if (state->interface != PHY_INTERFACE_MODE_NA &&
+ state->interface != ocelot_port->phy_mode) {
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+
+@@ -1528,9 +1528,8 @@ static void vsc7514_phylink_validate(str
+ phylink_set(mask, 2500baseT_Full);
+ phylink_set(mask, 2500baseX_Full);
+
+- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static void vsc7514_phylink_mac_config(struct phylink_config *config,
+--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
++++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+@@ -228,8 +228,7 @@ static int ionic_get_link_ksettings(stru
+ break;
+ }
+
+- bitmap_copy(ks->link_modes.advertising, ks->link_modes.supported,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_copy(ks->link_modes.advertising, ks->link_modes.supported);
+
+ ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER);
+ ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS);
+--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
++++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+@@ -1565,7 +1565,7 @@ static void axienet_validate(struct phyl
+ netdev_warn(ndev, "Cannot use PHY mode %s, supported: %s\n",
+ phy_modes(state->interface),
+ phy_modes(lp->phy_mode));
+- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(supported);
+ return;
+ }
+ }
+@@ -1598,10 +1598,8 @@ static void axienet_validate(struct phyl
+ break;
+ }
+
+- bitmap_and(supported, supported, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
+- bitmap_and(state->advertising, state->advertising, mask,
+- __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+ static void axienet_mac_pcs_get_state(struct phylink_config *config,
+--- a/drivers/net/pcs/pcs-xpcs.c
++++ b/drivers/net/pcs/pcs-xpcs.c
+@@ -637,7 +637,7 @@ void xpcs_validate(struct dw_xpcs *xpcs,
+ if (state->interface == PHY_INTERFACE_MODE_NA)
+ return;
+
+- bitmap_zero(xpcs_supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(xpcs_supported);
+
+ compat = xpcs_find_compat(xpcs->id, state->interface);
+
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -379,7 +379,7 @@ void sfp_parse_support(struct sfp_bus *b
+ if (bus->sfp_quirk)
+ bus->sfp_quirk->modes(id, modes);
+
+- bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_or(support, support, modes);
+
+ phylink_set(support, Autoneg);
+ phylink_set(support, Pause);
+--- a/net/ethtool/ioctl.c
++++ b/net/ethtool/ioctl.c
+@@ -335,7 +335,7 @@ EXPORT_SYMBOL(ethtool_intersect_link_mas
+ void ethtool_convert_legacy_u32_to_link_mode(unsigned long *dst,
+ u32 legacy_u32)
+ {
+- bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(dst);
+ dst[0] = legacy_u32;
+ }
+ EXPORT_SYMBOL(ethtool_convert_legacy_u32_to_link_mode);
+@@ -350,11 +350,10 @@ bool ethtool_convert_link_mode_to_legacy
+ if (__ETHTOOL_LINK_MODE_MASK_NBITS > 32) {
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(ext);
+
+- bitmap_zero(ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
++ linkmode_zero(ext);
+ bitmap_fill(ext, 32);
+ bitmap_complement(ext, ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
+- if (bitmap_intersects(ext, src,
+- __ETHTOOL_LINK_MODE_MASK_NBITS)) {
++ if (linkmode_intersects(ext, src)) {
+ /* src mask goes beyond bit 31 */
+ retval = false;
+ }
diff --git a/target/linux/generic/backport-6.1/703-01-v5.16-net-phylink-add-MAC-phy_interface_t-bitmap.patch b/target/linux/generic/backport-6.1/703-01-v5.16-net-phylink-add-MAC-phy_interface_t-bitmap.patch
new file mode 100644
index 0000000000..885c2fcf25
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-01-v5.16-net-phylink-add-MAC-phy_interface_t-bitmap.patch
@@ -0,0 +1,24 @@
+From 38c310eb46f5f80213a92093af11af270c209a76 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 26 Oct 2021 11:06:06 +0100
+Subject: [PATCH] net: phylink: add MAC phy_interface_t bitmap
+
+Add a phy_interface_t bitmap so the MAC driver can specifiy which PHY
+interface modes it supports.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/phylink.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -78,6 +78,7 @@ struct phylink_config {
+ bool ovr_an_inband;
+ void (*get_fixed_state)(struct phylink_config *config,
+ struct phylink_link_state *state);
++ DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
+ };
+
+ /**
diff --git a/target/linux/generic/backport-6.1/703-02-v5.16-net-phylink-use-supported_interfaces-for-phylink-val.patch b/target/linux/generic/backport-6.1/703-02-v5.16-net-phylink-use-supported_interfaces-for-phylink-val.patch
new file mode 100644
index 0000000000..9800884f6e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-02-v5.16-net-phylink-use-supported_interfaces-for-phylink-val.patch
@@ -0,0 +1,98 @@
+From d25f3a74f30aace819163dfa54f2a4b8ca1dc932 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 26 Oct 2021 11:06:11 +0100
+Subject: [PATCH] net: phylink: use supported_interfaces for phylink
+ validation
+
+If the network device supplies a supported interface bitmap, we can use
+that during phylink's validation to simplify MAC drivers in two ways by
+using the supported_interfaces bitmap to:
+
+1. reject unsupported interfaces before calling into the MAC driver.
+2. generate the set of all supported link modes across all supported
+ interfaces (used mainly for SFP, but also some 10G PHYs.)
+
+Suggested-by: Sean Anderson <sean.anderson@seco.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/phylink.c | 36 ++++++++++++++++++++++++++++++++++++
+ include/linux/phylink.h | 12 ++++++++++--
+ 2 files changed, 46 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -155,9 +155,45 @@ static const char *phylink_an_mode_str(u
+ return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
+ }
+
++static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, };
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
++ struct phylink_link_state t;
++ int intf;
++
++ for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
++ if (test_bit(intf, pl->config->supported_interfaces)) {
++ linkmode_copy(s, supported);
++
++ t = *state;
++ t.interface = intf;
++ pl->mac_ops->validate(pl->config, s, &t);
++ linkmode_or(all_s, all_s, s);
++ linkmode_or(all_adv, all_adv, t.advertising);
++ }
++ }
++
++ linkmode_copy(supported, all_s);
++ linkmode_copy(state->advertising, all_adv);
++
++ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
++}
++
+ static int phylink_validate(struct phylink *pl, unsigned long *supported,
+ struct phylink_link_state *state)
+ {
++ if (!phy_interface_empty(pl->config->supported_interfaces)) {
++ if (state->interface == PHY_INTERFACE_MODE_NA)
++ return phylink_validate_any(pl, supported, state);
++
++ if (!test_bit(state->interface,
++ pl->config->supported_interfaces))
++ return -EINVAL;
++ }
++
+ pl->mac_ops->validate(pl->config, supported, state);
+
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -68,6 +68,8 @@ enum phylink_op_type {
+ * @ovr_an_inband: if true, override PCS to MLO_AN_INBAND
+ * @get_fixed_state: callback to execute to determine the fixed link state,
+ * if MAC link is at %MLO_AN_FIXED mode.
++ * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx
++ * are supported by the MAC/PCS.
+ */
+ struct phylink_config {
+ struct device *dev;
+@@ -136,8 +138,14 @@ struct phylink_mac_ops {
+ * based on @state->advertising and/or @state->speed and update
+ * @state->interface accordingly. See phylink_helper_basex_speed().
+ *
+- * When @state->interface is %PHY_INTERFACE_MODE_NA, phylink expects the
+- * MAC driver to return all supported link modes.
++ * When @config->supported_interfaces has been set, phylink will iterate
++ * over the supported interfaces to determine the full capability of the
++ * MAC. The validation function must not print errors if @state->interface
++ * is set to an unexpected value.
++ *
++ * When @config->supported_interfaces is empty, phylink will call this
++ * function with @state->interface set to %PHY_INTERFACE_MODE_NA, and
++ * expects the MAC driver to return all supported link modes.
+ *
+ * If the @state->interface mode is not supported, then the @supported
+ * mask must be cleared.
diff --git a/target/linux/generic/backport-6.1/703-03-v5.16-net-dsa-populate-supported_interfaces-member.patch b/target/linux/generic/backport-6.1/703-03-v5.16-net-dsa-populate-supported_interfaces-member.patch
new file mode 100644
index 0000000000..b10e6da0e9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-03-v5.16-net-dsa-populate-supported_interfaces-member.patch
@@ -0,0 +1,63 @@
+From c07c6e8eb4b38bae921f9e2f108d1e7f8e14226e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Thu, 28 Oct 2021 18:00:14 +0100
+Subject: [PATCH] net: dsa: populate supported_interfaces member
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add a new DSA switch operation, phylink_get_interfaces, which should
+fill in which PHY_INTERFACE_MODE_* are supported by given port.
+
+Use this before phylink_create() to fill phylinks supported_interfaces
+member, allowing phylink to determine which PHY_INTERFACE_MODEs are
+supported.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+[tweaked patch and description to add more complete support -- rmk]
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 2 ++
+ net/dsa/port.c | 4 ++++
+ net/dsa/slave.c | 4 ++++
+ 3 files changed, 10 insertions(+)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -626,6 +626,8 @@ struct dsa_switch_ops {
+ /*
+ * PHYLINK integration
+ */
++ void (*phylink_get_interfaces)(struct dsa_switch *ds, int port,
++ unsigned long *supported_interfaces);
+ void (*phylink_validate)(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state);
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1188,6 +1188,10 @@ static int dsa_port_phylink_register(str
+ dp->pl_config.type = PHYLINK_DEV;
+ dp->pl_config.pcs_poll = ds->pcs_poll;
+
++ if (ds->ops->phylink_get_interfaces)
++ ds->ops->phylink_get_interfaces(ds, dp->index,
++ dp->pl_config.supported_interfaces);
++
+ dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
+ mode, &dsa_port_phylink_mac_ops);
+ if (IS_ERR(dp->pl)) {
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1837,6 +1837,10 @@ static int dsa_slave_phy_setup(struct ne
+ dp->pl_config.poll_fixed_state = true;
+ }
+
++ if (ds->ops->phylink_get_interfaces)
++ ds->ops->phylink_get_interfaces(ds, dp->index,
++ dp->pl_config.supported_interfaces);
++
+ dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode,
+ &dsa_port_phylink_mac_ops);
+ if (IS_ERR(dp->pl)) {
diff --git a/target/linux/generic/backport-6.1/703-04-v5.17-net-dsa-consolidate-phylink-creation.patch b/target/linux/generic/backport-6.1/703-04-v5.17-net-dsa-consolidate-phylink-creation.patch
new file mode 100644
index 0000000000..9b67a8a518
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-04-v5.17-net-dsa-consolidate-phylink-creation.patch
@@ -0,0 +1,149 @@
+From 21bd64bd717dedac96f53b668144cbe37d3c12d4 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 30 Nov 2021 13:09:55 +0000
+Subject: [PATCH] net: dsa: consolidate phylink creation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The code in port.c and slave.c creating the phylink instance is very
+similar - let's consolidate this into a single function.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Marek Behún <kabel@kernel.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ net/dsa/dsa_priv.h | 2 +-
+ net/dsa/port.c | 44 ++++++++++++++++++++++++++++----------------
+ net/dsa/slave.c | 19 +++----------------
+ 3 files changed, 32 insertions(+), 33 deletions(-)
+
+--- a/net/dsa/dsa_priv.h
++++ b/net/dsa/dsa_priv.h
+@@ -261,13 +261,13 @@ int dsa_port_mrp_add_ring_role(const str
+ const struct switchdev_obj_ring_role_mrp *mrp);
+ int dsa_port_mrp_del_ring_role(const struct dsa_port *dp,
+ const struct switchdev_obj_ring_role_mrp *mrp);
++int dsa_port_phylink_create(struct dsa_port *dp);
+ int dsa_port_link_register_of(struct dsa_port *dp);
+ void dsa_port_link_unregister_of(struct dsa_port *dp);
+ int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr);
+ void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr);
+ int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast);
+ void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast);
+-extern const struct phylink_mac_ops dsa_port_phylink_mac_ops;
+
+ static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp,
+ const struct net_device *dev)
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1092,7 +1092,7 @@ static void dsa_port_phylink_mac_link_up
+ speed, duplex, tx_pause, rx_pause);
+ }
+
+-const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
++static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
+ .validate = dsa_port_phylink_validate,
+ .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
+ .mac_config = dsa_port_phylink_mac_config,
+@@ -1101,6 +1101,30 @@ const struct phylink_mac_ops dsa_port_ph
+ .mac_link_up = dsa_port_phylink_mac_link_up,
+ };
+
++int dsa_port_phylink_create(struct dsa_port *dp)
++{
++ struct dsa_switch *ds = dp->ds;
++ phy_interface_t mode;
++ int err;
++
++ err = of_get_phy_mode(dp->dn, &mode);
++ if (err)
++ mode = PHY_INTERFACE_MODE_NA;
++
++ if (ds->ops->phylink_get_interfaces)
++ ds->ops->phylink_get_interfaces(ds, dp->index,
++ dp->pl_config.supported_interfaces);
++
++ dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
++ mode, &dsa_port_phylink_mac_ops);
++ if (IS_ERR(dp->pl)) {
++ pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
++ return PTR_ERR(dp->pl);
++ }
++
++ return 0;
++}
++
+ static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
+ {
+ struct dsa_switch *ds = dp->ds;
+@@ -1177,27 +1201,15 @@ static int dsa_port_phylink_register(str
+ {
+ struct dsa_switch *ds = dp->ds;
+ struct device_node *port_dn = dp->dn;
+- phy_interface_t mode;
+ int err;
+
+- err = of_get_phy_mode(port_dn, &mode);
+- if (err)
+- mode = PHY_INTERFACE_MODE_NA;
+-
+ dp->pl_config.dev = ds->dev;
+ dp->pl_config.type = PHYLINK_DEV;
+ dp->pl_config.pcs_poll = ds->pcs_poll;
+
+- if (ds->ops->phylink_get_interfaces)
+- ds->ops->phylink_get_interfaces(ds, dp->index,
+- dp->pl_config.supported_interfaces);
+-
+- dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
+- mode, &dsa_port_phylink_mac_ops);
+- if (IS_ERR(dp->pl)) {
+- pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
+- return PTR_ERR(dp->pl);
+- }
++ err = dsa_port_phylink_create(dp);
++ if (err)
++ return err;
+
+ err = phylink_of_phy_connect(dp->pl, port_dn, 0);
+ if (err && err != -ENODEV) {
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1817,14 +1817,9 @@ static int dsa_slave_phy_setup(struct ne
+ struct dsa_port *dp = dsa_slave_to_port(slave_dev);
+ struct device_node *port_dn = dp->dn;
+ struct dsa_switch *ds = dp->ds;
+- phy_interface_t mode;
+ u32 phy_flags = 0;
+ int ret;
+
+- ret = of_get_phy_mode(port_dn, &mode);
+- if (ret)
+- mode = PHY_INTERFACE_MODE_NA;
+-
+ dp->pl_config.dev = &slave_dev->dev;
+ dp->pl_config.type = PHYLINK_NETDEV;
+
+@@ -1837,17 +1832,9 @@ static int dsa_slave_phy_setup(struct ne
+ dp->pl_config.poll_fixed_state = true;
+ }
+
+- if (ds->ops->phylink_get_interfaces)
+- ds->ops->phylink_get_interfaces(ds, dp->index,
+- dp->pl_config.supported_interfaces);
+-
+- dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode,
+- &dsa_port_phylink_mac_ops);
+- if (IS_ERR(dp->pl)) {
+- netdev_err(slave_dev,
+- "error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
+- return PTR_ERR(dp->pl);
+- }
++ ret = dsa_port_phylink_create(dp);
++ if (ret)
++ return ret;
+
+ if (ds->ops->get_phy_flags)
+ phy_flags = ds->ops->get_phy_flags(ds, dp->index);
diff --git a/target/linux/generic/backport-6.1/703-05-v5.17-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch b/target/linux/generic/backport-6.1/703-05-v5.17-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch
new file mode 100644
index 0000000000..d70b9aa6a4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-05-v5.17-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch
@@ -0,0 +1,51 @@
+From 072eea6c22b2af680c3949e64f9adde278c71e68 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 30 Nov 2021 13:10:01 +0000
+Subject: [PATCH] net: dsa: replace phylink_get_interfaces() with
+ phylink_get_caps()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Phylink needs slightly more information than phylink_get_interfaces()
+allows us to get from the DSA drivers - we need the MAC capabilities.
+Replace the phylink_get_interfaces() method with phylink_get_caps() to
+allow DSA drivers to fill in the phylink_config MAC capabilities field
+as well.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Marek Behún <kabel@kernel.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ include/net/dsa.h | 4 ++--
+ net/dsa/port.c | 5 ++---
+ 2 files changed, 4 insertions(+), 5 deletions(-)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -626,8 +626,8 @@ struct dsa_switch_ops {
+ /*
+ * PHYLINK integration
+ */
+- void (*phylink_get_interfaces)(struct dsa_switch *ds, int port,
+- unsigned long *supported_interfaces);
++ void (*phylink_get_caps)(struct dsa_switch *ds, int port,
++ struct phylink_config *config);
+ void (*phylink_validate)(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state);
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1111,9 +1111,8 @@ int dsa_port_phylink_create(struct dsa_p
+ if (err)
+ mode = PHY_INTERFACE_MODE_NA;
+
+- if (ds->ops->phylink_get_interfaces)
+- ds->ops->phylink_get_interfaces(ds, dp->index,
+- dp->pl_config.supported_interfaces);
++ if (ds->ops->phylink_get_caps)
++ ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config);
+
+ dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
+ mode, &dsa_port_phylink_mac_ops);
diff --git a/target/linux/generic/backport-6.1/703-06-v5.18-net-dsa-add-support-for-phylink-mac_select_pcs.patch b/target/linux/generic/backport-6.1/703-06-v5.18-net-dsa-add-support-for-phylink-mac_select_pcs.patch
new file mode 100644
index 0000000000..2af5cb9d9d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-06-v5.18-net-dsa-add-support-for-phylink-mac_select_pcs.patch
@@ -0,0 +1,59 @@
+From bde018222c6b084ac32933a9f933581dd83da18e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 17 Feb 2022 18:30:35 +0000
+Subject: [PATCH] net: dsa: add support for phylink mac_select_pcs()
+
+Add DSA support for the phylink mac_select_pcs() method so DSA drivers
+can return provide phylink with the appropriate PCS for the PHY
+interface mode.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 3 +++
+ net/dsa/port.c | 15 +++++++++++++++
+ 2 files changed, 18 insertions(+)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -631,6 +631,9 @@ struct dsa_switch_ops {
+ void (*phylink_validate)(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state);
++ struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds,
++ int port,
++ phy_interface_t iface);
+ int (*phylink_mac_link_state)(struct dsa_switch *ds, int port,
+ struct phylink_link_state *state);
+ void (*phylink_mac_config)(struct dsa_switch *ds, int port,
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1028,6 +1028,20 @@ static void dsa_port_phylink_mac_pcs_get
+ }
+ }
+
++static struct phylink_pcs *
++dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
++ phy_interface_t interface)
++{
++ struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
++ struct dsa_switch *ds = dp->ds;
++ struct phylink_pcs *pcs = NULL;
++
++ if (ds->ops->phylink_mac_select_pcs)
++ pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface);
++
++ return pcs;
++}
++
+ static void dsa_port_phylink_mac_config(struct phylink_config *config,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+@@ -1094,6 +1108,7 @@ static void dsa_port_phylink_mac_link_up
+
+ static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
+ .validate = dsa_port_phylink_validate,
++ .mac_select_pcs = dsa_port_phylink_mac_select_pcs,
+ .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
+ .mac_config = dsa_port_phylink_mac_config,
+ .mac_an_restart = dsa_port_phylink_mac_an_restart,
diff --git a/target/linux/generic/backport-6.1/703-07-v5.16-net-phy-add-phy_interface_t-bitmap-support.patch b/target/linux/generic/backport-6.1/703-07-v5.16-net-phy-add-phy_interface_t-bitmap-support.patch
new file mode 100644
index 0000000000..1a7817b0f9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-07-v5.16-net-phy-add-phy_interface_t-bitmap-support.patch
@@ -0,0 +1,61 @@
+From 8e20f591f204f8db7f1182918f8e2285d3f589e0 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 26 Oct 2021 11:06:01 +0100
+Subject: [PATCH] net: phy: add phy_interface_t bitmap support
+
+Add support for a bitmap for phy interface modes, which includes:
+- a macro to declare the interface bitmap
+- an inline helper to zero the interface bitmap
+- an inline helper to detect an empty interface bitmap
+- inline helpers to do a bitwise AND and OR operations on two interface
+ bitmaps
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/phy.h | 34 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -155,6 +155,40 @@ typedef enum {
+ PHY_INTERFACE_MODE_MAX,
+ } phy_interface_t;
+
++/* PHY interface mode bitmap handling */
++#define DECLARE_PHY_INTERFACE_MASK(name) \
++ DECLARE_BITMAP(name, PHY_INTERFACE_MODE_MAX)
++
++static inline void phy_interface_zero(unsigned long *intf)
++{
++ bitmap_zero(intf, PHY_INTERFACE_MODE_MAX);
++}
++
++static inline bool phy_interface_empty(const unsigned long *intf)
++{
++ return bitmap_empty(intf, PHY_INTERFACE_MODE_MAX);
++}
++
++static inline void phy_interface_and(unsigned long *dst, const unsigned long *a,
++ const unsigned long *b)
++{
++ bitmap_and(dst, a, b, PHY_INTERFACE_MODE_MAX);
++}
++
++static inline void phy_interface_or(unsigned long *dst, const unsigned long *a,
++ const unsigned long *b)
++{
++ bitmap_or(dst, a, b, PHY_INTERFACE_MODE_MAX);
++}
++
++static inline void phy_interface_set_rgmii(unsigned long *intf)
++{
++ __set_bit(PHY_INTERFACE_MODE_RGMII, intf);
++ __set_bit(PHY_INTERFACE_MODE_RGMII_ID, intf);
++ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, intf);
++ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, intf);
++}
++
+ /*
+ * phy_supported_speeds - return all speeds currently supported by a PHY device
+ */
diff --git a/target/linux/generic/backport-6.1/703-08-v5.17-net-phylink-add-mac_select_pcs-method-to-phylink_mac.patch b/target/linux/generic/backport-6.1/703-08-v5.17-net-phylink-add-mac_select_pcs-method-to-phylink_mac.patch
new file mode 100644
index 0000000000..d826877e7d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-08-v5.17-net-phylink-add-mac_select_pcs-method-to-phylink_mac.patch
@@ -0,0 +1,197 @@
+From d1e86325af377129adb7fc6f34eb044ca6068b47 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 15 Dec 2021 15:34:15 +0000
+Subject: [PATCH] net: phylink: add mac_select_pcs() method to phylink_mac_ops
+
+mac_select_pcs() allows us to have an explicit point to query which
+PCS the MAC wishes to use for a particular PHY interface mode, thereby
+allowing us to add support to validate the link settings with the PCS.
+
+Phylink will also use this to select the PCS to be used during a major
+configuration event without the MAC driver needing to call
+phylink_set_pcs().
+
+Note that if mac_select_pcs() is present, the supported_interfaces
+bitmap must be filled in; this avoids mac_select_pcs() being called
+with PHY_INTERFACE_MODE_NA when we want to get support for all
+interface types. Phylink will return an error in phylink_create()
+unless this condition is satisfied.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/phylink.c | 68 +++++++++++++++++++++++++++++++++------
+ include/linux/phylink.h | 18 +++++++++++
+ 2 files changed, 77 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -155,6 +155,23 @@ static const char *phylink_an_mode_str(u
+ return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
+ }
+
++static int phylink_validate_mac_and_pcs(struct phylink *pl,
++ unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ struct phylink_pcs *pcs;
++
++ if (pl->mac_ops->mac_select_pcs) {
++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
++ if (IS_ERR(pcs))
++ return PTR_ERR(pcs);
++ }
++
++ pl->mac_ops->validate(pl->config, supported, state);
++
++ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
++}
++
+ static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+@@ -170,9 +187,10 @@ static int phylink_validate_any(struct p
+
+ t = *state;
+ t.interface = intf;
+- pl->mac_ops->validate(pl->config, s, &t);
+- linkmode_or(all_s, all_s, s);
+- linkmode_or(all_adv, all_adv, t.advertising);
++ if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
++ linkmode_or(all_s, all_s, s);
++ linkmode_or(all_adv, all_adv, t.advertising);
++ }
+ }
+ }
+
+@@ -194,9 +212,7 @@ static int phylink_validate(struct phyli
+ return -EINVAL;
+ }
+
+- pl->mac_ops->validate(pl->config, supported, state);
+-
+- return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
++ return phylink_validate_mac_and_pcs(pl, supported, state);
+ }
+
+ static int phylink_parse_fixedlink(struct phylink *pl,
+@@ -486,10 +502,21 @@ static void phylink_mac_pcs_an_restart(s
+ static void phylink_major_config(struct phylink *pl, bool restart,
+ const struct phylink_link_state *state)
+ {
++ struct phylink_pcs *pcs = NULL;
+ int err;
+
+ phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
+
++ if (pl->mac_ops->mac_select_pcs) {
++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
++ if (IS_ERR(pcs)) {
++ phylink_err(pl,
++ "mac_select_pcs unexpectedly failed: %pe\n",
++ pcs);
++ return;
++ }
++ }
++
+ if (pl->mac_ops->mac_prepare) {
+ err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
+ state->interface);
+@@ -500,6 +527,12 @@ static void phylink_major_config(struct
+ }
+ }
+
++ /* If we have a new PCS, switch to the new PCS after preparing the MAC
++ * for the change.
++ */
++ if (pcs)
++ phylink_set_pcs(pl, pcs);
++
+ phylink_mac_config(pl, state);
+
+ if (pl->pcs_ops) {
+@@ -879,6 +912,14 @@ struct phylink *phylink_create(struct ph
+ struct phylink *pl;
+ int ret;
+
++ /* Validate the supplied configuration */
++ if (mac_ops->mac_select_pcs &&
++ phy_interface_empty(config->supported_interfaces)) {
++ dev_err(config->dev,
++ "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
++ return ERR_PTR(-EINVAL);
++ }
++
+ pl = kzalloc(sizeof(*pl), GFP_KERNEL);
+ if (!pl)
+ return ERR_PTR(-ENOMEM);
+@@ -946,9 +987,10 @@ EXPORT_SYMBOL_GPL(phylink_create);
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ * @pcs: a pointer to the &struct phylink_pcs
+ *
+- * Bind the MAC PCS to phylink. This may be called after phylink_create(),
+- * in mac_prepare() or mac_config() methods if it is desired to dynamically
+- * change the PCS.
++ * Bind the MAC PCS to phylink. This may be called after phylink_create().
++ * If it is desired to dynamically change the PCS, then the preferred method
++ * is to use mac_select_pcs(), but it may also be called in mac_prepare()
++ * or mac_config().
+ *
+ * Please note that there are behavioural changes with the mac_config()
+ * callback if a PCS is present (denoting a newer setup) so removing a PCS
+@@ -959,6 +1001,14 @@ void phylink_set_pcs(struct phylink *pl,
+ {
+ pl->pcs = pcs;
+ pl->pcs_ops = pcs->ops;
++
++ if (!pl->phylink_disable_state &&
++ pl->cfg_link_an_mode == MLO_AN_INBAND) {
++ if (pl->config->pcs_poll || pcs->poll)
++ mod_timer(&pl->link_poll, jiffies + HZ);
++ else
++ del_timer(&pl->link_poll);
++ }
+ }
+ EXPORT_SYMBOL_GPL(phylink_set_pcs);
+
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -86,6 +86,7 @@ struct phylink_config {
+ /**
+ * struct phylink_mac_ops - MAC operations structure.
+ * @validate: Validate and update the link configuration.
++ * @mac_select_pcs: Select a PCS for the interface mode.
+ * @mac_pcs_get_state: Read the current link state from the hardware.
+ * @mac_prepare: prepare for a major reconfiguration of the interface.
+ * @mac_config: configure the MAC for the selected mode and state.
+@@ -100,6 +101,8 @@ struct phylink_mac_ops {
+ void (*validate)(struct phylink_config *config,
+ unsigned long *supported,
+ struct phylink_link_state *state);
++ struct phylink_pcs *(*mac_select_pcs)(struct phylink_config *config,
++ phy_interface_t interface);
+ void (*mac_pcs_get_state)(struct phylink_config *config,
+ struct phylink_link_state *state);
+ int (*mac_prepare)(struct phylink_config *config, unsigned int mode,
+@@ -152,6 +155,21 @@ struct phylink_mac_ops {
+ */
+ void validate(struct phylink_config *config, unsigned long *supported,
+ struct phylink_link_state *state);
++/**
++ * mac_select_pcs: Select a PCS for the interface mode.
++ * @config: a pointer to a &struct phylink_config.
++ * @interface: PHY interface mode for PCS
++ *
++ * Return the &struct phylink_pcs for the specified interface mode, or
++ * NULL if none is required, or an error pointer on error.
++ *
++ * This must not modify any state. It is used to query which PCS should
++ * be used. Phylink will use this during validation to ensure that the
++ * configuration is valid, and when setting a configuration to internally
++ * set the PCS that will be used.
++ */
++struct phylink_pcs *mac_select_pcs(struct phylink_config *config,
++ phy_interface_t interface);
+
+ /**
+ * mac_pcs_get_state() - Read the current inband link state from the hardware
diff --git a/target/linux/generic/backport-6.1/703-09-v5.17-net-phylink-add-generic-validate-implementation.patch b/target/linux/generic/backport-6.1/703-09-v5.17-net-phylink-add-generic-validate-implementation.patch
new file mode 100644
index 0000000000..f30a566c81
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-09-v5.17-net-phylink-add-generic-validate-implementation.patch
@@ -0,0 +1,341 @@
+From 34ae2c09d46a2d0abd907e139b466f798e4095a8 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 15 Nov 2021 10:00:27 +0000
+Subject: [PATCH] net: phylink: add generic validate implementation
+
+Add a generic validate() implementation using the supported_interfaces
+and a bitmask of MAC pause/speed/duplex capabilities. This allows us
+to entirely eliminate many driver private validate() implementations.
+
+We expose the underlying phylink_get_linkmodes() function so that
+drivers which have special needs can still benefit from conversion.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/phylink.c | 252 ++++++++++++++++++++++++++++++++++++++
+ include/linux/phylink.h | 31 +++++
+ 2 files changed, 283 insertions(+)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -172,6 +172,258 @@ static int phylink_validate_mac_and_pcs(
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+ }
+
++static void phylink_caps_to_linkmodes(unsigned long *linkmodes,
++ unsigned long caps)
++{
++ if (caps & MAC_SYM_PAUSE)
++ __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes);
++
++ if (caps & MAC_ASYM_PAUSE)
++ __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes);
++
++ if (caps & MAC_10HD)
++ __set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, linkmodes);
++
++ if (caps & MAC_10FD)
++ __set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, linkmodes);
++
++ if (caps & MAC_100HD) {
++ __set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, linkmodes);
++ }
++
++ if (caps & MAC_100FD) {
++ __set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_1000HD)
++ __set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, linkmodes);
++
++ if (caps & MAC_1000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_2500FD) {
++ __set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_5000FD)
++ __set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, linkmodes);
++
++ if (caps & MAC_10000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_25000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_40000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_50000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_50000baseDR_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_56000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_100000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_200000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, linkmodes);
++ }
++
++ if (caps & MAC_400000FD) {
++ __set_bit(ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
++ linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, linkmodes);
++ __set_bit(ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, linkmodes);
++ }
++}
++
++/**
++ * phylink_get_linkmodes() - get acceptable link modes
++ * @linkmodes: ethtool linkmode mask (must be already initialised)
++ * @interface: phy interface mode defined by &typedef phy_interface_t
++ * @mac_capabilities: bitmask of MAC capabilities
++ *
++ * Set all possible pause, speed and duplex linkmodes in @linkmodes that
++ * are supported by the @interface mode and @mac_capabilities. @linkmodes
++ * must have been initialised previously.
++ */
++void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface,
++ unsigned long mac_capabilities)
++{
++ unsigned long caps = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
++
++ switch (interface) {
++ case PHY_INTERFACE_MODE_USXGMII:
++ caps |= MAC_10000FD | MAC_5000FD | MAC_2500FD;
++ fallthrough;
++
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_QSGMII:
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_GMII:
++ caps |= MAC_1000HD | MAC_1000FD;
++ fallthrough;
++
++ case PHY_INTERFACE_MODE_REVRMII:
++ case PHY_INTERFACE_MODE_RMII:
++ case PHY_INTERFACE_MODE_REVMII:
++ case PHY_INTERFACE_MODE_MII:
++ caps |= MAC_10HD | MAC_10FD;
++ fallthrough;
++
++ case PHY_INTERFACE_MODE_100BASEX:
++ caps |= MAC_100HD | MAC_100FD;
++ break;
++
++ case PHY_INTERFACE_MODE_TBI:
++ case PHY_INTERFACE_MODE_MOCA:
++ case PHY_INTERFACE_MODE_RTBI:
++ case PHY_INTERFACE_MODE_1000BASEX:
++ caps |= MAC_1000HD;
++ fallthrough;
++ case PHY_INTERFACE_MODE_TRGMII:
++ caps |= MAC_1000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_2500BASEX:
++ caps |= MAC_2500FD;
++ break;
++
++ case PHY_INTERFACE_MODE_5GBASER:
++ caps |= MAC_5000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_XGMII:
++ case PHY_INTERFACE_MODE_RXAUI:
++ case PHY_INTERFACE_MODE_XAUI:
++ case PHY_INTERFACE_MODE_10GBASER:
++ case PHY_INTERFACE_MODE_10GKR:
++ caps |= MAC_10000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_25GBASER:
++ caps |= MAC_25000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_XLGMII:
++ caps |= MAC_40000FD;
++ break;
++
++ case PHY_INTERFACE_MODE_INTERNAL:
++ caps |= ~0;
++ break;
++
++ case PHY_INTERFACE_MODE_NA:
++ case PHY_INTERFACE_MODE_MAX:
++ case PHY_INTERFACE_MODE_SMII:
++ break;
++ }
++
++ phylink_caps_to_linkmodes(linkmodes, caps & mac_capabilities);
++}
++EXPORT_SYMBOL_GPL(phylink_get_linkmodes);
++
++/**
++ * phylink_generic_validate() - generic validate() callback implementation
++ * @config: a pointer to a &struct phylink_config.
++ * @supported: ethtool bitmask for supported link modes.
++ * @state: a pointer to a &struct phylink_link_state.
++ *
++ * Generic implementation of the validate() callback that MAC drivers can
++ * use when they pass the range of supported interfaces and MAC capabilities.
++ * This makes use of phylink_get_linkmodes().
++ */
++void phylink_generic_validate(struct phylink_config *config,
++ unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
++
++ phylink_set_port_modes(mask);
++ phylink_set(mask, Autoneg);
++ phylink_get_linkmodes(mask, state->interface, config->mac_capabilities);
++
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
++}
++EXPORT_SYMBOL_GPL(phylink_generic_validate);
++
+ static int phylink_validate_any(struct phylink *pl, unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -20,6 +20,29 @@ enum {
+ MLO_AN_PHY = 0, /* Conventional PHY */
+ MLO_AN_FIXED, /* Fixed-link mode */
+ MLO_AN_INBAND, /* In-band protocol */
++
++ MAC_SYM_PAUSE = BIT(0),
++ MAC_ASYM_PAUSE = BIT(1),
++ MAC_10HD = BIT(2),
++ MAC_10FD = BIT(3),
++ MAC_10 = MAC_10HD | MAC_10FD,
++ MAC_100HD = BIT(4),
++ MAC_100FD = BIT(5),
++ MAC_100 = MAC_100HD | MAC_100FD,
++ MAC_1000HD = BIT(6),
++ MAC_1000FD = BIT(7),
++ MAC_1000 = MAC_1000HD | MAC_1000FD,
++ MAC_2500FD = BIT(8),
++ MAC_5000FD = BIT(9),
++ MAC_10000FD = BIT(10),
++ MAC_20000FD = BIT(11),
++ MAC_25000FD = BIT(12),
++ MAC_40000FD = BIT(13),
++ MAC_50000FD = BIT(14),
++ MAC_56000FD = BIT(15),
++ MAC_100000FD = BIT(16),
++ MAC_200000FD = BIT(17),
++ MAC_400000FD = BIT(18),
+ };
+
+ static inline bool phylink_autoneg_inband(unsigned int mode)
+@@ -70,6 +93,7 @@ enum phylink_op_type {
+ * if MAC link is at %MLO_AN_FIXED mode.
+ * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx
+ * are supported by the MAC/PCS.
++ * @mac_capabilities: MAC pause/speed/duplex capabilities.
+ */
+ struct phylink_config {
+ struct device *dev;
+@@ -81,6 +105,7 @@ struct phylink_config {
+ void (*get_fixed_state)(struct phylink_config *config,
+ struct phylink_link_state *state);
+ DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
++ unsigned long mac_capabilities;
+ };
+
+ /**
+@@ -462,6 +487,12 @@ void pcs_link_up(struct phylink_pcs *pcs
+ phy_interface_t interface, int speed, int duplex);
+ #endif
+
++void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface,
++ unsigned long mac_capabilities);
++void phylink_generic_validate(struct phylink_config *config,
++ unsigned long *supported,
++ struct phylink_link_state *state);
++
+ struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *,
+ phy_interface_t iface,
+ const struct phylink_mac_ops *mac_ops);
diff --git a/target/linux/generic/backport-6.1/703-10-v5.16-net-dsa-introduce-helpers-for-iterating-through-port.patch b/target/linux/generic/backport-6.1/703-10-v5.16-net-dsa-introduce-helpers-for-iterating-through-port.patch
new file mode 100644
index 0000000000..a55623519c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-10-v5.16-net-dsa-introduce-helpers-for-iterating-through-port.patch
@@ -0,0 +1,68 @@
+From 82b318983c515f29b8b3a0dad9f6a5fe8a68a7f4 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 20 Oct 2021 20:49:49 +0300
+Subject: [PATCH] net: dsa: introduce helpers for iterating through ports using
+ dp
+
+Since the DSA conversion from the ds->ports array into the dst->ports
+list, the DSA API has encouraged driver writers, as well as the core
+itself, to write inefficient code.
+
+Currently, code that wants to filter by a specific type of port when
+iterating, like {!unused, user, cpu, dsa}, uses the dsa_is_*_port helper.
+Under the hood, this uses dsa_to_port which iterates again through
+dst->ports. But the driver iterates through the port list already, so
+the complexity is quadratic for the typical case of a single-switch
+tree.
+
+This patch introduces some iteration helpers where the iterator is
+already a struct dsa_port *dp, so that the other variant of the
+filtering functions, dsa_port_is_{unused,user,cpu_dsa}, can be used
+directly on the iterator. This eliminates the second lookup.
+
+These functions can be used both by the core and by drivers.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -476,6 +476,34 @@ static inline bool dsa_is_user_port(stru
+ return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_USER;
+ }
+
++#define dsa_tree_for_each_user_port(_dp, _dst) \
++ list_for_each_entry((_dp), &(_dst)->ports, list) \
++ if (dsa_port_is_user((_dp)))
++
++#define dsa_switch_for_each_port(_dp, _ds) \
++ list_for_each_entry((_dp), &(_ds)->dst->ports, list) \
++ if ((_dp)->ds == (_ds))
++
++#define dsa_switch_for_each_port_safe(_dp, _next, _ds) \
++ list_for_each_entry_safe((_dp), (_next), &(_ds)->dst->ports, list) \
++ if ((_dp)->ds == (_ds))
++
++#define dsa_switch_for_each_port_continue_reverse(_dp, _ds) \
++ list_for_each_entry_continue_reverse((_dp), &(_ds)->dst->ports, list) \
++ if ((_dp)->ds == (_ds))
++
++#define dsa_switch_for_each_available_port(_dp, _ds) \
++ dsa_switch_for_each_port((_dp), (_ds)) \
++ if (!dsa_port_is_unused((_dp)))
++
++#define dsa_switch_for_each_user_port(_dp, _ds) \
++ dsa_switch_for_each_port((_dp), (_ds)) \
++ if (dsa_port_is_user((_dp)))
++
++#define dsa_switch_for_each_cpu_port(_dp, _ds) \
++ dsa_switch_for_each_port((_dp), (_ds)) \
++ if (dsa_port_is_cpu((_dp)))
++
+ static inline u32 dsa_user_ports(struct dsa_switch *ds)
+ {
+ u32 mask = 0;
diff --git a/target/linux/generic/backport-6.1/703-11-v5.17-net-phylink-add-pcs_validate-method.patch b/target/linux/generic/backport-6.1/703-11-v5.17-net-phylink-add-pcs_validate-method.patch
new file mode 100644
index 0000000000..524ce9bd92
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-11-v5.17-net-phylink-add-pcs_validate-method.patch
@@ -0,0 +1,106 @@
+From 0d22d4b626a4eaa3196019092eb6c1919e9f8caa Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 15 Dec 2021 15:34:20 +0000
+Subject: [PATCH] net: phylink: add pcs_validate() method
+
+Add a hook for PCS to validate the link parameters. This avoids MAC
+drivers having to have knowledge of their PCS in their validate()
+method, thereby allowing several MAC drivers to be simplfied.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/phylink.c | 31 +++++++++++++++++++++++++++++++
+ include/linux/phylink.h | 20 ++++++++++++++++++++
+ 2 files changed, 51 insertions(+)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -160,13 +160,44 @@ static int phylink_validate_mac_and_pcs(
+ struct phylink_link_state *state)
+ {
+ struct phylink_pcs *pcs;
++ int ret;
+
++ /* Get the PCS for this interface mode */
+ if (pl->mac_ops->mac_select_pcs) {
+ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
+ if (IS_ERR(pcs))
+ return PTR_ERR(pcs);
++ } else {
++ pcs = pl->pcs;
+ }
+
++ if (pcs) {
++ /* The PCS, if present, must be setup before phylink_create()
++ * has been called. If the ops is not initialised, print an
++ * error and backtrace rather than oopsing the kernel.
++ */
++ if (!pcs->ops) {
++ phylink_err(pl, "interface %s: uninitialised PCS\n",
++ phy_modes(state->interface));
++ dump_stack();
++ return -EINVAL;
++ }
++
++ /* Validate the link parameters with the PCS */
++ if (pcs->ops->pcs_validate) {
++ ret = pcs->ops->pcs_validate(pcs, supported, state);
++ if (ret < 0 || phylink_is_empty_linkmode(supported))
++ return -EINVAL;
++
++ /* Ensure the advertising mask is a subset of the
++ * supported mask.
++ */
++ linkmode_and(state->advertising, state->advertising,
++ supported);
++ }
++ }
++
++ /* Then validate the link parameters with the MAC */
+ pl->mac_ops->validate(pl->config, supported, state);
+
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -398,6 +398,7 @@ struct phylink_pcs {
+
+ /**
+ * struct phylink_pcs_ops - MAC PCS operations structure.
++ * @pcs_validate: validate the link configuration.
+ * @pcs_get_state: read the current MAC PCS link state from the hardware.
+ * @pcs_config: configure the MAC PCS for the selected mode and state.
+ * @pcs_an_restart: restart 802.3z BaseX autonegotiation.
+@@ -405,6 +406,8 @@ struct phylink_pcs {
+ * (where necessary).
+ */
+ struct phylink_pcs_ops {
++ int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
++ const struct phylink_link_state *state);
+ void (*pcs_get_state)(struct phylink_pcs *pcs,
+ struct phylink_link_state *state);
+ int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
+@@ -418,6 +421,23 @@ struct phylink_pcs_ops {
+
+ #if 0 /* For kernel-doc purposes only. */
+ /**
++ * pcs_validate() - validate the link configuration.
++ * @pcs: a pointer to a &struct phylink_pcs.
++ * @supported: ethtool bitmask for supported link modes.
++ * @state: a const pointer to a &struct phylink_link_state.
++ *
++ * Validate the interface mode, and advertising's autoneg bit, removing any
++ * media ethtool link modes that would not be supportable from the supported
++ * mask. Phylink will propagate the changes to the advertising mask. See the
++ * &struct phylink_mac_ops validate() method.
++ *
++ * Returns -EINVAL if the interface mode/autoneg mode is not supported.
++ * Returns non-zero positive if the link state can be supported.
++ */
++int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
++ const struct phylink_link_state *state);
++
++/**
+ * pcs_get_state() - Read the current inband link state from the hardware
+ * @pcs: a pointer to a &struct phylink_pcs.
+ * @state: a pointer to a &struct phylink_link_state.
diff --git a/target/linux/generic/backport-6.1/703-12-v5.17-net-phylink-add-legacy_pre_march2020-indicator.patch b/target/linux/generic/backport-6.1/703-12-v5.17-net-phylink-add-legacy_pre_march2020-indicator.patch
new file mode 100644
index 0000000000..16d5da9c70
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-12-v5.17-net-phylink-add-legacy_pre_march2020-indicator.patch
@@ -0,0 +1,43 @@
+From 3e5b1feccea7db576353ffc302f78d522e4116e6 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 9 Dec 2021 13:11:32 +0000
+Subject: [PATCH] net: phylink: add legacy_pre_march2020 indicator
+
+Add a boolean to phylink_config to indicate whether a driver has not
+been updated for the changes in commit 7cceb599d15d ("net: phylink:
+avoid mac_config calls"), and thus are reliant on the old behaviour.
+
+We were currently keying the phylink behaviour on the presence of a
+PCS, but this is sub-optimal for modern drivers that may not have a
+PCS.
+
+This commit merely introduces the new flag, but does not add any use,
+since we need all legacy drivers to set this flag before it can be
+used. Once these legacy drivers have been updated, we can remove this
+flag.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ include/linux/phylink.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -84,6 +84,8 @@ enum phylink_op_type {
+ * struct phylink_config - PHYLINK configuration structure
+ * @dev: a pointer to a struct device associated with the MAC
+ * @type: operation type of PHYLINK instance
++ * @legacy_pre_march2020: driver has not been updated for March 2020 updates
++ * (See commit 7cceb599d15d ("net: phylink: avoid mac_config calls")
+ * @pcs_poll: MAC PCS cannot provide link change interrupt
+ * @poll_fixed_state: if true, starts link_poll,
+ * if MAC link is at %MLO_AN_FIXED mode.
+@@ -98,6 +100,7 @@ enum phylink_op_type {
+ struct phylink_config {
+ struct device *dev;
+ enum phylink_op_type type;
++ bool legacy_pre_march2020;
+ bool pcs_poll;
+ bool poll_fixed_state;
+ bool mac_managed_pm;
diff --git a/target/linux/generic/backport-6.1/703-13-v5.17-net-dsa-mark-DSA-phylink-as-legacy_pre_march2020.patch b/target/linux/generic/backport-6.1/703-13-v5.17-net-dsa-mark-DSA-phylink-as-legacy_pre_march2020.patch
new file mode 100644
index 0000000000..849881942e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-13-v5.17-net-dsa-mark-DSA-phylink-as-legacy_pre_march2020.patch
@@ -0,0 +1,36 @@
+From 0a9f0794d9bd67e590a9488afe87fbb0419d9539 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 9 Dec 2021 13:11:38 +0000
+Subject: [PATCH] net: dsa: mark DSA phylink as legacy_pre_march2020
+
+The majority of DSA drivers do not make use of the PCS support, and
+thus operate in legacy mode. In order to preserve this behaviour in
+future, we need to set the legacy_pre_march2020 flag so phylink knows
+this may require the legacy calls.
+
+There are some DSA drivers that do make use of PCS support, and these
+will continue operating as before - legacy_pre_march2020 will not
+prevent split-PCS support enabling the newer phylink behaviour.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ net/dsa/port.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1126,6 +1126,13 @@ int dsa_port_phylink_create(struct dsa_p
+ if (err)
+ mode = PHY_INTERFACE_MODE_NA;
+
++ /* Presence of phylink_mac_link_state or phylink_mac_an_restart is
++ * an indicator of a legacy phylink driver.
++ */
++ if (ds->ops->phylink_mac_link_state ||
++ ds->ops->phylink_mac_an_restart)
++ dp->pl_config.legacy_pre_march2020 = true;
++
+ if (ds->ops->phylink_get_caps)
+ ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config);
+
diff --git a/target/linux/generic/backport-6.1/703-14-v5.17-net-phylink-use-legacy_pre_march2020.patch b/target/linux/generic/backport-6.1/703-14-v5.17-net-phylink-use-legacy_pre_march2020.patch
new file mode 100644
index 0000000000..73e53068b8
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-14-v5.17-net-phylink-use-legacy_pre_march2020.patch
@@ -0,0 +1,115 @@
+From 001f4261fe4d5ae710cf1f445b6cae6d9d3ae26e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 9 Dec 2021 13:11:48 +0000
+Subject: [PATCH] net: phylink: use legacy_pre_march2020
+
+Use the legacy flag to indicate whether we should operate in legacy
+mode. This allows us to stop using the presence of a PCS as an
+indicator to the age of the phylink user, and make PCS presence
+optional.
+
+Legacy mode involves:
+1) calling mac_config() whenever the link comes up
+2) calling mac_config() whenever the inband advertisement changes,
+ possibly followed by a call to mac_an_restart()
+3) making use of mac_an_restart()
+4) making use of mac_pcs_get_state()
+
+All the above functionality was moved to a seperate "PCS" block of
+operations in March 2020.
+
+Update the documents to indicate that the differences that this flag
+makes.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 12 ++++++------
+ include/linux/phylink.h | 17 +++++++++++++++++
+ 2 files changed, 23 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -777,7 +777,7 @@ static void phylink_mac_pcs_an_restart(s
+ phylink_autoneg_inband(pl->cur_link_an_mode)) {
+ if (pl->pcs_ops)
+ pl->pcs_ops->pcs_an_restart(pl->pcs);
+- else
++ else if (pl->config->legacy_pre_march2020)
+ pl->mac_ops->mac_an_restart(pl->config);
+ }
+ }
+@@ -855,7 +855,7 @@ static int phylink_change_inband_advert(
+ if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
+ return 0;
+
+- if (!pl->pcs_ops) {
++ if (!pl->pcs_ops && pl->config->legacy_pre_march2020) {
+ /* Legacy method */
+ phylink_mac_config(pl, &pl->link_config);
+ phylink_mac_pcs_an_restart(pl);
+@@ -900,7 +900,8 @@ static void phylink_mac_pcs_get_state(st
+
+ if (pl->pcs_ops)
+ pl->pcs_ops->pcs_get_state(pl->pcs, state);
+- else if (pl->mac_ops->mac_pcs_get_state)
++ else if (pl->mac_ops->mac_pcs_get_state &&
++ pl->config->legacy_pre_march2020)
+ pl->mac_ops->mac_pcs_get_state(pl->config, state);
+ else
+ state->link = 0;
+@@ -1094,12 +1095,11 @@ static void phylink_resolve(struct work_
+ }
+ phylink_major_config(pl, false, &link_state);
+ pl->link_config.interface = link_state.interface;
+- } else if (!pl->pcs_ops) {
++ } else if (!pl->pcs_ops && pl->config->legacy_pre_march2020) {
+ /* The interface remains unchanged, only the speed,
+ * duplex or pause settings have changed. Call the
+ * old mac_config() method to configure the MAC/PCS
+- * only if we do not have a PCS installed (an
+- * unconverted user.)
++ * only if we do not have a legacy MAC driver.
+ */
+ phylink_mac_config(pl, &link_state);
+ }
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -210,6 +210,10 @@ struct phylink_pcs *mac_select_pcs(struc
+ * negotiation completion state in @state->an_complete, and link up state
+ * in @state->link. If possible, @state->lp_advertising should also be
+ * populated.
++ *
++ * Note: This is a legacy method. This function will not be called unless
++ * legacy_pre_march2020 is set in &struct phylink_config and there is no
++ * PCS attached.
+ */
+ void mac_pcs_get_state(struct phylink_config *config,
+ struct phylink_link_state *state);
+@@ -250,6 +254,15 @@ int mac_prepare(struct phylink_config *c
+ * guaranteed to be correct, and so any mac_config() implementation must
+ * never reference these fields.
+ *
++ * Note: For legacy March 2020 drivers (drivers with legacy_pre_march2020 set
++ * in their &phylnk_config and which don't have a PCS), this function will be
++ * called on each link up event, and to also change the in-band advert. For
++ * non-legacy drivers, it will only be called to reconfigure the MAC for a
++ * "major" change in e.g. interface mode. It will not be called for changes
++ * in speed, duplex or pause modes or to change the in-band advertisement.
++ * In any case, it is strongly preferred that speed, duplex and pause settings
++ * are handled in the mac_link_up() method and not in this method.
++ *
+ * (this requires a rewrite - please refer to mac_link_up() for situations
+ * where the PCS and MAC are not tightly integrated.)
+ *
+@@ -334,6 +347,10 @@ int mac_finish(struct phylink_config *co
+ /**
+ * mac_an_restart() - restart 802.3z BaseX autonegotiation
+ * @config: a pointer to a &struct phylink_config.
++ *
++ * Note: This is a legacy method. This function will not be called unless
++ * legacy_pre_march2020 is set in &struct phylink_config and there is no
++ * PCS attached.
+ */
+ void mac_an_restart(struct phylink_config *config);
+
diff --git a/target/linux/generic/backport-6.1/703-15-v5.18-net-phy-phylink-fix-DSA-mac_select_pcs-introduction.patch b/target/linux/generic/backport-6.1/703-15-v5.18-net-phy-phylink-fix-DSA-mac_select_pcs-introduction.patch
new file mode 100644
index 0000000000..9e5061aaed
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-15-v5.18-net-phy-phylink-fix-DSA-mac_select_pcs-introduction.patch
@@ -0,0 +1,88 @@
+From 1054457006d4a14de4ae4132030e33d7eedaeba1 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 21 Feb 2022 17:10:52 +0000
+Subject: [PATCH] net: phy: phylink: fix DSA mac_select_pcs() introduction
+
+Vladimir Oltean reports that probing on DSA drivers that aren't yet
+populating supported_interfaces now fails. Fix this by allowing
+phylink to detect whether DSA actually provides an underlying
+mac_select_pcs() implementation.
+
+Reported-by: Vladimir Oltean <olteanv@gmail.com>
+Fixes: bde018222c6b ("net: dsa: add support for phylink mac_select_pcs()")
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Tested-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/E1nMCD6-00A0wC-FG@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 14 +++++++++++---
+ net/dsa/port.c | 2 +-
+ 2 files changed, 12 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -74,6 +74,7 @@ struct phylink {
+ struct work_struct resolve;
+
+ bool mac_link_dropped;
++ bool using_mac_select_pcs;
+
+ struct sfp_bus *sfp_bus;
+ bool sfp_may_have_phy;
+@@ -163,7 +164,7 @@ static int phylink_validate_mac_and_pcs(
+ int ret;
+
+ /* Get the PCS for this interface mode */
+- if (pl->mac_ops->mac_select_pcs) {
++ if (pl->using_mac_select_pcs) {
+ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
+ if (IS_ERR(pcs))
+ return PTR_ERR(pcs);
+@@ -790,7 +791,7 @@ static void phylink_major_config(struct
+
+ phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
+
+- if (pl->mac_ops->mac_select_pcs) {
++ if (pl->using_mac_select_pcs) {
+ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
+ if (IS_ERR(pcs)) {
+ phylink_err(pl,
+@@ -1192,11 +1193,17 @@ struct phylink *phylink_create(struct ph
+ phy_interface_t iface,
+ const struct phylink_mac_ops *mac_ops)
+ {
++ bool using_mac_select_pcs = false;
+ struct phylink *pl;
+ int ret;
+
+- /* Validate the supplied configuration */
+ if (mac_ops->mac_select_pcs &&
++ mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) !=
++ ERR_PTR(-EOPNOTSUPP))
++ using_mac_select_pcs = true;
++
++ /* Validate the supplied configuration */
++ if (using_mac_select_pcs &&
+ phy_interface_empty(config->supported_interfaces)) {
+ dev_err(config->dev,
+ "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n");
+@@ -1220,6 +1227,7 @@ struct phylink *phylink_create(struct ph
+ return ERR_PTR(-EINVAL);
+ }
+
++ pl->using_mac_select_pcs = using_mac_select_pcs;
+ pl->phy_state.interface = iface;
+ pl->link_interface = iface;
+ if (iface == PHY_INTERFACE_MODE_MOCA)
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1033,8 +1033,8 @@ dsa_port_phylink_mac_select_pcs(struct p
+ phy_interface_t interface)
+ {
+ struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
++ struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
+ struct dsa_switch *ds = dp->ds;
+- struct phylink_pcs *pcs = NULL;
+
+ if (ds->ops->phylink_mac_select_pcs)
+ pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface);
diff --git a/target/linux/generic/backport-6.1/703-16-v5.16-net-mvneta-populate-supported_interfaces-member.patch b/target/linux/generic/backport-6.1/703-16-v5.16-net-mvneta-populate-supported_interfaces-member.patch
new file mode 100644
index 0000000000..f21fa4b277
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-16-v5.16-net-mvneta-populate-supported_interfaces-member.patch
@@ -0,0 +1,48 @@
+From fdedb695e6a8657302341cda81d519ef04f9acaa Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Wed, 27 Oct 2021 10:03:43 +0100
+Subject: [PATCH] net: mvneta: populate supported_interfaces member
+
+Populate the phy_interface_t bitmap for the Marvell mvneta driver with
+interfaces modes supported by the MAC.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -5180,6 +5180,31 @@ static int mvneta_probe(struct platform_
+
+ pp->phylink_config.dev = &dev->dev;
+ pp->phylink_config.type = PHYLINK_NETDEV;
++ phy_interface_set_rgmii(pp->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_QSGMII,
++ pp->phylink_config.supported_interfaces);
++ if (comphy) {
++ /* If a COMPHY is present, we can support any of the serdes
++ * modes and switch between them.
++ */
++ __set_bit(PHY_INTERFACE_MODE_SGMII,
++ pp->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
++ pp->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++ pp->phylink_config.supported_interfaces);
++ } else if (phy_mode == PHY_INTERFACE_MODE_2500BASEX) {
++ /* No COMPHY, with only 2500BASE-X mode supported */
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++ pp->phylink_config.supported_interfaces);
++ } else if (phy_mode == PHY_INTERFACE_MODE_1000BASEX ||
++ phy_mode == PHY_INTERFACE_MODE_SGMII) {
++ /* No COMPHY, we can switch between 1000BASE-X and SGMII */
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
++ pp->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_SGMII,
++ pp->phylink_config.supported_interfaces);
++ }
+
+ phylink = phylink_create(&pp->phylink_config, pdev->dev.fwnode,
+ phy_mode, &mvneta_phylink_ops);
diff --git a/target/linux/generic/backport-6.1/703-17-v5.16-net-mvneta-remove-interface-checks-in-mvneta_validat.patch b/target/linux/generic/backport-6.1/703-17-v5.16-net-mvneta-remove-interface-checks-in-mvneta_validat.patch
new file mode 100644
index 0000000000..e287e39d6a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-17-v5.16-net-mvneta-remove-interface-checks-in-mvneta_validat.patch
@@ -0,0 +1,35 @@
+From d9ca72807ecb236f679b960c70ef5b7d4a5f0222 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 27 Oct 2021 10:03:48 +0100
+Subject: [PATCH] net: mvneta: remove interface checks in mvneta_validate()
+
+As phylink checks the interface mode against the supported_interfaces
+bitmap, we no longer need to validate the interface mode in the
+validation function. Remove this to simplify it.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 11 ++---------
+ 1 file changed, 2 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3833,15 +3833,8 @@ static void mvneta_validate(struct phyli
+ * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
+ * When <PortType> = 1 (1000BASE-X) this field must be set to 1."
+ */
+- if (phy_interface_mode_is_8023z(state->interface)) {
+- if (!phylink_test(state->advertising, Autoneg)) {
+- linkmode_zero(supported);
+- return;
+- }
+- } else if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_QSGMII &&
+- state->interface != PHY_INTERFACE_MODE_SGMII &&
+- !phy_interface_mode_is_rgmii(state->interface)) {
++ if (phy_interface_mode_is_8023z(state->interface) &&
++ !phylink_test(state->advertising, Autoneg)) {
+ linkmode_zero(supported);
+ return;
+ }
diff --git a/target/linux/generic/backport-6.1/703-18-v5.16-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch b/target/linux/generic/backport-6.1/703-18-v5.16-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch
new file mode 100644
index 0000000000..9121612bf8
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-18-v5.16-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch
@@ -0,0 +1,55 @@
+From 099cbfa286ab937d8213c2dc5c0b401969b78042 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 27 Oct 2021 10:03:53 +0100
+Subject: [PATCH] net: mvneta: drop use of phylink_helper_basex_speed()
+
+Now that we have a better method to select SFP interface modes, we
+no longer need to use phylink_helper_basex_speed() in a driver's
+validation function, and we can also get rid of our hack to indicate
+both 1000base-X and 2500base-X if the comphy is present to make that
+work. Remove this hack and use of phylink_helper_basex_speed().
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 12 +++---------
+ 1 file changed, 3 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3824,8 +3824,6 @@ static void mvneta_validate(struct phyli
+ unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+- struct net_device *ndev = to_net_dev(config->dev);
+- struct mvneta_port *pp = netdev_priv(ndev);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+ /* We only support QSGMII, SGMII, 802.3z and RGMII modes.
+@@ -3847,11 +3845,12 @@ static void mvneta_validate(struct phyli
+ phylink_set(mask, Pause);
+
+ /* Half-duplex at speeds higher than 100Mbit is unsupported */
+- if (pp->comphy || state->interface != PHY_INTERFACE_MODE_2500BASEX) {
++ if (state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+ phylink_set(mask, 1000baseT_Full);
+ phylink_set(mask, 1000baseX_Full);
+ }
+- if (pp->comphy || state->interface == PHY_INTERFACE_MODE_2500BASEX) {
++
++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) {
+ phylink_set(mask, 2500baseT_Full);
+ phylink_set(mask, 2500baseX_Full);
+ }
+@@ -3866,11 +3865,6 @@ static void mvneta_validate(struct phyli
+
+ linkmode_and(supported, supported, mask);
+ linkmode_and(state->advertising, state->advertising, mask);
+-
+- /* We can only operate at 2500BaseX or 1000BaseX. If requested
+- * to advertise both, only report advertising at 2500BaseX.
+- */
+- phylink_helper_basex_speed(state);
+ }
+
+ static void mvneta_mac_pcs_get_state(struct phylink_config *config,
diff --git a/target/linux/generic/backport-6.1/703-19-v5.17-net-mvneta-use-phylink_generic_validate.patch b/target/linux/generic/backport-6.1/703-19-v5.17-net-mvneta-use-phylink_generic_validate.patch
new file mode 100644
index 0000000000..209dfbc0de
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-19-v5.17-net-mvneta-use-phylink_generic_validate.patch
@@ -0,0 +1,72 @@
+From 02a0988b98930491db95966fb8086072e47dabb6 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 15 Nov 2021 10:00:32 +0000
+Subject: [PATCH] net: mvneta: use phylink_generic_validate()
+
+Convert mvneta to use phylink_generic_validate() for the bulk of its
+validate() implementation. This network adapter has a restriction
+that for 802.3z links, autonegotiation must be enabled.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 34 ++++-----------------------
+ 1 file changed, 4 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3824,8 +3824,6 @@ static void mvneta_validate(struct phyli
+ unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+-
+ /* We only support QSGMII, SGMII, 802.3z and RGMII modes.
+ * When in 802.3z mode, we must have AN enabled:
+ * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
+@@ -3837,34 +3835,7 @@ static void mvneta_validate(struct phyli
+ return;
+ }
+
+- /* Allow all the expected bits */
+- phylink_set(mask, Autoneg);
+- phylink_set_port_modes(mask);
+-
+- /* Asymmetric pause is unsupported */
+- phylink_set(mask, Pause);
+-
+- /* Half-duplex at speeds higher than 100Mbit is unsupported */
+- if (state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- }
+-
+- if (state->interface == PHY_INTERFACE_MODE_2500BASEX) {
+- phylink_set(mask, 2500baseT_Full);
+- phylink_set(mask, 2500baseX_Full);
+- }
+-
+- if (!phy_interface_mode_is_8023z(state->interface)) {
+- /* 10M and 100M are only supported in non-802.3z mode */
+- phylink_set(mask, 10baseT_Half);
+- phylink_set(mask, 10baseT_Full);
+- phylink_set(mask, 100baseT_Half);
+- phylink_set(mask, 100baseT_Full);
+- }
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
++ phylink_generic_validate(config, supported, state);
+ }
+
+ static void mvneta_mac_pcs_get_state(struct phylink_config *config,
+@@ -5167,6 +5138,9 @@ static int mvneta_probe(struct platform_
+
+ pp->phylink_config.dev = &dev->dev;
+ pp->phylink_config.type = PHYLINK_NETDEV;
++ pp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 |
++ MAC_100 | MAC_1000FD | MAC_2500FD;
++
+ phy_interface_set_rgmii(pp->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_QSGMII,
+ pp->phylink_config.supported_interfaces);
diff --git a/target/linux/generic/backport-6.1/703-20-v5.17-net-mvneta-mark-as-a-legacy_pre_march2020-driver.patch b/target/linux/generic/backport-6.1/703-20-v5.17-net-mvneta-mark-as-a-legacy_pre_march2020-driver.patch
new file mode 100644
index 0000000000..31717565bf
--- /dev/null
+++ b/target/linux/generic/backport-6.1/703-20-v5.17-net-mvneta-mark-as-a-legacy_pre_march2020-driver.patch
@@ -0,0 +1,29 @@
+From 2106be4fdf3223d9c5bd485e6ef094139e3197ba Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Sun, 12 Dec 2021 13:01:21 +0000
+Subject: [PATCH] net: mvneta: mark as a legacy_pre_march2020 driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+mvneta provides mac_an_restart and mac_pcs_get_state methods, so needs
+to be marked as a legacy driver. Marek spotted that mvneta had stopped
+working in 2500base-X mode - thanks for reporting.
+
+Reported-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -5138,6 +5138,7 @@ static int mvneta_probe(struct platform_
+
+ pp->phylink_config.dev = &dev->dev;
+ pp->phylink_config.type = PHYLINK_NETDEV;
++ pp->phylink_config.legacy_pre_march2020 = true;
+ pp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 |
+ MAC_100 | MAC_1000FD | MAC_2500FD;
+
diff --git a/target/linux/generic/backport-6.1/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch b/target/linux/generic/backport-6.1/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch
new file mode 100644
index 0000000000..b6fe0dad4c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch
@@ -0,0 +1,43 @@
+From 83800d29f0c578e82554e7d4c6bfdbdf9b6cf428 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 16 Nov 2021 10:06:43 +0000
+Subject: [PATCH] net: mtk_eth_soc: populate supported_interfaces member
+
+Populate the phy interface mode bitmap for the Mediatek driver with
+interfaces modes supported by the MAC.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3355,6 +3355,26 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ mac->phylink_config.dev = &eth->netdev[id]->dev;
+ mac->phylink_config.type = PHYLINK_NETDEV;
++ __set_bit(PHY_INTERFACE_MODE_MII,
++ mac->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ mac->phylink_config.supported_interfaces);
++
++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII))
++ phy_interface_set_rgmii(mac->phylink_config.supported_interfaces);
++
++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) && !mac->id)
++ __set_bit(PHY_INTERFACE_MODE_TRGMII,
++ mac->phylink_config.supported_interfaces);
++
++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) {
++ __set_bit(PHY_INTERFACE_MODE_SGMII,
++ mac->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
++ mac->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++ mac->phylink_config.supported_interfaces);
++ }
+
+ phylink = phylink_create(&mac->phylink_config,
+ of_fwnode_handle(mac->of_node),
diff --git a/target/linux/generic/backport-6.1/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch b/target/linux/generic/backport-6.1/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch
new file mode 100644
index 0000000000..0a33ab0093
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch
@@ -0,0 +1,75 @@
+From db81ca153814475d7e07365d46a4d1134bd122e2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 16 Nov 2021 10:06:48 +0000
+Subject: [PATCH] net: mtk_eth_soc: remove interface checks in mtk_validate()
+
+As phylink checks the interface mode against the supported_interfaces
+bitmap, we no longer need to validate the interface mode, nor handle
+PHY_INTERFACE_MODE_NA in the validation function. Remove these to
+simplify the implementation.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 34 ---------------------
+ 1 file changed, 34 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -568,24 +568,8 @@ static void mtk_validate(struct phylink_
+ unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+- struct mtk_mac *mac = container_of(config, struct mtk_mac,
+- phylink_config);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_MII &&
+- state->interface != PHY_INTERFACE_MODE_GMII &&
+- !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII) &&
+- phy_interface_mode_is_rgmii(state->interface)) &&
+- !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) &&
+- !mac->id && state->interface == PHY_INTERFACE_MODE_TRGMII) &&
+- !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII) &&
+- (state->interface == PHY_INTERFACE_MODE_SGMII ||
+- phy_interface_mode_is_8023z(state->interface)))) {
+- linkmode_zero(supported);
+- return;
+- }
+-
+ phylink_set_port_modes(mask);
+ phylink_set(mask, Autoneg);
+
+@@ -612,7 +596,6 @@ static void mtk_validate(struct phylink_
+ case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_RMII:
+ case PHY_INTERFACE_MODE_REVMII:
+- case PHY_INTERFACE_MODE_NA:
+ default:
+ phylink_set(mask, 10baseT_Half);
+ phylink_set(mask, 10baseT_Full);
+@@ -621,23 +604,6 @@ static void mtk_validate(struct phylink_
+ break;
+ }
+
+- if (state->interface == PHY_INTERFACE_MODE_NA) {
+- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- phylink_set(mask, 2500baseX_Full);
+- }
+- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII)) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseT_Half);
+- phylink_set(mask, 1000baseX_Full);
+- }
+- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GEPHY)) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseT_Half);
+- }
+- }
+-
+ phylink_set(mask, Pause);
+ phylink_set(mask, Asym_Pause);
+
diff --git a/target/linux/generic/backport-6.1/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch b/target/linux/generic/backport-6.1/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch
new file mode 100644
index 0000000000..f8cc8105a4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch
@@ -0,0 +1,42 @@
+From 71d927494463c4f016d828e1134da26b7e961af5 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 16 Nov 2021 10:06:53 +0000
+Subject: [PATCH] net: mtk_eth_soc: drop use of phylink_helper_basex_speed()
+
+Now that we have a better method to select SFP interface modes, we
+no longer need to use phylink_helper_basex_speed() in a driver's
+validation function, and we can also get rid of our hack to indicate
+both 1000base-X and 2500base-X if the comphy is present to make that
+work. Remove this hack and use of phylink_helper_basex_speed().
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -578,8 +578,9 @@ static void mtk_validate(struct phylink_
+ phylink_set(mask, 1000baseT_Full);
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+- case PHY_INTERFACE_MODE_2500BASEX:
+ phylink_set(mask, 1000baseX_Full);
++ break;
++ case PHY_INTERFACE_MODE_2500BASEX:
+ phylink_set(mask, 2500baseX_Full);
+ break;
+ case PHY_INTERFACE_MODE_GMII:
+@@ -609,11 +610,6 @@ static void mtk_validate(struct phylink_
+
+ linkmode_and(supported, supported, mask);
+ linkmode_and(state->advertising, state->advertising, mask);
+-
+- /* We can only operate at 2500BaseX or 1000BaseX. If requested
+- * to advertise both, only report advertising at 2500BaseX.
+- */
+- phylink_helper_basex_speed(state);
+ }
+
+ static const struct phylink_mac_ops mtk_phylink_ops = {
diff --git a/target/linux/generic/backport-6.1/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch b/target/linux/generic/backport-6.1/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch
new file mode 100644
index 0000000000..f695991ec1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch
@@ -0,0 +1,84 @@
+From a4238f6ce151afa331375d74a5033b76da637644 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 16 Nov 2021 10:06:58 +0000
+Subject: [PATCH] net: mtk_eth_soc: use phylink_generic_validate()
+
+mtk_eth_soc has no special behaviour in its validation implementation,
+so can be switched to phylink_generic_validate().
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 53 ++-------------------
+ 1 file changed, 4 insertions(+), 49 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -564,56 +564,8 @@ static void mtk_mac_link_up(struct phyli
+ mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
+ }
+
+-static void mtk_validate(struct phylink_config *config,
+- unsigned long *supported,
+- struct phylink_link_state *state)
+-{
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+-
+- phylink_set_port_modes(mask);
+- phylink_set(mask, Autoneg);
+-
+- switch (state->interface) {
+- case PHY_INTERFACE_MODE_TRGMII:
+- phylink_set(mask, 1000baseT_Full);
+- break;
+- case PHY_INTERFACE_MODE_1000BASEX:
+- phylink_set(mask, 1000baseX_Full);
+- break;
+- case PHY_INTERFACE_MODE_2500BASEX:
+- phylink_set(mask, 2500baseX_Full);
+- break;
+- case PHY_INTERFACE_MODE_GMII:
+- case PHY_INTERFACE_MODE_RGMII:
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- phylink_set(mask, 1000baseT_Half);
+- fallthrough;
+- case PHY_INTERFACE_MODE_SGMII:
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- fallthrough;
+- case PHY_INTERFACE_MODE_MII:
+- case PHY_INTERFACE_MODE_RMII:
+- case PHY_INTERFACE_MODE_REVMII:
+- default:
+- phylink_set(mask, 10baseT_Half);
+- phylink_set(mask, 10baseT_Full);
+- phylink_set(mask, 100baseT_Half);
+- phylink_set(mask, 100baseT_Full);
+- break;
+- }
+-
+- phylink_set(mask, Pause);
+- phylink_set(mask, Asym_Pause);
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
+-}
+-
+ static const struct phylink_mac_ops mtk_phylink_ops = {
+- .validate = mtk_validate,
++ .validate = phylink_generic_validate,
+ .mac_pcs_get_state = mtk_mac_pcs_get_state,
+ .mac_an_restart = mtk_mac_an_restart,
+ .mac_config = mtk_mac_config,
+@@ -3317,6 +3269,9 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ mac->phylink_config.dev = &eth->netdev[id]->dev;
+ mac->phylink_config.type = PHYLINK_NETDEV;
++ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
++ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
++
+ __set_bit(PHY_INTERFACE_MODE_MII,
+ mac->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_GMII,
diff --git a/target/linux/generic/backport-6.1/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch b/target/linux/generic/backport-6.1/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch
new file mode 100644
index 0000000000..cbff1bfbbc
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch
@@ -0,0 +1,29 @@
+From b06515367facfadcf5e70cf6f39db749cf4eb5e3 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 9 Dec 2021 13:11:43 +0000
+Subject: [PATCH] net: mtk_eth_soc: mark as a legacy_pre_march2020 driver
+
+mtk_eth_soc has not been updated for commit 7cceb599d15d ("net: phylink:
+avoid mac_config calls"), and makes use of state->speed and
+state->duplex in contravention of the phylink documentation. This makes
+reliant on the legacy behaviours, so mark it as a legacy driver.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3269,6 +3269,10 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ mac->phylink_config.dev = &eth->netdev[id]->dev;
+ mac->phylink_config.type = PHYLINK_NETDEV;
++ /* This driver makes use of state->speed/state->duplex in
++ * mac_config
++ */
++ mac->phylink_config.legacy_pre_march2020 = true;
+ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
+
diff --git a/target/linux/generic/backport-6.1/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch b/target/linux/generic/backport-6.1/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch
new file mode 100644
index 0000000000..c0b4a61cb6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch
@@ -0,0 +1,40 @@
+From 889e3691b9d6573de133da1f5e78f590e52152cd Mon Sep 17 00:00:00 2001
+From: Jakub Kicinski <kuba@kernel.org>
+Date: Thu, 28 Apr 2022 14:23:13 -0700
+Subject: [PATCH] eth: mtk_eth_soc: remove a copy of the NAPI_POLL_WEIGHT
+ define
+
+Defining local versions of NAPI_POLL_WEIGHT with the same
+values in the drivers just makes refactoring harder.
+
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++--
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 -
+ 2 files changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3568,9 +3568,9 @@ static int mtk_probe(struct platform_dev
+ */
+ init_dummy_netdev(&eth->dummy_dev);
+ 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,
+- MTK_NAPI_WEIGHT);
++ NAPI_POLL_WEIGHT);
+
+ platform_set_drvdata(pdev, eth);
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -25,7 +25,6 @@
+ #define MTK_TX_DMA_BUF_LEN 0x3fff
+ #define MTK_TX_DMA_BUF_LEN_V2 0xffff
+ #define MTK_DMA_SIZE 512
+-#define MTK_NAPI_WEIGHT 64
+ #define MTK_MAC_COUNT 2
+ #define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN)
+ #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
diff --git a/target/linux/generic/backport-6.1/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch b/target/linux/generic/backport-6.1/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch
new file mode 100644
index 0000000000..5940ac27df
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch
@@ -0,0 +1,35 @@
+From 0600bdde1fae75fb9bad72033d28edddc72b44b2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:31 +0100
+Subject: [PATCH 01/12] net: mtk_eth_soc: remove unused mac->mode
+
+mac->mode is only ever written to in one location, and is thus
+superflous. Remove it.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 -
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 -
+ 2 files changed, 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3264,7 +3264,6 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ /* mac config is not set */
+ mac->interface = PHY_INTERFACE_MODE_NA;
+- mac->mode = MLO_AN_PHY;
+ mac->speed = SPEED_UNKNOWN;
+
+ mac->phylink_config.dev = &eth->netdev[id]->dev;
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1086,7 +1086,6 @@ struct mtk_eth {
+ struct mtk_mac {
+ int id;
+ phy_interface_t interface;
+- unsigned int mode;
+ int speed;
+ struct device_node *of_node;
+ struct phylink *phylink;
diff --git a/target/linux/generic/backport-6.1/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch b/target/linux/generic/backport-6.1/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch
new file mode 100644
index 0000000000..a15914bd55
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch
@@ -0,0 +1,40 @@
+From 5a7a2f4b29d7546244da7d8bbc1962fce5b230f2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:36 +0100
+Subject: [PATCH 02/12] net: mtk_eth_soc: remove unused sgmii flags
+
+The "flags" member of struct mtk_sgmii appears to be unused, as are
+the MTK_SGMII_PHYSPEED_* and MTK_HAS_FLAGS() macros. Remove them.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 8 --------
+ 1 file changed, 8 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -957,23 +957,15 @@ struct mtk_soc_data {
+ /* currently no SoC has more than 2 macs */
+ #define MTK_MAX_DEVS 2
+
+-#define MTK_SGMII_PHYSPEED_AN BIT(31)
+-#define MTK_SGMII_PHYSPEED_MASK GENMASK(2, 0)
+-#define MTK_SGMII_PHYSPEED_1000 BIT(0)
+-#define MTK_SGMII_PHYSPEED_2500 BIT(1)
+-#define MTK_HAS_FLAGS(flags, _x) (((flags) & (_x)) == (_x))
+-
+ /* struct mtk_sgmii - This is the structure holding sgmii regmap and its
+ * characteristics
+ * @regmap: The register map pointing at the range used to setup
+ * SGMII modes
+- * @flags: The enum refers to which mode the sgmii wants to run on
+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
+ */
+
+ struct mtk_sgmii {
+ struct regmap *regmap[MTK_MAX_DEVS];
+- u32 flags[MTK_MAX_DEVS];
+ u32 ana_rgc3;
+ };
+
diff --git a/target/linux/generic/backport-6.1/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch b/target/linux/generic/backport-6.1/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch
new file mode 100644
index 0000000000..e16bc875e5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch
@@ -0,0 +1,40 @@
+From bc5e93e0cd22e360eda23859b939280205567580 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:42 +0100
+Subject: [PATCH 03/12] net: mtk_eth_soc: add mask and update PCS speed
+ definitions
+
+The PCS speed setting is a two bit field, but it is defined as two
+separate bits. Add a bitfield mask for the speed definitions, an
+ use the FIELD_PREP() macro to define each PCS speed.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -17,6 +17,7 @@
+ #include <linux/phylink.h>
+ #include <linux/rhashtable.h>
+ #include <linux/dim.h>
++#include <linux/bitfield.h>
+ #include "mtk_ppe.h"
+
+ #define MTK_QDMA_PAGE_SIZE 2048
+@@ -474,9 +475,10 @@
+ #define SGMSYS_SGMII_MODE 0x20
+ #define SGMII_IF_MODE_BIT0 BIT(0)
+ #define SGMII_SPEED_DUPLEX_AN BIT(1)
+-#define SGMII_SPEED_10 0x0
+-#define SGMII_SPEED_100 BIT(2)
+-#define SGMII_SPEED_1000 BIT(3)
++#define SGMII_SPEED_MASK GENMASK(3, 2)
++#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
++#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
++#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
+ #define SGMII_DUPLEX_FULL BIT(4)
+ #define SGMII_IF_MODE_BIT5 BIT(5)
+ #define SGMII_REMOTE_FAULT_DIS BIT(8)
diff --git a/target/linux/generic/backport-6.1/704-10-v5.19-net-mtk_eth_soc-correct-802.3z-speed-setting.patch b/target/linux/generic/backport-6.1/704-10-v5.19-net-mtk_eth_soc-correct-802.3z-speed-setting.patch
new file mode 100644
index 0000000000..fb1ee4e310
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-10-v5.19-net-mtk_eth_soc-correct-802.3z-speed-setting.patch
@@ -0,0 +1,60 @@
+From 7da3f901f8ecb425105fad39a0f5de73306abe52 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:47 +0100
+Subject: [PATCH 04/12] net: mtk_eth_soc: correct 802.3z speed setting
+
+Phylink does not guarantee that state->speed will be set correctly in
+the mac_config() call, so it's a bug that the driver makes use of it.
+Moreover, it is making use of it in a function that is only ever called
+for 1000BASE-X and 2500BASE-X which operate at a fixed speed which
+happens to be the same setting irrespective of the interface mode. We
+can simply remove the switch statement and just set the SGMII interface
+speed.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 +++++-------------
+ 1 file changed, 5 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -34,6 +34,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ return 0;
+ }
+
++/* For SGMII interface mode */
+ int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
+ {
+ unsigned int val;
+@@ -60,6 +61,9 @@ int mtk_sgmii_setup_mode_an(struct mtk_s
+ return 0;
+ }
+
++/* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
++ * fixed speed.
++ */
+ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+ const struct phylink_link_state *state)
+ {
+@@ -82,19 +86,7 @@ int mtk_sgmii_setup_mode_force(struct mt
+ /* SGMII force mode setting */
+ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
+ val &= ~SGMII_IF_MODE_MASK;
+-
+- switch (state->speed) {
+- case SPEED_10:
+- val |= SGMII_SPEED_10;
+- break;
+- case SPEED_100:
+- val |= SGMII_SPEED_100;
+- break;
+- case SPEED_2500:
+- case SPEED_1000:
+- val |= SGMII_SPEED_1000;
+- break;
+- }
++ val |= SGMII_SPEED_1000;
+
+ if (state->duplex == DUPLEX_FULL)
+ val |= SGMII_DUPLEX_FULL;
diff --git a/target/linux/generic/backport-6.1/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch b/target/linux/generic/backport-6.1/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch
new file mode 100644
index 0000000000..78444903a8
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch
@@ -0,0 +1,101 @@
+From a459187390bb221827f9c07866c3a5ffbdf9622b Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:52 +0100
+Subject: [PATCH 05/12] net: mtk_eth_soc: correct 802.3z duplex setting
+
+Phylink does not guarantee that state->duplex will be set correctly in
+the mac_config() call, so it's a bug that the driver makes use of it.
+
+Move the 802.3z PCS duplex configuration to mac_link_up().
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 16 +++++++++++----
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 22 +++++++++++++++------
+ 3 files changed, 29 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -533,8 +533,18 @@ static void mtk_mac_link_up(struct phyli
+ {
+ struct mtk_mac *mac = container_of(config, struct mtk_mac,
+ phylink_config);
+- u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
++ u32 mcr;
+
++ if (phy_interface_mode_is_8023z(interface)) {
++ struct mtk_eth *eth = mac->hw;
++
++ /* Decide how GMAC and SGMIISYS be mapped */
++ int sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
++ 0 : mac->id;
++ mtk_sgmii_link_up(eth->sgmii, sid, speed, duplex);
++ }
++
++ mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 |
+ MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC |
+ MAC_MCR_FORCE_RX_FC);
+@@ -3268,9 +3278,7 @@ static int mtk_add_mac(struct mtk_eth *e
+
+ mac->phylink_config.dev = &eth->netdev[id]->dev;
+ mac->phylink_config.type = PHYLINK_NETDEV;
+- /* This driver makes use of state->speed/state->duplex in
+- * mac_config
+- */
++ /* This driver makes use of state->speed in mac_config */
+ mac->phylink_config.legacy_pre_march2020 = true;
+ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1104,6 +1104,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
+ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+ const struct phylink_link_state *state);
++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex);
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
+
+ int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -83,14 +83,10 @@ int mtk_sgmii_setup_mode_force(struct mt
+ val &= ~SGMII_AN_ENABLE;
+ regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
+
+- /* SGMII force mode setting */
++ /* Set the speed etc but leave the duplex unchanged */
+ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
+- val &= ~SGMII_IF_MODE_MASK;
++ val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
+ val |= SGMII_SPEED_1000;
+-
+- if (state->duplex == DUPLEX_FULL)
+- val |= SGMII_DUPLEX_FULL;
+-
+ regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
+
+ /* Release PHYA power down state */
+@@ -101,6 +97,20 @@ int mtk_sgmii_setup_mode_force(struct mt
+ return 0;
+ }
+
++/* For 1000BASE-X and 2500BASE-X interface modes */
++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
++{
++ unsigned int val;
++
++ /* SGMII force duplex setting */
++ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
++ val &= ~SGMII_DUPLEX_FULL;
++ if (duplex == DUPLEX_FULL)
++ val |= SGMII_DUPLEX_FULL;
++
++ regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++}
++
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id)
+ {
+ struct mtk_sgmii *ss = eth->sgmii;
diff --git a/target/linux/generic/backport-6.1/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch b/target/linux/generic/backport-6.1/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch
new file mode 100644
index 0000000000..6556bb7d07
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch
@@ -0,0 +1,60 @@
+From 4ce5a0bd3958ed248f0325bfcb95339f7c74feb2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:54:57 +0100
+Subject: [PATCH 06/12] net: mtk_eth_soc: stop passing phylink state to sgmii
+ setup
+
+Now that mtk_sgmii_setup_mode_force() only uses the interface mode
+from the phylink state, pass just the interface mode into this
+function.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +-
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 4 ++--
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -437,7 +437,7 @@ static void mtk_mac_config(struct phylin
+ /* Setup SGMIISYS with the determined property */
+ if (state->interface != PHY_INTERFACE_MODE_SGMII)
+ err = mtk_sgmii_setup_mode_force(eth->sgmii, sid,
+- state);
++ state->interface);
+ else if (phylink_autoneg_inband(mode))
+ err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1103,7 +1103,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ u32 ana_rgc3);
+ int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
+ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- const struct phylink_link_state *state);
++ phy_interface_t interface);
+ void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex);
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -65,7 +65,7 @@ int mtk_sgmii_setup_mode_an(struct mtk_s
+ * fixed speed.
+ */
+ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- const struct phylink_link_state *state)
++ phy_interface_t interface)
+ {
+ unsigned int val;
+
+@@ -74,7 +74,7 @@ int mtk_sgmii_setup_mode_force(struct mt
+
+ regmap_read(ss->regmap[id], ss->ana_rgc3, &val);
+ val &= ~RG_PHY_SPEED_MASK;
+- if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
++ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ val |= RG_PHY_SPEED_3_125G;
+ regmap_write(ss->regmap[id], ss->ana_rgc3, val);
+
diff --git a/target/linux/generic/backport-6.1/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch b/target/linux/generic/backport-6.1/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch
new file mode 100644
index 0000000000..0e22c7fd67
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch
@@ -0,0 +1,89 @@
+From 1ec619ee4a052fb9ac48b57554ac2722a0bfe73c Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:02 +0100
+Subject: [PATCH 07/12] net: mtk_eth_soc: provide mtk_sgmii_config()
+
+Provide mtk_sgmii_config() to wrap up the decisions about which SGMII
+configuration will be called.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 7 +------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 5 ++---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 20 +++++++++++++++++---
+ 3 files changed, 20 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -435,12 +435,7 @@ static void mtk_mac_config(struct phylin
+ 0 : mac->id;
+
+ /* Setup SGMIISYS with the determined property */
+- if (state->interface != PHY_INTERFACE_MODE_SGMII)
+- err = mtk_sgmii_setup_mode_force(eth->sgmii, sid,
+- state->interface);
+- else if (phylink_autoneg_inband(mode))
+- err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
+-
++ err = mtk_sgmii_config(eth->sgmii, sid, mode, state->interface);
+ if (err)
+ goto init_err;
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1101,9 +1101,8 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne
+
+ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
+ u32 ana_rgc3);
+-int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
+-int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- phy_interface_t interface);
++int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
++ phy_interface_t interface);
+ void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex);
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -35,7 +35,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ }
+
+ /* For SGMII interface mode */
+-int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
++static int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
+ {
+ unsigned int val;
+
+@@ -64,8 +64,8 @@ int mtk_sgmii_setup_mode_an(struct mtk_s
+ /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
+ * fixed speed.
+ */
+-int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- phy_interface_t interface)
++static int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
++ phy_interface_t interface)
+ {
+ unsigned int val;
+
+@@ -97,6 +97,20 @@ int mtk_sgmii_setup_mode_force(struct mt
+ return 0;
+ }
+
++int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
++ phy_interface_t interface)
++{
++ int err = 0;
++
++ /* Setup SGMIISYS with the determined property */
++ if (interface != PHY_INTERFACE_MODE_SGMII)
++ err = mtk_sgmii_setup_mode_force(ss, id, interface);
++ else if (phylink_autoneg_inband(mode))
++ err = mtk_sgmii_setup_mode_an(ss, id);
++
++ return err;
++}
++
+ /* For 1000BASE-X and 2500BASE-X interface modes */
+ void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
+ {
diff --git a/target/linux/generic/backport-6.1/704-14-v5.19-net-mtk_eth_soc-add-fixme-comment-for-state-speed-us.patch b/target/linux/generic/backport-6.1/704-14-v5.19-net-mtk_eth_soc-add-fixme-comment-for-state-speed-us.patch
new file mode 100644
index 0000000000..8080a2ca44
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-14-v5.19-net-mtk_eth_soc-add-fixme-comment-for-state-speed-us.patch
@@ -0,0 +1,38 @@
+From 650a49bc65df6b0e0051a8f62d7c22d95a8f350d Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:07 +0100
+Subject: [PATCH 08/12] net: mtk_eth_soc: add fixme comment for state->speed
+ use
+
+Add a fixme comment for the last remaining incorrect usage of
+state->speed in the mac_config() method, which is strangely in a code
+path which is only run when the PHY interface mode changes.
+
+This means if we are in RGMII mode, changes in state->speed will not
+cause the INTF_MODE, TRGMII_RCK_CTRL and TRGMII_TCK_CTRL registers to
+be set according to the speed, nor will the TRGPLL clock be set to the
+correct value.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -374,6 +374,14 @@ static void mtk_mac_config(struct phylin
+ state->interface))
+ goto err_phy;
+ } else {
++ /* FIXME: this is incorrect. Not only does it
++ * use state->speed (which is not guaranteed
++ * to be correct) but it also makes use of it
++ * in a code path that will only be reachable
++ * when the PHY interface mode changes, not
++ * when the speed changes. Consequently, RGMII
++ * is probably broken.
++ */
+ mtk_gmac0_rgmii_adjust(mac->hw,
+ state->interface,
+ state->speed);
diff --git a/target/linux/generic/backport-6.1/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch b/target/linux/generic/backport-6.1/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch
new file mode 100644
index 0000000000..337c6112d6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch
@@ -0,0 +1,79 @@
+From 0e37ad71b2ff772009595002da2860999e98e14e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:12 +0100
+Subject: [PATCH 09/12] net: mtk_eth_soc: move MAC_MCR setting to mac_finish()
+
+Move the setting of the MTK_MAC_MCR register from the end of mac_config
+into the phylink mac_finish() method, to keep it as the very last write
+that is done during configuration.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 33 ++++++++++++++-------
+ 1 file changed, 22 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -316,8 +316,8 @@ static void mtk_mac_config(struct phylin
+ struct mtk_mac *mac = container_of(config, struct mtk_mac,
+ phylink_config);
+ struct mtk_eth *eth = mac->hw;
+- u32 mcr_cur, mcr_new, sid, i;
+ int val, ge_mode, err = 0;
++ u32 sid, i;
+
+ /* MT76x8 has no hardware settings between for the MAC */
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
+@@ -455,6 +455,25 @@ static void mtk_mac_config(struct phylin
+ return;
+ }
+
++ return;
++
++err_phy:
++ dev_err(eth->dev, "%s: GMAC%d mode %s not supported!\n", __func__,
++ mac->id, phy_modes(state->interface));
++ return;
++
++init_err:
++ dev_err(eth->dev, "%s: GMAC%d mode %s err: %d!\n", __func__,
++ mac->id, phy_modes(state->interface), err);
++}
++
++static int mtk_mac_finish(struct phylink_config *config, unsigned int mode,
++ phy_interface_t interface)
++{
++ struct mtk_mac *mac = container_of(config, struct mtk_mac,
++ phylink_config);
++ u32 mcr_cur, mcr_new;
++
+ /* Setup gmac */
+ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr_new = mcr_cur;
+@@ -466,16 +485,7 @@ static void mtk_mac_config(struct phylin
+ if (mcr_new != mcr_cur)
+ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
+
+- return;
+-
+-err_phy:
+- dev_err(eth->dev, "%s: GMAC%d mode %s not supported!\n", __func__,
+- mac->id, phy_modes(state->interface));
+- return;
+-
+-init_err:
+- dev_err(eth->dev, "%s: GMAC%d mode %s err: %d!\n", __func__,
+- mac->id, phy_modes(state->interface), err);
++ return 0;
+ }
+
+ static void mtk_mac_pcs_get_state(struct phylink_config *config,
+@@ -582,6 +592,7 @@ static const struct phylink_mac_ops mtk_
+ .mac_pcs_get_state = mtk_mac_pcs_get_state,
+ .mac_an_restart = mtk_mac_an_restart,
+ .mac_config = mtk_mac_config,
++ .mac_finish = mtk_mac_finish,
+ .mac_link_down = mtk_mac_link_down,
+ .mac_link_up = mtk_mac_link_up,
+ };
diff --git a/target/linux/generic/backport-6.1/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch b/target/linux/generic/backport-6.1/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch
new file mode 100644
index 0000000000..b03ef436bd
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch
@@ -0,0 +1,57 @@
+From 21089867278deb2a110b685e3cd33f64f9ce41e2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:17 +0100
+Subject: [PATCH 10/12] net: mtk_eth_soc: move restoration of SYSCFG0 to
+ mac_finish()
+
+The SGMIISYS configuration is performed while ETHSYS_SYSCFG0 is in a
+disabled state. In order to preserve this when we switch to phylink_pcs
+we need to move the restoration of this register to the mac_finish()
+callback.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 11 +++++++++--
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -447,8 +447,8 @@ static void mtk_mac_config(struct phylin
+ if (err)
+ goto init_err;
+
+- regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
+- SYSCFG0_SGMII_MASK, val);
++ /* Save the syscfg0 value for mac_finish */
++ mac->syscfg0 = val;
+ } else if (phylink_autoneg_inband(mode)) {
+ dev_err(eth->dev,
+ "In-band mode not supported in non SGMII mode!\n");
+@@ -472,8 +472,15 @@ static int mtk_mac_finish(struct phylink
+ {
+ struct mtk_mac *mac = container_of(config, struct mtk_mac,
+ phylink_config);
++ struct mtk_eth *eth = mac->hw;
+ u32 mcr_cur, mcr_new;
+
++ /* Enable SGMII */
++ if (interface == PHY_INTERFACE_MODE_SGMII ||
++ phy_interface_mode_is_8023z(interface))
++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
++ SYSCFG0_SGMII_MASK, mac->syscfg0);
++
+ /* Setup gmac */
+ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr_new = mcr_cur;
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1088,6 +1088,7 @@ struct mtk_mac {
+ struct mtk_hw_stats *hw_stats;
+ __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT];
+ int hwlro_ip_cnt;
++ unsigned int syscfg0;
+ };
+
+ /* the struct describing the SoC. these are declared in the soc_xyz.c files */
diff --git a/target/linux/generic/backport-6.1/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch b/target/linux/generic/backport-6.1/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch
new file mode 100644
index 0000000000..4c84703cd9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch
@@ -0,0 +1,254 @@
+From 901f3fbe13c3e56f0742e02717ccbfabbc95c463 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:22 +0100
+Subject: [PATCH 11/12] net: mtk_eth_soc: convert code structure to suit split
+ PCS support
+
+Provide a mtk_pcs structure which encapsulates everything that the PCS
+functions need (the regmap and ana_rgc3 offset), and use this in the
+PCS functions. Provide shim functions to convert from the existing
+"mtk_sgmii_*" interface to the converted PCS functions.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 15 ++-
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 123 +++++++++++---------
+ 2 files changed, 79 insertions(+), 59 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -959,16 +959,23 @@ struct mtk_soc_data {
+ /* currently no SoC has more than 2 macs */
+ #define MTK_MAX_DEVS 2
+
+-/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
+- * characteristics
++/* struct mtk_pcs - This structure holds each sgmii regmap and associated
++ * data
+ * @regmap: The register map pointing at the range used to setup
+ * SGMII modes
+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
+ */
++struct mtk_pcs {
++ struct regmap *regmap;
++ u32 ana_rgc3;
++};
+
++/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
++ * characteristics
++ * @pcs Array of individual PCS structures
++ */
+ struct mtk_sgmii {
+- struct regmap *regmap[MTK_MAX_DEVS];
+- u32 ana_rgc3;
++ struct mtk_pcs pcs[MTK_MAX_DEVS];
+ };
+
+ /* struct mtk_eth - This is the main datasructure for holding the state
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -9,90 +9,71 @@
+
+ #include <linux/mfd/syscon.h>
+ #include <linux/of.h>
++#include <linux/phylink.h>
+ #include <linux/regmap.h>
+
+ #include "mtk_eth_soc.h"
+
+-int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
+-{
+- struct device_node *np;
+- int i;
+-
+- ss->ana_rgc3 = ana_rgc3;
+-
+- for (i = 0; i < MTK_MAX_DEVS; i++) {
+- np = of_parse_phandle(r, "mediatek,sgmiisys", i);
+- if (!np)
+- break;
+-
+- ss->regmap[i] = syscon_node_to_regmap(np);
+- of_node_put(np);
+- if (IS_ERR(ss->regmap[i]))
+- return PTR_ERR(ss->regmap[i]);
+- }
+-
+- return 0;
+-}
+-
+ /* For SGMII interface mode */
+-static int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
++static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+ {
+ unsigned int val;
+
+- if (!ss->regmap[id])
++ if (!mpcs->regmap)
+ return -EINVAL;
+
+ /* Setup the link timer and QPHY power up inside SGMIISYS */
+- regmap_write(ss->regmap[id], SGMSYS_PCS_LINK_TIMER,
++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+ SGMII_LINK_TIMER_DEFAULT);
+
+- regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
++ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val |= SGMII_REMOTE_FAULT_DIS;
+- regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+
+- regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+ val |= SGMII_AN_RESTART;
+- regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
++ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+
+- regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
++ regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+ val &= ~SGMII_PHYA_PWD;
+- regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
+
+ return 0;
++
+ }
+
+ /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
+ * fixed speed.
+ */
+-static int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
+- phy_interface_t interface)
++static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
++ phy_interface_t interface)
+ {
+ unsigned int val;
+
+- if (!ss->regmap[id])
++ if (!mpcs->regmap)
+ return -EINVAL;
+
+- regmap_read(ss->regmap[id], ss->ana_rgc3, &val);
++ 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(ss->regmap[id], ss->ana_rgc3, val);
++ regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
+
+ /* Disable SGMII AN */
+- regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+ val &= ~SGMII_AN_ENABLE;
+- regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
++ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+
+ /* Set the speed etc but leave the duplex unchanged */
+- regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
++ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
+ val |= SGMII_SPEED_1000;
+- regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+
+ /* Release PHYA power down state */
+- regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
++ regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+ val &= ~SGMII_PHYA_PWD;
+- regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
+
+ return 0;
+ }
+@@ -100,44 +81,76 @@ static int mtk_sgmii_setup_mode_force(st
+ int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
+ phy_interface_t interface)
+ {
++ struct mtk_pcs *mpcs = &ss->pcs[id];
+ int err = 0;
+
+ /* Setup SGMIISYS with the determined property */
+ if (interface != PHY_INTERFACE_MODE_SGMII)
+- err = mtk_sgmii_setup_mode_force(ss, id, interface);
++ err = mtk_pcs_setup_mode_force(mpcs, interface);
+ else if (phylink_autoneg_inband(mode))
+- err = mtk_sgmii_setup_mode_an(ss, id);
++ err = mtk_pcs_setup_mode_an(mpcs);
+
+ return err;
+ }
+
+-/* For 1000BASE-X and 2500BASE-X interface modes */
+-void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
++static void mtk_pcs_restart_an(struct mtk_pcs *mpcs)
++{
++ unsigned int val;
++
++ if (!mpcs->regmap)
++ return;
++
++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
++ val |= SGMII_AN_RESTART;
++ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
++}
++
++static void mtk_pcs_link_up(struct mtk_pcs *mpcs, int speed, int duplex)
+ {
+ unsigned int val;
+
+ /* SGMII force duplex setting */
+- regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
++ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val &= ~SGMII_DUPLEX_FULL;
+ if (duplex == DUPLEX_FULL)
+ val |= SGMII_DUPLEX_FULL;
+
+- regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
++}
++
++/* For 1000BASE-X and 2500BASE-X interface modes */
++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
++{
++ mtk_pcs_link_up(&ss->pcs[id], speed, duplex);
++}
++
++int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
++{
++ struct device_node *np;
++ int i;
++
++ for (i = 0; i < MTK_MAX_DEVS; i++) {
++ np = of_parse_phandle(r, "mediatek,sgmiisys", i);
++ if (!np)
++ break;
++
++ ss->pcs[i].ana_rgc3 = ana_rgc3;
++ ss->pcs[i].regmap = syscon_node_to_regmap(np);
++ of_node_put(np);
++ if (IS_ERR(ss->pcs[i].regmap))
++ return PTR_ERR(ss->pcs[i].regmap);
++ }
++
++ return 0;
+ }
+
+ void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id)
+ {
+- struct mtk_sgmii *ss = eth->sgmii;
+- unsigned int val, sid;
++ unsigned int sid;
+
+ /* Decide how GMAC and SGMIISYS be mapped */
+ sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+ 0 : mac_id;
+
+- if (!ss->regmap[sid])
+- return;
+-
+- regmap_read(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, &val);
+- val |= SGMII_AN_RESTART;
+- regmap_write(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, val);
++ mtk_pcs_restart_an(&eth->sgmii->pcs[sid]);
+ }
diff --git a/target/linux/generic/backport-6.1/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch b/target/linux/generic/backport-6.1/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch
new file mode 100644
index 0000000000..b585867935
--- /dev/null
+++ b/target/linux/generic/backport-6.1/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch
@@ -0,0 +1,262 @@
+From 14a44ab0330d290fade1403a920e299cc56d7300 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 18 May 2022 15:55:28 +0100
+Subject: [PATCH 12/12] net: mtk_eth_soc: partially convert to phylink_pcs
+
+Partially convert mtk_eth_soc to phylink_pcs, moving the configuration,
+link up and AN restart over. However, it seems mac_pcs_get_state()
+doesn't actually get the state from the PCS, so we can't convert that
+over without a better understanding of the hardware.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 49 ++++++++----------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 7 ++-
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 55 +++++++++++----------
+ 3 files changed, 53 insertions(+), 58 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -310,6 +310,25 @@ static void mtk_gmac0_rgmii_adjust(struc
+ mtk_w32(eth, val, TRGMII_TCK_CTRL);
+ }
+
++static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
++ phy_interface_t interface)
++{
++ struct mtk_mac *mac = container_of(config, struct mtk_mac,
++ phylink_config);
++ struct mtk_eth *eth = mac->hw;
++ unsigned int sid;
++
++ if (interface == PHY_INTERFACE_MODE_SGMII ||
++ phy_interface_mode_is_8023z(interface)) {
++ sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
++ 0 : mac->id;
++
++ return mtk_sgmii_select_pcs(eth->sgmii, sid);
++ }
++
++ return NULL;
++}
++
+ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+ {
+@@ -317,7 +336,7 @@ static void mtk_mac_config(struct phylin
+ phylink_config);
+ struct mtk_eth *eth = mac->hw;
+ int val, ge_mode, err = 0;
+- u32 sid, i;
++ u32 i;
+
+ /* MT76x8 has no hardware settings between for the MAC */
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
+@@ -438,15 +457,6 @@ static void mtk_mac_config(struct phylin
+ SYSCFG0_SGMII_MASK,
+ ~(u32)SYSCFG0_SGMII_MASK);
+
+- /* Decide how GMAC and SGMIISYS be mapped */
+- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+- 0 : mac->id;
+-
+- /* Setup SGMIISYS with the determined property */
+- err = mtk_sgmii_config(eth->sgmii, sid, mode, state->interface);
+- if (err)
+- goto init_err;
+-
+ /* Save the syscfg0 value for mac_finish */
+ mac->syscfg0 = val;
+ } else if (phylink_autoneg_inband(mode)) {
+@@ -527,14 +537,6 @@ static void mtk_mac_pcs_get_state(struct
+ state->pause |= MLO_PAUSE_TX;
+ }
+
+-static void mtk_mac_an_restart(struct phylink_config *config)
+-{
+- struct mtk_mac *mac = container_of(config, struct mtk_mac,
+- phylink_config);
+-
+- mtk_sgmii_restart_an(mac->hw, mac->id);
+-}
+-
+ static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+ {
+@@ -555,15 +557,6 @@ static void mtk_mac_link_up(struct phyli
+ phylink_config);
+ u32 mcr;
+
+- if (phy_interface_mode_is_8023z(interface)) {
+- struct mtk_eth *eth = mac->hw;
+-
+- /* Decide how GMAC and SGMIISYS be mapped */
+- int sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+- 0 : mac->id;
+- mtk_sgmii_link_up(eth->sgmii, sid, speed, duplex);
+- }
+-
+ mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 |
+ MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC |
+@@ -596,8 +589,8 @@ static void mtk_mac_link_up(struct phyli
+
+ static const struct phylink_mac_ops mtk_phylink_ops = {
+ .validate = phylink_generic_validate,
++ .mac_select_pcs = mtk_mac_select_pcs,
+ .mac_pcs_get_state = mtk_mac_pcs_get_state,
+- .mac_an_restart = mtk_mac_an_restart,
+ .mac_config = mtk_mac_config,
+ .mac_finish = mtk_mac_finish,
+ .mac_link_down = mtk_mac_link_down,
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -964,10 +964,12 @@ struct mtk_soc_data {
+ * @regmap: The register map pointing at the range used to setup
+ * SGMII modes
+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
++ * @pcs: Phylink PCS structure
+ */
+ struct mtk_pcs {
+ struct regmap *regmap;
+ u32 ana_rgc3;
++ struct phylink_pcs pcs;
+ };
+
+ /* struct mtk_sgmii - This is the structure holding sgmii regmap and its
+@@ -1107,12 +1109,9 @@ void mtk_stats_update_mac(struct mtk_mac
+ void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
+ u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+
++struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id);
+ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
+ u32 ana_rgc3);
+-int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
+- phy_interface_t interface);
+-void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex);
+-void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
+
+ int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -14,14 +14,16 @@
+
+ #include "mtk_eth_soc.h"
+
++static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
++{
++ return container_of(pcs, struct mtk_pcs, pcs);
++}
++
+ /* For SGMII interface mode */
+ static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+ {
+ unsigned int val;
+
+- if (!mpcs->regmap)
+- return -EINVAL;
+-
+ /* Setup the link timer and QPHY power up inside SGMIISYS */
+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+ SGMII_LINK_TIMER_DEFAULT);
+@@ -50,9 +52,6 @@ static int mtk_pcs_setup_mode_force(stru
+ {
+ unsigned int val;
+
+- if (!mpcs->regmap)
+- return -EINVAL;
+-
+ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
+ val &= ~RG_PHY_SPEED_MASK;
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+@@ -78,10 +77,12 @@ static int mtk_pcs_setup_mode_force(stru
+ return 0;
+ }
+
+-int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode,
+- phy_interface_t interface)
++static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface,
++ const unsigned long *advertising,
++ bool permit_pause_to_mac)
+ {
+- struct mtk_pcs *mpcs = &ss->pcs[id];
++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ int err = 0;
+
+ /* Setup SGMIISYS with the determined property */
+@@ -93,22 +94,25 @@ int mtk_sgmii_config(struct mtk_sgmii *s
+ return err;
+ }
+
+-static void mtk_pcs_restart_an(struct mtk_pcs *mpcs)
++static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
+ {
++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ unsigned int val;
+
+- if (!mpcs->regmap)
+- return;
+-
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+ val |= SGMII_AN_RESTART;
+ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+ }
+
+-static void mtk_pcs_link_up(struct mtk_pcs *mpcs, int speed, int duplex)
++static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface, int speed, int duplex)
+ {
++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ unsigned int val;
+
++ if (!phy_interface_mode_is_8023z(interface))
++ return;
++
+ /* SGMII force duplex setting */
+ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val &= ~SGMII_DUPLEX_FULL;
+@@ -118,11 +122,11 @@ static void mtk_pcs_link_up(struct mtk_p
+ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+ }
+
+-/* For 1000BASE-X and 2500BASE-X interface modes */
+-void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
+-{
+- mtk_pcs_link_up(&ss->pcs[id], speed, duplex);
+-}
++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,
++};
+
+ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
+ {
+@@ -139,18 +143,17 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ of_node_put(np);
+ if (IS_ERR(ss->pcs[i].regmap))
+ return PTR_ERR(ss->pcs[i].regmap);
++
++ ss->pcs[i].pcs.ops = &mtk_pcs_ops;
+ }
+
+ return 0;
+ }
+
+-void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id)
++struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
+ {
+- unsigned int sid;
+-
+- /* Decide how GMAC and SGMIISYS be mapped */
+- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+- 0 : mac_id;
++ if (!ss->pcs[id].regmap)
++ return NULL;
+
+- mtk_pcs_restart_an(&eth->sgmii->pcs[sid]);
++ return &ss->pcs[id].pcs;
+ }
diff --git a/target/linux/generic/backport-6.1/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch b/target/linux/generic/backport-6.1/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch
new file mode 100644
index 0000000000..4142fd1d50
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch
@@ -0,0 +1,106 @@
+From 505560028b6deb9b4385cf6100f05ca6f4aacaf8 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Mon, 6 Dec 2021 18:57:49 +0200
+Subject: [PATCH 01/13] net: dsa: mt7530: iterate using
+ dsa_switch_for_each_user_port in bridging ops
+
+Avoid repeated calls to dsa_to_port() (some hidden behind dsa_is_user_port
+and some in plain sight) by keeping two struct dsa_port references: one
+to the port passed as argument, and another to the other ports of the
+switch that we're iterating over.
+
+dsa_to_port(ds, i) gets replaced by other_dp, i gets replaced by
+other_port which is derived from other_dp->index, dsa_is_user_port is
+handled by the DSA iterator.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 52 +++++++++++++++++++++++-----------------
+ 1 file changed, 30 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1190,27 +1190,31 @@ static int
+ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
+ u32 port_bitmap = BIT(MT7530_CPU_PORT);
+- int i;
++ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+
+- for (i = 0; i < MT7530_NUM_PORTS; i++) {
++ dsa_switch_for_each_user_port(other_dp, ds) {
++ int other_port = other_dp->index;
++
++ if (dp == other_dp)
++ continue;
++
+ /* Add this port to the port matrix of the other ports in the
+ * same bridge. If the port is disabled, port matrix is kept
+ * and not being setup until the port becomes enabled.
+ */
+- if (dsa_is_user_port(ds, i) && i != port) {
+- if (dsa_to_port(ds, i)->bridge_dev != bridge)
+- continue;
+- if (priv->ports[i].enable)
+- mt7530_set(priv, MT7530_PCR_P(i),
+- PCR_MATRIX(BIT(port)));
+- priv->ports[i].pm |= PCR_MATRIX(BIT(port));
++ if (other_dp->bridge_dev != bridge)
++ continue;
+
+- port_bitmap |= BIT(i);
+- }
++ if (priv->ports[other_port].enable)
++ mt7530_set(priv, MT7530_PCR_P(other_port),
++ PCR_MATRIX(BIT(port)));
++ priv->ports[other_port].pm |= PCR_MATRIX(BIT(port));
++
++ port_bitmap |= BIT(other_port);
+ }
+
+ /* Add the all other ports to this port matrix. */
+@@ -1315,24 +1319,28 @@ static void
+ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
+ {
++ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
+ struct mt7530_priv *priv = ds->priv;
+- int i;
+
+ mutex_lock(&priv->reg_mutex);
+
+- for (i = 0; i < MT7530_NUM_PORTS; i++) {
++ dsa_switch_for_each_user_port(other_dp, ds) {
++ int other_port = other_dp->index;
++
++ if (dp == other_dp)
++ continue;
++
+ /* Remove this port from the port matrix of the other ports
+ * in the same bridge. If the port is disabled, port matrix
+ * is kept and not being setup until the port becomes enabled.
+ */
+- if (dsa_is_user_port(ds, i) && i != port) {
+- if (dsa_to_port(ds, i)->bridge_dev != bridge)
+- continue;
+- if (priv->ports[i].enable)
+- mt7530_clear(priv, MT7530_PCR_P(i),
+- PCR_MATRIX(BIT(port)));
+- priv->ports[i].pm &= ~PCR_MATRIX(BIT(port));
+- }
++ if (other_dp->bridge_dev != bridge)
++ continue;
++
++ if (priv->ports[other_port].enable)
++ mt7530_clear(priv, MT7530_PCR_P(other_port),
++ PCR_MATRIX(BIT(port)));
++ priv->ports[other_port].pm &= ~PCR_MATRIX(BIT(port));
+ }
+
+ /* Set the cpu port to be the only one in the port matrix of
diff --git a/target/linux/generic/backport-6.1/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch b/target/linux/generic/backport-6.1/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch
new file mode 100644
index 0000000000..435f282845
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch
@@ -0,0 +1,166 @@
+From a1da54bcd664fc27169386db966575675ac3ccb0 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:01 +0100
+Subject: [PATCH 02/13] net: dsa: mt7530: populate supported_interfaces and
+ mac_capabilities
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Populate the supported interfaces and MAC capabilities for mt7530,
+mt7531 and mt7621 DSA switches. Filling this in will enable phylink
+to pre-check the PHY interface mode against the the supported
+interfaces bitmap prior to calling the validate function, and will
+eventually allow us to convert to using the generic validation.
+
+Tested-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 74 ++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/mt7530.h | 2 ++
+ 2 files changed, 76 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2412,6 +2412,32 @@ mt7531_setup(struct dsa_switch *ds)
+ return 0;
+ }
+
++static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
++{
++ switch (port) {
++ case 0 ... 4: /* Internal phy */
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ config->supported_interfaces);
++ break;
++
++ case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
++ phy_interface_set_rgmii(config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_MII,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ config->supported_interfaces);
++ break;
++
++ case 6: /* 1st cpu port */
++ __set_bit(PHY_INTERFACE_MODE_RGMII,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_TRGMII,
++ config->supported_interfaces);
++ break;
++ }
++}
++
+ static bool
+ mt7530_phy_mode_supported(struct dsa_switch *ds, int port,
+ const struct phylink_link_state *state)
+@@ -2448,6 +2474,37 @@ static bool mt7531_is_rgmii_port(struct
+ return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII);
+ }
+
++static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
++{
++ struct mt7530_priv *priv = ds->priv;
++
++ switch (port) {
++ case 0 ... 4: /* Internal phy */
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ config->supported_interfaces);
++ break;
++
++ case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */
++ if (mt7531_is_rgmii_port(priv, port)) {
++ phy_interface_set_rgmii(config->supported_interfaces);
++ break;
++ }
++ fallthrough;
++
++ case 6: /* 1st cpu port supports sgmii/8023z only */
++ __set_bit(PHY_INTERFACE_MODE_SGMII,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++ config->supported_interfaces);
++
++ config->mac_capabilities |= MAC_2500FD;
++ break;
++ }
++}
++
+ static bool
+ mt7531_phy_mode_supported(struct dsa_switch *ds, int port,
+ const struct phylink_link_state *state)
+@@ -2924,6 +2981,18 @@ mt7531_cpu_port_config(struct dsa_switch
+ return 0;
+ }
+
++static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
++{
++ struct mt7530_priv *priv = ds->priv;
++
++ /* This switch only supports full-duplex at 1Gbps */
++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
++ MAC_10 | MAC_100 | MAC_1000FD;
++
++ priv->info->mac_port_get_caps(ds, port, config);
++}
++
+ static void
+ mt7530_mac_port_validate(struct dsa_switch *ds, int port,
+ unsigned long *supported)
+@@ -3159,6 +3228,7 @@ static const struct dsa_switch_ops mt753
+ .port_vlan_del = mt7530_port_vlan_del,
+ .port_mirror_add = mt753x_port_mirror_add,
+ .port_mirror_del = mt753x_port_mirror_del,
++ .phylink_get_caps = mt753x_phylink_get_caps,
+ .phylink_validate = mt753x_phylink_validate,
+ .phylink_mac_link_state = mt753x_phylink_mac_link_state,
+ .phylink_mac_config = mt753x_phylink_mac_config,
+@@ -3176,6 +3246,7 @@ static const struct mt753x_info mt753x_t
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
++ .mac_port_get_caps = mt7530_mac_port_get_caps,
+ .phy_mode_supported = mt7530_phy_mode_supported,
+ .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+@@ -3187,6 +3258,7 @@ static const struct mt753x_info mt753x_t
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
++ .mac_port_get_caps = mt7530_mac_port_get_caps,
+ .phy_mode_supported = mt7530_phy_mode_supported,
+ .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+@@ -3199,6 +3271,7 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7531_ind_phy_write,
+ .pad_setup = mt7531_pad_setup,
+ .cpu_port_config = mt7531_cpu_port_config,
++ .mac_port_get_caps = mt7531_mac_port_get_caps,
+ .phy_mode_supported = mt7531_phy_mode_supported,
+ .mac_port_validate = mt7531_mac_port_validate,
+ .mac_port_get_state = mt7531_phylink_mac_link_state,
+@@ -3261,6 +3334,7 @@ mt7530_probe(struct mdio_device *mdiodev
+ */
+ if (!priv->info->sw_setup || !priv->info->pad_setup ||
+ !priv->info->phy_read || !priv->info->phy_write ||
++ !priv->info->mac_port_get_caps ||
+ !priv->info->phy_mode_supported ||
+ !priv->info->mac_port_validate ||
+ !priv->info->mac_port_get_state || !priv->info->mac_port_config)
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -769,6 +769,8 @@ struct mt753x_info {
+ int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val);
+ int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface);
+ int (*cpu_port_config)(struct dsa_switch *ds, int port);
++ void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
++ struct phylink_config *config);
+ bool (*phy_mode_supported)(struct dsa_switch *ds, int port,
+ const struct phylink_link_state *state);
+ void (*mac_port_validate)(struct dsa_switch *ds, int port,
diff --git a/target/linux/generic/backport-6.1/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch b/target/linux/generic/backport-6.1/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch
new file mode 100644
index 0000000000..38a24dc46b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch
@@ -0,0 +1,172 @@
+From e3f6719e2269868ca129b05da50cd55786848954 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:06 +0100
+Subject: [PATCH 03/13] net: dsa: mt7530: remove interface checks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+As phylink checks the interface mode against the supported_interfaces
+bitmap, we no longer need to validate the interface mode, nor handle
+PHY_INTERFACE_MODE_NA in the validation function. Remove these to
+simplify the implementation.
+
+Tested-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 82 ----------------------------------------
+ drivers/net/dsa/mt7530.h | 2 -
+ 2 files changed, 84 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2438,37 +2438,6 @@ static void mt7530_mac_port_get_caps(str
+ }
+ }
+
+-static bool
+-mt7530_phy_mode_supported(struct dsa_switch *ds, int port,
+- const struct phylink_link_state *state)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- switch (port) {
+- case 0 ... 4: /* Internal phy */
+- if (state->interface != PHY_INTERFACE_MODE_GMII)
+- return false;
+- break;
+- case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
+- if (!phy_interface_mode_is_rgmii(state->interface) &&
+- state->interface != PHY_INTERFACE_MODE_MII &&
+- state->interface != PHY_INTERFACE_MODE_GMII)
+- return false;
+- break;
+- case 6: /* 1st cpu port */
+- if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_TRGMII)
+- return false;
+- break;
+- default:
+- dev_err(priv->dev, "%s: unsupported port: %i\n", __func__,
+- port);
+- return false;
+- }
+-
+- return true;
+-}
+-
+ static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port)
+ {
+ return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII);
+@@ -2505,44 +2474,6 @@ static void mt7531_mac_port_get_caps(str
+ }
+ }
+
+-static bool
+-mt7531_phy_mode_supported(struct dsa_switch *ds, int port,
+- const struct phylink_link_state *state)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- switch (port) {
+- case 0 ... 4: /* Internal phy */
+- if (state->interface != PHY_INTERFACE_MODE_GMII)
+- return false;
+- break;
+- case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */
+- if (mt7531_is_rgmii_port(priv, port))
+- return phy_interface_mode_is_rgmii(state->interface);
+- fallthrough;
+- case 6: /* 1st cpu port supports sgmii/8023z only */
+- if (state->interface != PHY_INTERFACE_MODE_SGMII &&
+- !phy_interface_mode_is_8023z(state->interface))
+- return false;
+- break;
+- default:
+- dev_err(priv->dev, "%s: unsupported port: %i\n", __func__,
+- port);
+- return false;
+- }
+-
+- return true;
+-}
+-
+-static bool
+-mt753x_phy_mode_supported(struct dsa_switch *ds, int port,
+- const struct phylink_link_state *state)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- return priv->info->phy_mode_supported(ds, port, state);
+-}
+-
+ static int
+ mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state)
+ {
+@@ -2797,9 +2728,6 @@ mt753x_phylink_mac_config(struct dsa_swi
+ struct mt7530_priv *priv = ds->priv;
+ u32 mcr_cur, mcr_new;
+
+- if (!mt753x_phy_mode_supported(ds, port, state))
+- goto unsupported;
+-
+ switch (port) {
+ case 0 ... 4: /* Internal phy */
+ if (state->interface != PHY_INTERFACE_MODE_GMII)
+@@ -3015,12 +2943,6 @@ mt753x_phylink_validate(struct dsa_switc
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+ struct mt7530_priv *priv = ds->priv;
+
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- !mt753x_phy_mode_supported(ds, port, state)) {
+- linkmode_zero(supported);
+- return;
+- }
+-
+ phylink_set_port_modes(mask);
+
+ if (state->interface != PHY_INTERFACE_MODE_TRGMII &&
+@@ -3247,7 +3169,6 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .phy_mode_supported = mt7530_phy_mode_supported,
+ .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+@@ -3259,7 +3180,6 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .phy_mode_supported = mt7530_phy_mode_supported,
+ .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+@@ -3272,7 +3192,6 @@ static const struct mt753x_info mt753x_t
+ .pad_setup = mt7531_pad_setup,
+ .cpu_port_config = mt7531_cpu_port_config,
+ .mac_port_get_caps = mt7531_mac_port_get_caps,
+- .phy_mode_supported = mt7531_phy_mode_supported,
+ .mac_port_validate = mt7531_mac_port_validate,
+ .mac_port_get_state = mt7531_phylink_mac_link_state,
+ .mac_port_config = mt7531_mac_config,
+@@ -3335,7 +3254,6 @@ mt7530_probe(struct mdio_device *mdiodev
+ if (!priv->info->sw_setup || !priv->info->pad_setup ||
+ !priv->info->phy_read || !priv->info->phy_write ||
+ !priv->info->mac_port_get_caps ||
+- !priv->info->phy_mode_supported ||
+ !priv->info->mac_port_validate ||
+ !priv->info->mac_port_get_state || !priv->info->mac_port_config)
+ return -EINVAL;
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -771,8 +771,6 @@ struct mt753x_info {
+ int (*cpu_port_config)(struct dsa_switch *ds, int port);
+ void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
+ struct phylink_config *config);
+- bool (*phy_mode_supported)(struct dsa_switch *ds, int port,
+- const struct phylink_link_state *state);
+ void (*mac_port_validate)(struct dsa_switch *ds, int port,
+ unsigned long *supported);
+ int (*mac_port_get_state)(struct dsa_switch *ds, int port,
diff --git a/target/linux/generic/backport-6.1/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch b/target/linux/generic/backport-6.1/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch
new file mode 100644
index 0000000000..e607a3c7d2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch
@@ -0,0 +1,34 @@
+From 58344a3b85f1bd5ffddfc2c11f6f2bf688b5f990 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:12 +0100
+Subject: [PATCH 04/13] net: dsa: mt7530: drop use of
+ phylink_helper_basex_speed()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Now that we have a better method to select SFP interface modes, we
+no longer need to use phylink_helper_basex_speed() in a driver's
+validation function.
+
+Tested-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 5 -----
+ 1 file changed, 5 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2967,11 +2967,6 @@ mt753x_phylink_validate(struct dsa_switc
+
+ linkmode_and(supported, supported, mask);
+ linkmode_and(state->advertising, state->advertising, mask);
+-
+- /* We can only operate at 2500BaseX or 1000BaseX. If requested
+- * to advertise both, only report advertising at 2500BaseX.
+- */
+- phylink_helper_basex_speed(state);
+ }
+
+ static int
diff --git a/target/linux/generic/backport-6.1/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch b/target/linux/generic/backport-6.1/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch
new file mode 100644
index 0000000000..63a3317630
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch
@@ -0,0 +1,86 @@
+From 3c1d788a62dc648d1846049b66119ebb69dedd52 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:17 +0100
+Subject: [PATCH 05/13] net: dsa: mt7530: only indicate linkmodes that can be
+ supported
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Now that mt7530 is not using the basex helper, it becomes unnecessary to
+indicate support for both 1000baseX and 2500baseX when one of the 803.3z
+PHY interface modes is being selected. Ensure that the driver indicates
+only those linkmodes that can actually be supported by the PHY interface
+mode.
+
+Tested-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 12 ++++++++----
+ drivers/net/dsa/mt7530.h | 1 +
+ 2 files changed, 9 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2545,12 +2545,13 @@ static int mt7531_rgmii_setup(struct mt7
+ }
+
+ static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port,
++ phy_interface_t interface,
+ unsigned long *supported)
+ {
+ /* Port5 supports ethier RGMII or SGMII.
+ * Port6 supports SGMII only.
+ */
+- if (port == 6) {
++ if (port == 6 && interface == PHY_INTERFACE_MODE_2500BASEX) {
+ phylink_set(supported, 2500baseX_Full);
+ phylink_set(supported, 2500baseT_Full);
+ }
+@@ -2923,16 +2924,18 @@ static void mt753x_phylink_get_caps(stru
+
+ static void
+ mt7530_mac_port_validate(struct dsa_switch *ds, int port,
++ phy_interface_t interface,
+ unsigned long *supported)
+ {
+ }
+
+ static void mt7531_mac_port_validate(struct dsa_switch *ds, int port,
++ phy_interface_t interface,
+ unsigned long *supported)
+ {
+ struct mt7530_priv *priv = ds->priv;
+
+- mt7531_sgmii_validate(priv, port, supported);
++ mt7531_sgmii_validate(priv, port, interface, supported);
+ }
+
+ static void
+@@ -2955,12 +2958,13 @@ mt753x_phylink_validate(struct dsa_switc
+ }
+
+ /* This switch only supports 1G full-duplex. */
+- if (state->interface != PHY_INTERFACE_MODE_MII) {
++ if (state->interface != PHY_INTERFACE_MODE_MII &&
++ state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+ phylink_set(mask, 1000baseT_Full);
+ phylink_set(mask, 1000baseX_Full);
+ }
+
+- priv->info->mac_port_validate(ds, port, mask);
++ priv->info->mac_port_validate(ds, port, state->interface, mask);
+
+ phylink_set(mask, Pause);
+ phylink_set(mask, Asym_Pause);
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -772,6 +772,7 @@ struct mt753x_info {
+ void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
+ struct phylink_config *config);
+ void (*mac_port_validate)(struct dsa_switch *ds, int port,
++ phy_interface_t interface,
+ unsigned long *supported);
+ int (*mac_port_get_state)(struct dsa_switch *ds, int port,
+ struct phylink_link_state *state);
diff --git a/target/linux/generic/backport-6.1/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch b/target/linux/generic/backport-6.1/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch
new file mode 100644
index 0000000000..97699d4c5e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch
@@ -0,0 +1,131 @@
+From 1c2211cb15dd3957fb26c0e1615eceb5db851ad6 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:22 +0100
+Subject: [PATCH 06/13] net: dsa: mt7530: switch to use phylink_get_linkmodes()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Switch mt7530 to use phylink_get_linkmodes() to generate the ethtool
+linkmodes that can be supported. We are unable to use the generic
+helper for this as pause modes are dependent on the interface as
+the Autoneg bit depends on the interface mode.
+
+Tested-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 57 ++++------------------------------------
+ 1 file changed, 5 insertions(+), 52 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2544,19 +2544,6 @@ static int mt7531_rgmii_setup(struct mt7
+ return 0;
+ }
+
+-static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port,
+- phy_interface_t interface,
+- unsigned long *supported)
+-{
+- /* Port5 supports ethier RGMII or SGMII.
+- * Port6 supports SGMII only.
+- */
+- if (port == 6 && interface == PHY_INTERFACE_MODE_2500BASEX) {
+- phylink_set(supported, 2500baseX_Full);
+- phylink_set(supported, 2500baseT_Full);
+- }
+-}
+-
+ static void
+ mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port,
+ unsigned int mode, phy_interface_t interface,
+@@ -2923,51 +2910,21 @@ static void mt753x_phylink_get_caps(stru
+ }
+
+ static void
+-mt7530_mac_port_validate(struct dsa_switch *ds, int port,
+- phy_interface_t interface,
+- unsigned long *supported)
+-{
+-}
+-
+-static void mt7531_mac_port_validate(struct dsa_switch *ds, int port,
+- phy_interface_t interface,
+- unsigned long *supported)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- mt7531_sgmii_validate(priv, port, interface, supported);
+-}
+-
+-static void
+ mt753x_phylink_validate(struct dsa_switch *ds, int port,
+ unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+- struct mt7530_priv *priv = ds->priv;
++ u32 caps;
++
++ caps = dsa_to_port(ds, port)->pl_config.mac_capabilities;
+
+ phylink_set_port_modes(mask);
++ phylink_get_linkmodes(mask, state->interface, caps);
+
+ if (state->interface != PHY_INTERFACE_MODE_TRGMII &&
+- !phy_interface_mode_is_8023z(state->interface)) {
+- phylink_set(mask, 10baseT_Half);
+- phylink_set(mask, 10baseT_Full);
+- phylink_set(mask, 100baseT_Half);
+- phylink_set(mask, 100baseT_Full);
++ !phy_interface_mode_is_8023z(state->interface))
+ phylink_set(mask, Autoneg);
+- }
+-
+- /* This switch only supports 1G full-duplex. */
+- if (state->interface != PHY_INTERFACE_MODE_MII &&
+- state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- }
+-
+- priv->info->mac_port_validate(ds, port, state->interface, mask);
+-
+- phylink_set(mask, Pause);
+- phylink_set(mask, Asym_Pause);
+
+ linkmode_and(supported, supported, mask);
+ linkmode_and(state->advertising, state->advertising, mask);
+@@ -3168,7 +3125,6 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+ },
+@@ -3179,7 +3135,6 @@ static const struct mt753x_info mt753x_t
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .mac_port_validate = mt7530_mac_port_validate,
+ .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+ },
+@@ -3191,7 +3146,6 @@ static const struct mt753x_info mt753x_t
+ .pad_setup = mt7531_pad_setup,
+ .cpu_port_config = mt7531_cpu_port_config,
+ .mac_port_get_caps = mt7531_mac_port_get_caps,
+- .mac_port_validate = mt7531_mac_port_validate,
+ .mac_port_get_state = mt7531_phylink_mac_link_state,
+ .mac_port_config = mt7531_mac_config,
+ .mac_pcs_an_restart = mt7531_sgmii_restart_an,
+@@ -3253,7 +3207,6 @@ mt7530_probe(struct mdio_device *mdiodev
+ if (!priv->info->sw_setup || !priv->info->pad_setup ||
+ !priv->info->phy_read || !priv->info->phy_write ||
+ !priv->info->mac_port_get_caps ||
+- !priv->info->mac_port_validate ||
+ !priv->info->mac_port_get_state || !priv->info->mac_port_config)
+ return -EINVAL;
+
diff --git a/target/linux/generic/backport-6.1/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch b/target/linux/generic/backport-6.1/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch
new file mode 100644
index 0000000000..b9b74833d2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch
@@ -0,0 +1,385 @@
+From fd993fd59d96d5e2d5972ec4ca1f9651025c987b Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:27 +0100
+Subject: [PATCH 07/13] net: dsa: mt7530: partially convert to phylink_pcs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Partially convert the mt7530 driver to use phylink's PCS support. This
+is a partial implementation as we don't move anything into the
+pcs_config method yet - this driver supports SGMII or 1000BASE-X
+without in-band.
+
+Tested-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 144 +++++++++++++++++++++++----------------
+ drivers/net/dsa/mt7530.h | 21 +++---
+ 2 files changed, 95 insertions(+), 70 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -24,6 +24,11 @@
+
+ #include "mt7530.h"
+
++static struct mt753x_pcs *pcs_to_mt753x_pcs(struct phylink_pcs *pcs)
++{
++ return container_of(pcs, struct mt753x_pcs, pcs);
++}
++
+ /* String, offset, and register size in bytes if different from 4 bytes */
+ static const struct mt7530_mib_desc mt7530_mib[] = {
+ MIB_DESC(1, 0x00, "TxDrop"),
+@@ -2544,12 +2549,11 @@ static int mt7531_rgmii_setup(struct mt7
+ return 0;
+ }
+
+-static void
+-mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port,
+- unsigned int mode, phy_interface_t interface,
+- int speed, int duplex)
++static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface, int speed, int duplex)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
++ int port = pcs_to_mt753x_pcs(pcs)->port;
+ unsigned int val;
+
+ /* For adjusting speed and duplex of SGMII force mode. */
+@@ -2575,6 +2579,9 @@ mt7531_sgmii_link_up_force(struct dsa_sw
+
+ /* MT7531 SGMII 1G force mode can only work in full duplex mode,
+ * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
++ *
++ * The speed check is unnecessary as the MAC capabilities apply
++ * this restriction. --rmk
+ */
+ if ((speed == SPEED_10 || speed == SPEED_100) &&
+ duplex != DUPLEX_FULL)
+@@ -2650,9 +2657,10 @@ static int mt7531_sgmii_setup_mode_an(st
+ return 0;
+ }
+
+-static void mt7531_sgmii_restart_an(struct dsa_switch *ds, int port)
++static void mt7531_pcs_an_restart(struct phylink_pcs *pcs)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
++ int port = pcs_to_mt753x_pcs(pcs)->port;
+ u32 val;
+
+ /* Only restart AN when AN is enabled */
+@@ -2709,6 +2717,24 @@ mt753x_mac_config(struct dsa_switch *ds,
+ return priv->info->mac_port_config(ds, port, mode, state->interface);
+ }
+
++static struct phylink_pcs *
++mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
++ phy_interface_t interface)
++{
++ struct mt7530_priv *priv = ds->priv;
++
++ switch (interface) {
++ case PHY_INTERFACE_MODE_TRGMII:
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_1000BASEX:
++ case PHY_INTERFACE_MODE_2500BASEX:
++ return &priv->pcs[port].pcs;
++
++ default:
++ return NULL;
++ }
++}
++
+ static void
+ mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+ const struct phylink_link_state *state)
+@@ -2770,17 +2796,6 @@ unsupported:
+ mt7530_write(priv, MT7530_PMCR_P(port), mcr_new);
+ }
+
+-static void
+-mt753x_phylink_mac_an_restart(struct dsa_switch *ds, int port)
+-{
+- struct mt7530_priv *priv = ds->priv;
+-
+- if (!priv->info->mac_pcs_an_restart)
+- return;
+-
+- priv->info->mac_pcs_an_restart(ds, port);
+-}
+-
+ static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface)
+@@ -2790,16 +2805,13 @@ static void mt753x_phylink_mac_link_down
+ mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
+ }
+
+-static void mt753x_mac_pcs_link_up(struct dsa_switch *ds, int port,
+- unsigned int mode, phy_interface_t interface,
+- int speed, int duplex)
++static void mt753x_phylink_pcs_link_up(struct phylink_pcs *pcs,
++ unsigned int mode,
++ phy_interface_t interface,
++ int speed, int duplex)
+ {
+- struct mt7530_priv *priv = ds->priv;
+-
+- if (!priv->info->mac_pcs_link_up)
+- return;
+-
+- priv->info->mac_pcs_link_up(ds, port, mode, interface, speed, duplex);
++ if (pcs->ops->pcs_link_up)
++ pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
+ }
+
+ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
+@@ -2812,8 +2824,6 @@ static void mt753x_phylink_mac_link_up(s
+ struct mt7530_priv *priv = ds->priv;
+ u32 mcr;
+
+- mt753x_mac_pcs_link_up(ds, port, mode, interface, speed, duplex);
+-
+ mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK;
+
+ /* MT753x MAC works in 1G full duplex mode for all up-clocked
+@@ -2891,6 +2901,8 @@ mt7531_cpu_port_config(struct dsa_switch
+ return ret;
+ mt7530_write(priv, MT7530_PMCR_P(port),
+ PMCR_CPU_PORT_SETTING(priv->id));
++ mt753x_phylink_pcs_link_up(&priv->pcs[port].pcs, MLO_AN_FIXED,
++ interface, speed, DUPLEX_FULL);
+ mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL,
+ speed, DUPLEX_FULL, true, true);
+
+@@ -2930,16 +2942,13 @@ mt753x_phylink_validate(struct dsa_switc
+ linkmode_and(state->advertising, state->advertising, mask);
+ }
+
+-static int
+-mt7530_phylink_mac_link_state(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state)
++static void mt7530_pcs_get_state(struct phylink_pcs *pcs,
++ struct phylink_link_state *state)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
++ int port = pcs_to_mt753x_pcs(pcs)->port;
+ u32 pmsr;
+
+- if (port < 0 || port >= MT7530_NUM_PORTS)
+- return -EINVAL;
+-
+ pmsr = mt7530_read(priv, MT7530_PMSR_P(port));
+
+ state->link = (pmsr & PMSR_LINK);
+@@ -2966,8 +2975,6 @@ mt7530_phylink_mac_link_state(struct dsa
+ state->pause |= MLO_PAUSE_RX;
+ if (pmsr & PMSR_TX_FC)
+ state->pause |= MLO_PAUSE_TX;
+-
+- return 1;
+ }
+
+ static int
+@@ -3009,32 +3016,49 @@ mt7531_sgmii_pcs_get_state_an(struct mt7
+ return 0;
+ }
+
+-static int
+-mt7531_phylink_mac_link_state(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state)
++static void mt7531_pcs_get_state(struct phylink_pcs *pcs,
++ struct phylink_link_state *state)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
++ int port = pcs_to_mt753x_pcs(pcs)->port;
+
+ if (state->interface == PHY_INTERFACE_MODE_SGMII)
+- return mt7531_sgmii_pcs_get_state_an(priv, port, state);
+-
+- return -EOPNOTSUPP;
++ mt7531_sgmii_pcs_get_state_an(priv, port, state);
++ else
++ state->link = false;
+ }
+
+-static int
+-mt753x_phylink_mac_link_state(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state)
++static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface,
++ const unsigned long *advertising,
++ bool permit_pause_to_mac)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ return 0;
++}
+
+- return priv->info->mac_port_get_state(ds, port, state);
++static void mt7530_pcs_an_restart(struct phylink_pcs *pcs)
++{
+ }
+
++static const struct phylink_pcs_ops mt7530_pcs_ops = {
++ .pcs_get_state = mt7530_pcs_get_state,
++ .pcs_config = mt753x_pcs_config,
++ .pcs_an_restart = mt7530_pcs_an_restart,
++};
++
++static const struct phylink_pcs_ops mt7531_pcs_ops = {
++ .pcs_get_state = mt7531_pcs_get_state,
++ .pcs_config = mt753x_pcs_config,
++ .pcs_an_restart = mt7531_pcs_an_restart,
++ .pcs_link_up = mt7531_pcs_link_up,
++};
++
+ static int
+ mt753x_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
+ int ret = priv->info->sw_setup(ds);
++ int i;
+
+ if (ret)
+ return ret;
+@@ -3047,6 +3071,13 @@ mt753x_setup(struct dsa_switch *ds)
+ if (ret && priv->irq)
+ mt7530_free_irq_common(priv);
+
++ /* Initialise the PCS devices */
++ for (i = 0; i < priv->ds->num_ports; i++) {
++ priv->pcs[i].pcs.ops = priv->info->pcs_ops;
++ priv->pcs[i].priv = priv;
++ priv->pcs[i].port = i;
++ }
++
+ return ret;
+ }
+
+@@ -3108,9 +3139,8 @@ static const struct dsa_switch_ops mt753
+ .port_mirror_del = mt753x_port_mirror_del,
+ .phylink_get_caps = mt753x_phylink_get_caps,
+ .phylink_validate = mt753x_phylink_validate,
+- .phylink_mac_link_state = mt753x_phylink_mac_link_state,
++ .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs,
+ .phylink_mac_config = mt753x_phylink_mac_config,
+- .phylink_mac_an_restart = mt753x_phylink_mac_an_restart,
+ .phylink_mac_link_down = mt753x_phylink_mac_link_down,
+ .phylink_mac_link_up = mt753x_phylink_mac_link_up,
+ .get_mac_eee = mt753x_get_mac_eee,
+@@ -3120,36 +3150,34 @@ static const struct dsa_switch_ops mt753
+ static const struct mt753x_info mt753x_table[] = {
+ [ID_MT7621] = {
+ .id = ID_MT7621,
++ .pcs_ops = &mt7530_pcs_ops,
+ .sw_setup = mt7530_setup,
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+ },
+ [ID_MT7530] = {
+ .id = ID_MT7530,
++ .pcs_ops = &mt7530_pcs_ops,
+ .sw_setup = mt7530_setup,
+ .phy_read = mt7530_phy_read,
+ .phy_write = mt7530_phy_write,
+ .pad_setup = mt7530_pad_clk_setup,
+ .mac_port_get_caps = mt7530_mac_port_get_caps,
+- .mac_port_get_state = mt7530_phylink_mac_link_state,
+ .mac_port_config = mt7530_mac_config,
+ },
+ [ID_MT7531] = {
+ .id = ID_MT7531,
++ .pcs_ops = &mt7531_pcs_ops,
+ .sw_setup = mt7531_setup,
+ .phy_read = mt7531_ind_phy_read,
+ .phy_write = mt7531_ind_phy_write,
+ .pad_setup = mt7531_pad_setup,
+ .cpu_port_config = mt7531_cpu_port_config,
+ .mac_port_get_caps = mt7531_mac_port_get_caps,
+- .mac_port_get_state = mt7531_phylink_mac_link_state,
+ .mac_port_config = mt7531_mac_config,
+- .mac_pcs_an_restart = mt7531_sgmii_restart_an,
+- .mac_pcs_link_up = mt7531_sgmii_link_up_force,
+ },
+ };
+
+@@ -3207,7 +3235,7 @@ mt7530_probe(struct mdio_device *mdiodev
+ if (!priv->info->sw_setup || !priv->info->pad_setup ||
+ !priv->info->phy_read || !priv->info->phy_write ||
+ !priv->info->mac_port_get_caps ||
+- !priv->info->mac_port_get_state || !priv->info->mac_port_config)
++ !priv->info->mac_port_config)
+ return -EINVAL;
+
+ priv->id = priv->info->id;
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -741,6 +741,12 @@ static const char *p5_intf_modes(unsigne
+
+ struct mt7530_priv;
+
++struct mt753x_pcs {
++ struct phylink_pcs pcs;
++ struct mt7530_priv *priv;
++ int port;
++};
++
+ /* struct mt753x_info - This is the main data structure for holding the specific
+ * part for each supported device
+ * @sw_setup: Holding the handler to a device initialization
+@@ -752,18 +758,14 @@ struct mt7530_priv;
+ * port
+ * @mac_port_validate: Holding the way to set addition validate type for a
+ * certan MAC port
+- * @mac_port_get_state: Holding the way getting the MAC/PCS state for a certain
+- * MAC port
+ * @mac_port_config: Holding the way setting up the PHY attribute to a
+ * certain MAC port
+- * @mac_pcs_an_restart Holding the way restarting PCS autonegotiation for a
+- * certain MAC port
+- * @mac_pcs_link_up: Holding the way setting up the PHY attribute to the pcs
+- * of the certain MAC port
+ */
+ struct mt753x_info {
+ enum mt753x_id id;
+
++ const struct phylink_pcs_ops *pcs_ops;
++
+ int (*sw_setup)(struct dsa_switch *ds);
+ int (*phy_read)(struct mt7530_priv *priv, int port, int regnum);
+ int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val);
+@@ -774,15 +776,9 @@ struct mt753x_info {
+ void (*mac_port_validate)(struct dsa_switch *ds, int port,
+ phy_interface_t interface,
+ unsigned long *supported);
+- int (*mac_port_get_state)(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state);
+ int (*mac_port_config)(struct dsa_switch *ds, int port,
+ unsigned int mode,
+ phy_interface_t interface);
+- void (*mac_pcs_an_restart)(struct dsa_switch *ds, int port);
+- void (*mac_pcs_link_up)(struct dsa_switch *ds, int port,
+- unsigned int mode, phy_interface_t interface,
+- int speed, int duplex);
+ };
+
+ /* struct mt7530_priv - This is the main data structure for holding the state
+@@ -824,6 +820,7 @@ struct mt7530_priv {
+ u8 mirror_tx;
+
+ struct mt7530_port ports[MT7530_NUM_PORTS];
++ struct mt753x_pcs pcs[MT7530_NUM_PORTS];
+ /* protect among processes for registers access*/
+ struct mutex reg_mutex;
+ int irq;
diff --git a/target/linux/generic/backport-6.1/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch b/target/linux/generic/backport-6.1/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch
new file mode 100644
index 0000000000..4aa013aad7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch
@@ -0,0 +1,80 @@
+From 2b0ee6768f3ac09072e5fd60b36580924e1cfa1c Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:32 +0100
+Subject: [PATCH 08/13] net: dsa: mt7530: move autoneg handling to PCS
+ validation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Move the autoneg bit handling to the PCS validation, which allows us to
+get rid of mt753x_phylink_validate() and rely on the default
+phylink_generic_validate() implementation for the MAC side.
+
+Tested-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 28 ++++++++++------------------
+ 1 file changed, 10 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2921,25 +2921,16 @@ static void mt753x_phylink_get_caps(stru
+ priv->info->mac_port_get_caps(ds, port, config);
+ }
+
+-static void
+-mt753x_phylink_validate(struct dsa_switch *ds, int port,
+- unsigned long *supported,
+- struct phylink_link_state *state)
+-{
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+- u32 caps;
+-
+- caps = dsa_to_port(ds, port)->pl_config.mac_capabilities;
+-
+- phylink_set_port_modes(mask);
+- phylink_get_linkmodes(mask, state->interface, caps);
++static int mt753x_pcs_validate(struct phylink_pcs *pcs,
++ unsigned long *supported,
++ const struct phylink_link_state *state)
++{
++ /* Autonegotiation is not supported in TRGMII nor 802.3z modes */
++ if (state->interface == PHY_INTERFACE_MODE_TRGMII ||
++ phy_interface_mode_is_8023z(state->interface))
++ phylink_clear(supported, Autoneg);
+
+- if (state->interface != PHY_INTERFACE_MODE_TRGMII &&
+- !phy_interface_mode_is_8023z(state->interface))
+- phylink_set(mask, Autoneg);
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
++ return 0;
+ }
+
+ static void mt7530_pcs_get_state(struct phylink_pcs *pcs,
+@@ -3041,12 +3032,14 @@ static void mt7530_pcs_an_restart(struct
+ }
+
+ static const struct phylink_pcs_ops mt7530_pcs_ops = {
++ .pcs_validate = mt753x_pcs_validate,
+ .pcs_get_state = mt7530_pcs_get_state,
+ .pcs_config = mt753x_pcs_config,
+ .pcs_an_restart = mt7530_pcs_an_restart,
+ };
+
+ static const struct phylink_pcs_ops mt7531_pcs_ops = {
++ .pcs_validate = mt753x_pcs_validate,
+ .pcs_get_state = mt7531_pcs_get_state,
+ .pcs_config = mt753x_pcs_config,
+ .pcs_an_restart = mt7531_pcs_an_restart,
+@@ -3138,7 +3131,6 @@ static const struct dsa_switch_ops mt753
+ .port_mirror_add = mt753x_port_mirror_add,
+ .port_mirror_del = mt753x_port_mirror_del,
+ .phylink_get_caps = mt753x_phylink_get_caps,
+- .phylink_validate = mt753x_phylink_validate,
+ .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs,
+ .phylink_mac_config = mt753x_phylink_mac_config,
+ .phylink_mac_link_down = mt753x_phylink_mac_link_down,
diff --git a/target/linux/generic/backport-6.1/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch b/target/linux/generic/backport-6.1/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch
new file mode 100644
index 0000000000..d921d78733
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch
@@ -0,0 +1,34 @@
+From 5bc26de9bfaa6bb5539c09d4435dced98f429cfc Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 11 Apr 2022 10:46:37 +0100
+Subject: [PATCH 09/13] net: dsa: mt7530: mark as non-legacy
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The mt7530 driver does not make use of the speed, duplex, pause or
+advertisement in its phylink_mac_config() implementation, so it can be
+marked as a non-legacy driver.
+
+Tested-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2918,6 +2918,12 @@ static void mt753x_phylink_get_caps(stru
+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000FD;
+
++ /* This driver does not make use of the speed, duplex, pause or the
++ * advertisement in its mac_config, so it is safe to mark this driver
++ * as non-legacy.
++ */
++ config->legacy_pre_march2020 = false;
++
+ priv->info->mac_port_get_caps(ds, port, config);
+ }
+
diff --git a/target/linux/generic/backport-6.1/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch b/target/linux/generic/backport-6.1/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch
new file mode 100644
index 0000000000..e797b1ecb4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch
@@ -0,0 +1,116 @@
+From 1f15b5e8733115cee65342bcaafeaf0368809fae Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 25 Apr 2022 22:28:02 +0100
+Subject: [PATCH 10/13] net: dsa: mt753x: fix pcs conversion regression
+
+Daniel Golle reports that the conversion of mt753x to phylink PCS caused
+an oops as below.
+
+The problem is with the placement of the PCS initialisation, which
+occurs after mt7531_setup() has been called. However, burited in this
+function is a call to setup the CPU port, which requires the PCS
+structure to be already setup.
+
+Fix this by changing the initialisation order.
+
+Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020
+Mem abort info:
+ ESR = 0x96000005
+ EC = 0x25: DABT (current EL), IL = 32 bits
+ SET = 0, FnV = 0
+ EA = 0, S1PTW = 0
+ FSC = 0x05: level 1 translation fault
+Data abort info:
+ ISV = 0, ISS = 0x00000005
+ CM = 0, WnR = 0
+user pgtable: 4k pages, 39-bit VAs, pgdp=0000000046057000
+[0000000000000020] pgd=0000000000000000, p4d=0000000000000000, pud=0000000000000000
+Internal error: Oops: 96000005 [#1] SMP
+Modules linked in:
+CPU: 0 PID: 32 Comm: kworker/u4:1 Tainted: G S 5.18.0-rc3-next-20220422+ #0
+Hardware name: Bananapi BPI-R64 (DT)
+Workqueue: events_unbound deferred_probe_work_func
+pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+pc : mt7531_cpu_port_config+0xcc/0x1b0
+lr : mt7531_cpu_port_config+0xc0/0x1b0
+sp : ffffffc008d5b980
+x29: ffffffc008d5b990 x28: ffffff80060562c8 x27: 00000000f805633b
+x26: ffffff80001a8880 x25: 00000000000009c4 x24: 0000000000000016
+x23: ffffff8005eb6470 x22: 0000000000003600 x21: ffffff8006948080
+x20: 0000000000000000 x19: 0000000000000006 x18: 0000000000000000
+x17: 0000000000000001 x16: 0000000000000001 x15: 02963607fcee069e
+x14: 0000000000000000 x13: 0000000000000030 x12: 0101010101010101
+x11: ffffffc037302000 x10: 0000000000000870 x9 : ffffffc008d5b800
+x8 : ffffff800028f950 x7 : 0000000000000001 x6 : 00000000662b3000
+x5 : 00000000000002f0 x4 : 0000000000000000 x3 : ffffff800028f080
+x2 : 0000000000000000 x1 : ffffff800028f080 x0 : 0000000000000000
+Call trace:
+ mt7531_cpu_port_config+0xcc/0x1b0
+ mt753x_cpu_port_enable+0x24/0x1f0
+ mt7531_setup+0x49c/0x5c0
+ mt753x_setup+0x20/0x31c
+ dsa_register_switch+0x8bc/0x1020
+ mt7530_probe+0x118/0x200
+ mdio_probe+0x30/0x64
+ really_probe.part.0+0x98/0x280
+ __driver_probe_device+0x94/0x140
+ driver_probe_device+0x40/0x114
+ __device_attach_driver+0xb0/0x10c
+ bus_for_each_drv+0x64/0xa0
+ __device_attach+0xa8/0x16c
+ device_initial_probe+0x10/0x20
+ bus_probe_device+0x94/0x9c
+ deferred_probe_work_func+0x80/0xb4
+ process_one_work+0x200/0x3a0
+ worker_thread+0x260/0x4c0
+ kthread+0xd4/0xe0
+ ret_from_fork+0x10/0x20
+Code: 9409e911 937b7e60 8b0002a0 f9405800 (f9401005)
+---[ end trace 0000000000000000 ]---
+
+Reported-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+Fixes: cbd1f243bc41 ("net: dsa: mt7530: partially convert to phylink_pcs")
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Link: https://lore.kernel.org/r/E1nj6FW-007WZB-5Y@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -3056,9 +3056,16 @@ static int
+ mt753x_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
+- int ret = priv->info->sw_setup(ds);
+- int i;
++ int i, ret;
+
++ /* Initialise the PCS devices */
++ for (i = 0; i < priv->ds->num_ports; i++) {
++ priv->pcs[i].pcs.ops = priv->info->pcs_ops;
++ priv->pcs[i].priv = priv;
++ priv->pcs[i].port = i;
++ }
++
++ ret = priv->info->sw_setup(ds);
+ if (ret)
+ return ret;
+
+@@ -3070,13 +3077,6 @@ mt753x_setup(struct dsa_switch *ds)
+ if (ret && priv->irq)
+ mt7530_free_irq_common(priv);
+
+- /* Initialise the PCS devices */
+- for (i = 0; i < priv->ds->num_ports; i++) {
+- priv->pcs[i].pcs.ops = priv->info->pcs_ops;
+- priv->pcs[i].priv = priv;
+- priv->pcs[i].port = i;
+- }
+-
+ return ret;
+ }
+
diff --git a/target/linux/generic/backport-6.1/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch b/target/linux/generic/backport-6.1/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch
new file mode 100644
index 0000000000..26d207ca6a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch
@@ -0,0 +1,87 @@
+From e26be16262e1fc1e9f1798c12762663bd9c265c6 Mon Sep 17 00:00:00 2001
+From: Frank Wunderlich <frank-w@public-files.de>
+Date: Fri, 10 Jun 2022 19:05:37 +0200
+Subject: [PATCH 11/13] net: dsa: mt7530: rework mt7530_hw_vlan_{add,del}
+
+Rework vlan_add/vlan_del functions in preparation for dynamic cpu port.
+
+Currently BIT(MT7530_CPU_PORT) is added to new_members, even though
+mt7530_port_vlan_add() will be called on the CPU port too.
+
+Let DSA core decide when to call port_vlan_add for the CPU port, rather
+than doing it implicitly.
+
+We can do autonomous forwarding in a certain VLAN, but not add br0 to that
+VLAN and avoid flooding the CPU with those packets, if software knows it
+doesn't need to process them.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 30 ++++++++++++------------------
+ 1 file changed, 12 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1536,11 +1536,11 @@ static void
+ mt7530_hw_vlan_add(struct mt7530_priv *priv,
+ struct mt7530_hw_vlan_entry *entry)
+ {
++ struct dsa_port *dp = dsa_to_port(priv->ds, entry->port);
+ u8 new_members;
+ u32 val;
+
+- new_members = entry->old_members | BIT(entry->port) |
+- BIT(MT7530_CPU_PORT);
++ new_members = entry->old_members | BIT(entry->port);
+
+ /* Validate the entry with independent learning, create egress tag per
+ * VLAN and joining the port as one of the port members.
+@@ -1551,22 +1551,20 @@ mt7530_hw_vlan_add(struct mt7530_priv *p
+
+ /* Decide whether adding tag or not for those outgoing packets from the
+ * port inside the VLAN.
+- */
+- val = entry->untagged ? MT7530_VLAN_EGRESS_UNTAG :
+- MT7530_VLAN_EGRESS_TAG;
+- mt7530_rmw(priv, MT7530_VAWD2,
+- ETAG_CTRL_P_MASK(entry->port),
+- ETAG_CTRL_P(entry->port, val));
+-
+- /* CPU port is always taken as a tagged port for serving more than one
++ * CPU port is always taken as a tagged port for serving more than one
+ * VLANs across and also being applied with egress type stack mode for
+ * that VLAN tags would be appended after hardware special tag used as
+ * DSA tag.
+ */
++ if (dsa_port_is_cpu(dp))
++ val = MT7530_VLAN_EGRESS_STACK;
++ else if (entry->untagged)
++ val = MT7530_VLAN_EGRESS_UNTAG;
++ else
++ val = MT7530_VLAN_EGRESS_TAG;
+ mt7530_rmw(priv, MT7530_VAWD2,
+- ETAG_CTRL_P_MASK(MT7530_CPU_PORT),
+- ETAG_CTRL_P(MT7530_CPU_PORT,
+- MT7530_VLAN_EGRESS_STACK));
++ ETAG_CTRL_P_MASK(entry->port),
++ ETAG_CTRL_P(entry->port, val));
+ }
+
+ static void
+@@ -1585,11 +1583,7 @@ mt7530_hw_vlan_del(struct mt7530_priv *p
+ return;
+ }
+
+- /* If certain member apart from CPU port is still alive in the VLAN,
+- * the entry would be kept valid. Otherwise, the entry is got to be
+- * disabled.
+- */
+- if (new_members && new_members != BIT(MT7530_CPU_PORT)) {
++ if (new_members) {
+ val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) |
+ VLAN_VALID;
+ mt7530_write(priv, MT7530_VAWD1, val);
diff --git a/target/linux/generic/backport-6.1/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch b/target/linux/generic/backport-6.1/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch
new file mode 100644
index 0000000000..b02705cc70
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch
@@ -0,0 +1,75 @@
+From 1f0dfd443eea7fc3e818e96f7c8264913ba41859 Mon Sep 17 00:00:00 2001
+From: Frank Wunderlich <frank-w@public-files.de>
+Date: Fri, 10 Jun 2022 19:05:38 +0200
+Subject: [PATCH 12/13] net: dsa: mt7530: rework mt753[01]_setup
+
+Enumerate available cpu-ports instead of using hardcoded constant.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 25 +++++++++++++++++++++----
+ 1 file changed, 21 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2101,11 +2101,12 @@ static int
+ mt7530_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
++ struct device_node *dn = NULL;
+ struct device_node *phy_node;
+ struct device_node *mac_np;
+ struct mt7530_dummy_poll p;
+ phy_interface_t interface;
+- struct device_node *dn;
++ struct dsa_port *cpu_dp;
+ u32 id, val;
+ int ret, i;
+
+@@ -2113,7 +2114,19 @@ mt7530_setup(struct dsa_switch *ds)
+ * controller also is the container for two GMACs nodes representing
+ * as two netdev instances.
+ */
+- dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent;
++ dsa_switch_for_each_cpu_port(cpu_dp, ds) {
++ dn = cpu_dp->master->dev.of_node->parent;
++ /* It doesn't matter which CPU port is found first,
++ * their masters should share the same parent OF node
++ */
++ break;
++ }
++
++ if (!dn) {
++ dev_err(ds->dev, "parent OF node of DSA master not found");
++ return -EINVAL;
++ }
++
+ ds->assisted_learning_on_cpu_port = true;
+ ds->mtu_enforcement_ingress = true;
+
+@@ -2286,6 +2299,7 @@ mt7531_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
+ struct mt7530_dummy_poll p;
++ struct dsa_port *cpu_dp;
+ u32 val, id;
+ int ret, i;
+
+@@ -2360,8 +2374,11 @@ mt7531_setup(struct dsa_switch *ds)
+ CORE_PLL_GROUP4, val);
+
+ /* BPDU to CPU port */
+- mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK,
+- BIT(MT7530_CPU_PORT));
++ dsa_switch_for_each_cpu_port(cpu_dp, ds) {
++ mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK,
++ BIT(cpu_dp->index));
++ break;
++ }
+ mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK,
+ MT753X_BPDU_CPU_ONLY);
+
diff --git a/target/linux/generic/backport-6.1/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch b/target/linux/generic/backport-6.1/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch
new file mode 100644
index 0000000000..2bfa1ef438
--- /dev/null
+++ b/target/linux/generic/backport-6.1/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch
@@ -0,0 +1,117 @@
+From ad2606f6fafae3a7d41c4f2af5554c8f6adecbc7 Mon Sep 17 00:00:00 2001
+From: Frank Wunderlich <frank-w@public-files.de>
+Date: Fri, 10 Jun 2022 19:05:39 +0200
+Subject: [PATCH 13/13] net: dsa: mt7530: get cpu-port via dp->cpu_dp instead
+ of constant
+
+Replace last occurences of hardcoded cpu-port by cpu_dp member of
+dsa_port struct.
+
+Now the constant can be dropped.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 27 ++++++++++++++++++++-------
+ drivers/net/dsa/mt7530.h | 1 -
+ 2 files changed, 20 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1040,6 +1040,7 @@ static int
+ mt7530_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+ {
++ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+@@ -1048,7 +1049,11 @@ mt7530_port_enable(struct dsa_switch *ds
+ * restore the port matrix if the port is the member of a certain
+ * bridge.
+ */
+- priv->ports[port].pm |= PCR_MATRIX(BIT(MT7530_CPU_PORT));
++ if (dsa_port_is_user(dp)) {
++ struct dsa_port *cpu_dp = dp->cpu_dp;
++
++ priv->ports[port].pm |= PCR_MATRIX(BIT(cpu_dp->index));
++ }
+ priv->ports[port].enable = true;
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+ priv->ports[port].pm);
+@@ -1196,7 +1201,8 @@ mt7530_port_bridge_join(struct dsa_switc
+ struct net_device *bridge)
+ {
+ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
+- u32 port_bitmap = BIT(MT7530_CPU_PORT);
++ struct dsa_port *cpu_dp = dp->cpu_dp;
++ u32 port_bitmap = BIT(cpu_dp->index);
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+@@ -1273,9 +1279,12 @@ mt7530_port_set_vlan_unaware(struct dsa_
+ * the CPU port get out of VLAN filtering mode.
+ */
+ if (all_user_ports_removed) {
+- mt7530_write(priv, MT7530_PCR_P(MT7530_CPU_PORT),
++ struct dsa_port *dp = dsa_to_port(ds, port);
++ struct dsa_port *cpu_dp = dp->cpu_dp;
++
++ mt7530_write(priv, MT7530_PCR_P(cpu_dp->index),
+ PCR_MATRIX(dsa_user_ports(priv->ds)));
+- mt7530_write(priv, MT7530_PVC_P(MT7530_CPU_PORT), PORT_SPEC_TAG
++ mt7530_write(priv, MT7530_PVC_P(cpu_dp->index), PORT_SPEC_TAG
+ | PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
+ }
+ }
+@@ -1325,6 +1334,7 @@ mt7530_port_bridge_leave(struct dsa_swit
+ struct net_device *bridge)
+ {
+ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
++ struct dsa_port *cpu_dp = dp->cpu_dp;
+ struct mt7530_priv *priv = ds->priv;
+
+ mutex_lock(&priv->reg_mutex);
+@@ -1353,8 +1363,8 @@ mt7530_port_bridge_leave(struct dsa_swit
+ */
+ if (priv->ports[port].enable)
+ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+- PCR_MATRIX(BIT(MT7530_CPU_PORT)));
+- priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT));
++ PCR_MATRIX(BIT(cpu_dp->index)));
++ priv->ports[port].pm = PCR_MATRIX(BIT(cpu_dp->index));
+
+ /* When a port is removed from the bridge, the port would be set up
+ * back to the default as is at initial boot which is a VLAN-unaware
+@@ -1517,6 +1527,9 @@ static int
+ mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+ struct netlink_ext_ack *extack)
+ {
++ struct dsa_port *dp = dsa_to_port(ds, port);
++ struct dsa_port *cpu_dp = dp->cpu_dp;
++
+ if (vlan_filtering) {
+ /* The port is being kept as VLAN-unaware port when bridge is
+ * set up with vlan_filtering not being set, Otherwise, the
+@@ -1524,7 +1537,7 @@ mt7530_port_vlan_filtering(struct dsa_sw
+ * for becoming a VLAN-aware port.
+ */
+ mt7530_port_set_vlan_aware(ds, port);
+- mt7530_port_set_vlan_aware(ds, MT7530_CPU_PORT);
++ mt7530_port_set_vlan_aware(ds, cpu_dp->index);
+ } else {
+ mt7530_port_set_vlan_unaware(ds, port);
+ }
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -8,7 +8,6 @@
+
+ #define MT7530_NUM_PORTS 7
+ #define MT7530_NUM_PHYS 5
+-#define MT7530_CPU_PORT 6
+ #define MT7530_NUM_FDB_RECORDS 2048
+ #define MT7530_ALL_MEMBERS 0xff
+
diff --git a/target/linux/generic/backport-6.1/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch b/target/linux/generic/backport-6.1/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch
new file mode 100644
index 0000000000..c5501fb040
--- /dev/null
+++ b/target/linux/generic/backport-6.1/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch
@@ -0,0 +1,330 @@
+From 23233e577ef973c2c5d0dd757a0a4605e34ecb57 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 22 Jul 2022 09:19:36 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on page_pool for single page
+ buffers
+
+Rely on page_pool allocator for single page buffers in order to keep
+them dma mapped and add skb recycling support.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/Kconfig | 1 +
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 185 +++++++++++++++-----
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 10 ++
+ 3 files changed, 156 insertions(+), 40 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/Kconfig
++++ b/drivers/net/ethernet/mediatek/Kconfig
+@@ -16,6 +16,7 @@ config NET_MEDIATEK_SOC
+ depends on NET_DSA || !NET_DSA
+ select PHYLINK
+ select DIMLIB
++ select PAGE_POOL
+ help
+ This driver supports the gigabit ethernet MACs in the
+ MediaTek SoC family.
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1389,6 +1389,68 @@ static void mtk_update_rx_cpu_idx(struct
+ }
+ }
+
++static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth,
++ struct xdp_rxq_info *xdp_q,
++ int id, int size)
++{
++ struct page_pool_params pp_params = {
++ .order = 0,
++ .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
++ .pool_size = size,
++ .nid = NUMA_NO_NODE,
++ .dev = eth->dma_dev,
++ .dma_dir = DMA_FROM_DEVICE,
++ .offset = MTK_PP_HEADROOM,
++ .max_len = MTK_PP_MAX_BUF_SIZE,
++ };
++ struct page_pool *pp;
++ int err;
++
++ pp = page_pool_create(&pp_params);
++ if (IS_ERR(pp))
++ return pp;
++
++ err = xdp_rxq_info_reg(xdp_q, &eth->dummy_dev, id,
++ eth->rx_napi.napi_id);
++ if (err < 0)
++ goto err_free_pp;
++
++ err = xdp_rxq_info_reg_mem_model(xdp_q, MEM_TYPE_PAGE_POOL, pp);
++ if (err)
++ goto err_unregister_rxq;
++
++ return pp;
++
++err_unregister_rxq:
++ xdp_rxq_info_unreg(xdp_q);
++err_free_pp:
++ page_pool_destroy(pp);
++
++ return ERR_PTR(err);
++}
++
++static void *mtk_page_pool_get_buff(struct page_pool *pp, dma_addr_t *dma_addr,
++ gfp_t gfp_mask)
++{
++ struct page *page;
++
++ page = page_pool_alloc_pages(pp, gfp_mask | __GFP_NOWARN);
++ if (!page)
++ return NULL;
++
++ *dma_addr = page_pool_get_dma_addr(page) + MTK_PP_HEADROOM;
++ return page_address(page);
++}
++
++static void mtk_rx_put_buff(struct mtk_rx_ring *ring, void *data, bool napi)
++{
++ if (ring->page_pool)
++ page_pool_put_full_page(ring->page_pool,
++ virt_to_head_page(data), napi);
++ else
++ skb_free_frag(data);
++}
++
+ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+ struct mtk_eth *eth)
+ {
+@@ -1402,9 +1464,9 @@ static int mtk_poll_rx(struct napi_struc
+
+ while (done < budget) {
+ unsigned int pktlen, *rxdcsum;
++ u32 hash, reason, reserve_len;
+ struct net_device *netdev;
+ dma_addr_t dma_addr;
+- u32 hash, reason;
+ int mac = 0;
+
+ ring = mtk_get_rx_ring(eth);
+@@ -1435,36 +1497,54 @@ static int mtk_poll_rx(struct napi_struc
+ goto release_desc;
+
+ /* alloc new buffer */
+- if (ring->frag_size <= PAGE_SIZE)
+- new_data = napi_alloc_frag(ring->frag_size);
+- else
+- new_data = mtk_max_lro_buf_alloc(GFP_ATOMIC);
+- if (unlikely(!new_data)) {
+- netdev->stats.rx_dropped++;
+- goto release_desc;
+- }
+- dma_addr = dma_map_single(eth->dma_dev,
+- new_data + NET_SKB_PAD +
+- eth->ip_align,
+- ring->buf_size,
+- DMA_FROM_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) {
+- skb_free_frag(new_data);
+- netdev->stats.rx_dropped++;
+- goto release_desc;
+- }
++ if (ring->page_pool) {
++ new_data = mtk_page_pool_get_buff(ring->page_pool,
++ &dma_addr,
++ GFP_ATOMIC);
++ if (unlikely(!new_data)) {
++ netdev->stats.rx_dropped++;
++ goto release_desc;
++ }
++ } else {
++ if (ring->frag_size <= PAGE_SIZE)
++ new_data = napi_alloc_frag(ring->frag_size);
++ else
++ new_data = mtk_max_lro_buf_alloc(GFP_ATOMIC);
++
++ if (unlikely(!new_data)) {
++ netdev->stats.rx_dropped++;
++ goto release_desc;
++ }
+
+- dma_unmap_single(eth->dma_dev, trxd.rxd1,
+- ring->buf_size, DMA_FROM_DEVICE);
++ dma_addr = dma_map_single(eth->dma_dev,
++ new_data + NET_SKB_PAD + eth->ip_align,
++ ring->buf_size, DMA_FROM_DEVICE);
++ if (unlikely(dma_mapping_error(eth->dma_dev,
++ dma_addr))) {
++ skb_free_frag(new_data);
++ netdev->stats.rx_dropped++;
++ goto release_desc;
++ }
++
++ dma_unmap_single(eth->dma_dev, trxd.rxd1,
++ ring->buf_size, DMA_FROM_DEVICE);
++ }
+
+ /* receive data */
+ skb = build_skb(data, ring->frag_size);
+ if (unlikely(!skb)) {
+- skb_free_frag(data);
++ mtk_rx_put_buff(ring, data, true);
+ netdev->stats.rx_dropped++;
+ goto skip_rx;
+ }
+- skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
++
++ if (ring->page_pool) {
++ reserve_len = MTK_PP_HEADROOM;
++ skb_mark_for_recycle(skb);
++ } else {
++ reserve_len = NET_SKB_PAD + NET_IP_ALIGN;
++ }
++ skb_reserve(skb, reserve_len);
+
+ pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
+ skb->dev = netdev;
+@@ -1518,7 +1598,6 @@ static int mtk_poll_rx(struct napi_struc
+ skip_rx:
+ ring->data[idx] = new_data;
+ rxd->rxd1 = (unsigned int)dma_addr;
+-
+ release_desc:
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+ rxd->rxd2 = RX_DMA_LSO;
+@@ -1526,7 +1605,6 @@ release_desc:
+ rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size);
+
+ ring->calc_idx = idx;
+-
+ done++;
+ }
+
+@@ -1890,13 +1968,15 @@ static int mtk_rx_alloc(struct mtk_eth *
+ if (!ring->data)
+ return -ENOMEM;
+
+- for (i = 0; i < rx_dma_size; i++) {
+- if (ring->frag_size <= PAGE_SIZE)
+- ring->data[i] = netdev_alloc_frag(ring->frag_size);
+- else
+- ring->data[i] = mtk_max_lro_buf_alloc(GFP_KERNEL);
+- if (!ring->data[i])
+- return -ENOMEM;
++ if (!eth->hwlro) {
++ struct page_pool *pp;
++
++ pp = mtk_create_page_pool(eth, &ring->xdp_q, ring_no,
++ rx_dma_size);
++ if (IS_ERR(pp))
++ return PTR_ERR(pp);
++
++ ring->page_pool = pp;
+ }
+
+ ring->dma = dma_alloc_coherent(eth->dma_dev,
+@@ -1907,16 +1987,33 @@ static int mtk_rx_alloc(struct mtk_eth *
+
+ for (i = 0; i < rx_dma_size; i++) {
+ struct mtk_rx_dma_v2 *rxd;
+-
+- dma_addr_t dma_addr = dma_map_single(eth->dma_dev,
+- ring->data[i] + NET_SKB_PAD + eth->ip_align,
+- ring->buf_size,
+- DMA_FROM_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
+- return -ENOMEM;
++ dma_addr_t dma_addr;
++ void *data;
+
+ rxd = ring->dma + i * eth->soc->txrx.rxd_size;
++ if (ring->page_pool) {
++ data = mtk_page_pool_get_buff(ring->page_pool,
++ &dma_addr, GFP_KERNEL);
++ if (!data)
++ return -ENOMEM;
++ } else {
++ if (ring->frag_size <= PAGE_SIZE)
++ data = netdev_alloc_frag(ring->frag_size);
++ else
++ data = mtk_max_lro_buf_alloc(GFP_KERNEL);
++
++ if (!data)
++ return -ENOMEM;
++
++ dma_addr = dma_map_single(eth->dma_dev,
++ data + NET_SKB_PAD + eth->ip_align,
++ ring->buf_size, DMA_FROM_DEVICE);
++ if (unlikely(dma_mapping_error(eth->dma_dev,
++ dma_addr)))
++ return -ENOMEM;
++ }
+ rxd->rxd1 = (unsigned int)dma_addr;
++ ring->data[i] = data;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+ rxd->rxd2 = RX_DMA_LSO;
+@@ -1932,6 +2029,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+ rxd->rxd8 = 0;
+ }
+ }
++
+ ring->dma_size = rx_dma_size;
+ ring->calc_idx_update = false;
+ ring->calc_idx = rx_dma_size - 1;
+@@ -1983,7 +2081,7 @@ static void mtk_rx_clean(struct mtk_eth
+
+ dma_unmap_single(eth->dma_dev, rxd->rxd1,
+ ring->buf_size, DMA_FROM_DEVICE);
+- skb_free_frag(ring->data[i]);
++ mtk_rx_put_buff(ring, ring->data[i], false);
+ }
+ kfree(ring->data);
+ ring->data = NULL;
+@@ -1995,6 +2093,13 @@ static void mtk_rx_clean(struct mtk_eth
+ ring->dma, ring->phys);
+ ring->dma = NULL;
+ }
++
++ if (ring->page_pool) {
++ if (xdp_rxq_info_is_reg(&ring->xdp_q))
++ xdp_rxq_info_unreg(&ring->xdp_q);
++ page_pool_destroy(ring->page_pool);
++ ring->page_pool = NULL;
++ }
+ }
+
+ static int mtk_hwlro_rx_init(struct mtk_eth *eth)
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -18,6 +18,8 @@
+ #include <linux/rhashtable.h>
+ #include <linux/dim.h>
+ #include <linux/bitfield.h>
++#include <net/page_pool.h>
++#include <linux/bpf_trace.h>
+ #include "mtk_ppe.h"
+
+ #define MTK_QDMA_PAGE_SIZE 2048
+@@ -49,6 +51,11 @@
+ #define MTK_HW_FEATURES_MT7628 (NETIF_F_SG | NETIF_F_RXCSUM)
+ #define NEXT_DESP_IDX(X, Y) (((X) + 1) & ((Y) - 1))
+
++#define MTK_PP_HEADROOM XDP_PACKET_HEADROOM
++#define MTK_PP_PAD (MTK_PP_HEADROOM + \
++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
++#define MTK_PP_MAX_BUF_SIZE (PAGE_SIZE - MTK_PP_PAD)
++
+ #define MTK_QRX_OFFSET 0x10
+
+ #define MTK_MAX_RX_RING_NUM 4
+@@ -743,6 +750,9 @@ struct mtk_rx_ring {
+ bool calc_idx_update;
+ u16 calc_idx;
+ u32 crx_idx_reg;
++ /* page_pool */
++ struct page_pool *page_pool;
++ struct xdp_rxq_info xdp_q;
+ };
+
+ enum mkt_eth_capabilities {
diff --git a/target/linux/generic/backport-6.1/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch b/target/linux/generic/backport-6.1/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch
new file mode 100644
index 0000000000..d94bdabd71
--- /dev/null
+++ b/target/linux/generic/backport-6.1/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch
@@ -0,0 +1,291 @@
+From 7c26c20da5d420cde55618263be4aa2f6de53056 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 22 Jul 2022 09:19:37 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add basic XDP support
+
+Introduce basic XDP support to mtk_eth_soc driver.
+Supported XDP verdicts:
+- XDP_PASS
+- XDP_DROP
+- XDP_REDIRECT
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 162 +++++++++++++++++---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +
+ 2 files changed, 145 insertions(+), 19 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1389,6 +1389,11 @@ static void mtk_update_rx_cpu_idx(struct
+ }
+ }
+
++static bool mtk_page_pool_enabled(struct mtk_eth *eth)
++{
++ return !eth->hwlro;
++}
++
+ static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth,
+ struct xdp_rxq_info *xdp_q,
+ int id, int size)
+@@ -1451,11 +1456,52 @@ static void mtk_rx_put_buff(struct mtk_r
+ skb_free_frag(data);
+ }
+
++static u32 mtk_xdp_run(struct mtk_eth *eth, struct mtk_rx_ring *ring,
++ struct xdp_buff *xdp, struct net_device *dev)
++{
++ struct bpf_prog *prog;
++ u32 act = XDP_PASS;
++
++ rcu_read_lock();
++
++ prog = rcu_dereference(eth->prog);
++ if (!prog)
++ goto out;
++
++ act = bpf_prog_run_xdp(prog, xdp);
++ switch (act) {
++ case XDP_PASS:
++ goto out;
++ case XDP_REDIRECT:
++ if (unlikely(xdp_do_redirect(dev, xdp, prog))) {
++ act = XDP_DROP;
++ break;
++ }
++ goto out;
++ default:
++ bpf_warn_invalid_xdp_action(act);
++ fallthrough;
++ case XDP_ABORTED:
++ trace_xdp_exception(dev, prog, act);
++ fallthrough;
++ case XDP_DROP:
++ break;
++ }
++
++ page_pool_put_full_page(ring->page_pool,
++ virt_to_head_page(xdp->data), true);
++out:
++ rcu_read_unlock();
++
++ return act;
++}
++
+ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+ struct mtk_eth *eth)
+ {
+ struct dim_sample dim_sample = {};
+ struct mtk_rx_ring *ring;
++ bool xdp_flush = false;
+ int idx;
+ struct sk_buff *skb;
+ u8 *data, *new_data;
+@@ -1464,9 +1510,9 @@ static int mtk_poll_rx(struct napi_struc
+
+ while (done < budget) {
+ unsigned int pktlen, *rxdcsum;
+- u32 hash, reason, reserve_len;
+ struct net_device *netdev;
+ dma_addr_t dma_addr;
++ u32 hash, reason;
+ int mac = 0;
+
+ ring = mtk_get_rx_ring(eth);
+@@ -1496,8 +1542,14 @@ static int mtk_poll_rx(struct napi_struc
+ if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
+ goto release_desc;
+
++ pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
++
+ /* alloc new buffer */
+ if (ring->page_pool) {
++ struct page *page = virt_to_head_page(data);
++ struct xdp_buff xdp;
++ u32 ret;
++
+ new_data = mtk_page_pool_get_buff(ring->page_pool,
+ &dma_addr,
+ GFP_ATOMIC);
+@@ -1505,6 +1557,34 @@ static int mtk_poll_rx(struct napi_struc
+ netdev->stats.rx_dropped++;
+ goto release_desc;
+ }
++
++ dma_sync_single_for_cpu(eth->dma_dev,
++ page_pool_get_dma_addr(page) + MTK_PP_HEADROOM,
++ pktlen, page_pool_get_dma_dir(ring->page_pool));
++
++ xdp_init_buff(&xdp, PAGE_SIZE, &ring->xdp_q);
++ xdp_prepare_buff(&xdp, data, MTK_PP_HEADROOM, pktlen,
++ false);
++ xdp_buff_clear_frags_flag(&xdp);
++
++ ret = mtk_xdp_run(eth, ring, &xdp, netdev);
++ if (ret == XDP_REDIRECT)
++ xdp_flush = true;
++
++ if (ret != XDP_PASS)
++ goto skip_rx;
++
++ skb = build_skb(data, PAGE_SIZE);
++ if (unlikely(!skb)) {
++ page_pool_put_full_page(ring->page_pool,
++ page, true);
++ netdev->stats.rx_dropped++;
++ goto skip_rx;
++ }
++
++ skb_reserve(skb, xdp.data - xdp.data_hard_start);
++ skb_put(skb, xdp.data_end - xdp.data);
++ skb_mark_for_recycle(skb);
+ } else {
+ if (ring->frag_size <= PAGE_SIZE)
+ new_data = napi_alloc_frag(ring->frag_size);
+@@ -1528,27 +1608,20 @@ static int mtk_poll_rx(struct napi_struc
+
+ dma_unmap_single(eth->dma_dev, trxd.rxd1,
+ ring->buf_size, DMA_FROM_DEVICE);
+- }
+
+- /* receive data */
+- skb = build_skb(data, ring->frag_size);
+- if (unlikely(!skb)) {
+- mtk_rx_put_buff(ring, data, true);
+- netdev->stats.rx_dropped++;
+- goto skip_rx;
+- }
++ skb = build_skb(data, ring->frag_size);
++ if (unlikely(!skb)) {
++ netdev->stats.rx_dropped++;
++ skb_free_frag(data);
++ goto skip_rx;
++ }
+
+- if (ring->page_pool) {
+- reserve_len = MTK_PP_HEADROOM;
+- skb_mark_for_recycle(skb);
+- } else {
+- reserve_len = NET_SKB_PAD + NET_IP_ALIGN;
++ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
++ skb_put(skb, pktlen);
+ }
+- skb_reserve(skb, reserve_len);
+
+- pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
+ skb->dev = netdev;
+- skb_put(skb, pktlen);
++ bytes += skb->len;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+ rxdcsum = &trxd.rxd3;
+@@ -1560,7 +1633,6 @@ static int mtk_poll_rx(struct napi_struc
+ else
+ skb_checksum_none_assert(skb);
+ skb->protocol = eth_type_trans(skb, netdev);
+- bytes += pktlen;
+
+ hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY;
+ if (hash != MTK_RXD4_FOE_ENTRY) {
+@@ -1623,6 +1695,9 @@ rx_done:
+ &dim_sample);
+ net_dim(&eth->rx_dim, dim_sample);
+
++ if (xdp_flush)
++ xdp_do_flush_map();
++
+ return done;
+ }
+
+@@ -1968,7 +2043,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+ if (!ring->data)
+ return -ENOMEM;
+
+- if (!eth->hwlro) {
++ if (mtk_page_pool_enabled(eth)) {
+ struct page_pool *pp;
+
+ pp = mtk_create_page_pool(eth, &ring->xdp_q, ring_no,
+@@ -2710,6 +2785,48 @@ static int mtk_stop(struct net_device *d
+ return 0;
+ }
+
++static int mtk_xdp_setup(struct net_device *dev, struct bpf_prog *prog,
++ struct netlink_ext_ack *extack)
++{
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_eth *eth = mac->hw;
++ struct bpf_prog *old_prog;
++ bool need_update;
++
++ if (eth->hwlro) {
++ NL_SET_ERR_MSG_MOD(extack, "XDP not supported with HWLRO");
++ return -EOPNOTSUPP;
++ }
++
++ if (dev->mtu > MTK_PP_MAX_BUF_SIZE) {
++ NL_SET_ERR_MSG_MOD(extack, "MTU too large for XDP");
++ return -EOPNOTSUPP;
++ }
++
++ need_update = !!eth->prog != !!prog;
++ if (netif_running(dev) && need_update)
++ mtk_stop(dev);
++
++ old_prog = rcu_replace_pointer(eth->prog, prog, lockdep_rtnl_is_held());
++ if (old_prog)
++ bpf_prog_put(old_prog);
++
++ if (netif_running(dev) && need_update)
++ return mtk_open(dev);
++
++ return 0;
++}
++
++static int mtk_xdp(struct net_device *dev, struct netdev_bpf *xdp)
++{
++ switch (xdp->command) {
++ case XDP_SETUP_PROG:
++ return mtk_xdp_setup(dev, xdp->prog, xdp->extack);
++ default:
++ return -EINVAL;
++ }
++}
++
+ static void ethsys_reset(struct mtk_eth *eth, u32 reset_bits)
+ {
+ regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL,
+@@ -3005,6 +3122,12 @@ static int mtk_change_mtu(struct net_dev
+ struct mtk_eth *eth = mac->hw;
+ u32 mcr_cur, mcr_new;
+
++ if (rcu_access_pointer(eth->prog) &&
++ length > MTK_PP_MAX_BUF_SIZE) {
++ netdev_err(dev, "Invalid MTU for XDP mode\n");
++ return -EINVAL;
++ }
++
+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
+ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
+@@ -3332,6 +3455,7 @@ static const struct net_device_ops mtk_n
+ .ndo_poll_controller = mtk_poll_controller,
+ #endif
+ .ndo_setup_tc = mtk_eth_setup_tc,
++ .ndo_bpf = mtk_xdp,
+ };
+
+ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1086,6 +1086,8 @@ struct mtk_eth {
+
+ struct mtk_ppe *ppe;
+ struct rhashtable flow_table;
++
++ struct bpf_prog __rcu *prog;
+ };
+
+ /* struct mtk_mac - the structure that holds the info about the MACs of the
diff --git a/target/linux/generic/backport-6.1/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch b/target/linux/generic/backport-6.1/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch
new file mode 100644
index 0000000000..cf4d658684
--- /dev/null
+++ b/target/linux/generic/backport-6.1/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch
@@ -0,0 +1,110 @@
+From 916a6ee836d6b7b8ef1ed5f0515e256ca60e9968 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 22 Jul 2022 09:19:38 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce xdp ethtool counters
+
+Report xdp stats through ethtool
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 26 +++++++++++++++++++--
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 12 ++++++++++
+ 2 files changed, 36 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -34,6 +34,10 @@ MODULE_PARM_DESC(msg_level, "Message lev
+ #define MTK_ETHTOOL_STAT(x) { #x, \
+ offsetof(struct mtk_hw_stats, x) / sizeof(u64) }
+
++#define MTK_ETHTOOL_XDP_STAT(x) { #x, \
++ offsetof(struct mtk_hw_stats, xdp_stats.x) / \
++ sizeof(u64) }
++
+ static const struct mtk_reg_map mtk_reg_map = {
+ .tx_irq_mask = 0x1a1c,
+ .tx_irq_status = 0x1a18,
+@@ -141,6 +145,13 @@ static const struct mtk_ethtool_stats {
+ MTK_ETHTOOL_STAT(rx_long_errors),
+ MTK_ETHTOOL_STAT(rx_checksum_errors),
+ MTK_ETHTOOL_STAT(rx_flow_control_packets),
++ MTK_ETHTOOL_XDP_STAT(rx_xdp_redirect),
++ MTK_ETHTOOL_XDP_STAT(rx_xdp_pass),
++ MTK_ETHTOOL_XDP_STAT(rx_xdp_drop),
++ MTK_ETHTOOL_XDP_STAT(rx_xdp_tx),
++ MTK_ETHTOOL_XDP_STAT(rx_xdp_tx_errors),
++ MTK_ETHTOOL_XDP_STAT(tx_xdp_xmit),
++ MTK_ETHTOOL_XDP_STAT(tx_xdp_xmit_errors),
+ };
+
+ static const char * const mtk_clks_source_name[] = {
+@@ -1459,6 +1470,9 @@ static void mtk_rx_put_buff(struct mtk_r
+ static u32 mtk_xdp_run(struct mtk_eth *eth, struct mtk_rx_ring *ring,
+ struct xdp_buff *xdp, struct net_device *dev)
+ {
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_hw_stats *hw_stats = mac->hw_stats;
++ u64 *count = &hw_stats->xdp_stats.rx_xdp_drop;
+ struct bpf_prog *prog;
+ u32 act = XDP_PASS;
+
+@@ -1471,13 +1485,16 @@ static u32 mtk_xdp_run(struct mtk_eth *e
+ act = bpf_prog_run_xdp(prog, xdp);
+ switch (act) {
+ case XDP_PASS:
+- goto out;
++ count = &hw_stats->xdp_stats.rx_xdp_pass;
++ goto update_stats;
+ case XDP_REDIRECT:
+ if (unlikely(xdp_do_redirect(dev, xdp, prog))) {
+ act = XDP_DROP;
+ break;
+ }
+- goto out;
++
++ count = &hw_stats->xdp_stats.rx_xdp_redirect;
++ goto update_stats;
+ default:
+ bpf_warn_invalid_xdp_action(act);
+ fallthrough;
+@@ -1490,6 +1507,11 @@ static u32 mtk_xdp_run(struct mtk_eth *e
+
+ page_pool_put_full_page(ring->page_pool,
+ virt_to_head_page(xdp->data), true);
++
++update_stats:
++ u64_stats_update_begin(&hw_stats->syncp);
++ *count = *count + 1;
++ u64_stats_update_end(&hw_stats->syncp);
+ out:
+ rcu_read_unlock();
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -568,6 +568,16 @@ struct mtk_tx_dma_v2 {
+ struct mtk_eth;
+ struct mtk_mac;
+
++struct mtk_xdp_stats {
++ u64 rx_xdp_redirect;
++ u64 rx_xdp_pass;
++ u64 rx_xdp_drop;
++ u64 rx_xdp_tx;
++ u64 rx_xdp_tx_errors;
++ u64 tx_xdp_xmit;
++ u64 tx_xdp_xmit_errors;
++};
++
+ /* struct mtk_hw_stats - the structure that holds the traffic statistics.
+ * @stats_lock: make sure that stats operations are atomic
+ * @reg_offset: the status register offset of the SoC
+@@ -591,6 +601,8 @@ struct mtk_hw_stats {
+ u64 rx_checksum_errors;
+ u64 rx_flow_control_packets;
+
++ struct mtk_xdp_stats xdp_stats;
++
+ spinlock_t stats_lock;
+ u32 reg_offset;
+ struct u64_stats_sync syncp;
diff --git a/target/linux/generic/backport-6.1/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch b/target/linux/generic/backport-6.1/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch
new file mode 100644
index 0000000000..58b49f28a2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch
@@ -0,0 +1,340 @@
+From 5886d26fd25bbe26130e3e5f7474b9b3e98a3469 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 22 Jul 2022 09:19:39 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add xmit XDP support
+
+Introduce XDP support for XDP_TX verdict and ndo_xdp_xmit function
+pointer.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 192 +++++++++++++++++---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 10 +-
+ 2 files changed, 180 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -988,15 +988,26 @@ static void mtk_tx_unmap(struct mtk_eth
+ }
+ }
+
+- tx_buf->flags = 0;
+- if (tx_buf->skb &&
+- (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC)) {
+- if (napi)
+- napi_consume_skb(tx_buf->skb, napi);
++ if (tx_buf->type == MTK_TYPE_SKB) {
++ if (tx_buf->data &&
++ tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
++ struct sk_buff *skb = tx_buf->data;
++
++ if (napi)
++ napi_consume_skb(skb, napi);
++ else
++ dev_kfree_skb_any(skb);
++ }
++ } else if (tx_buf->data) {
++ struct xdp_frame *xdpf = tx_buf->data;
++
++ if (napi && tx_buf->type == MTK_TYPE_XDP_TX)
++ xdp_return_frame_rx_napi(xdpf);
+ else
+- dev_kfree_skb_any(tx_buf->skb);
++ xdp_return_frame(xdpf);
+ }
+- tx_buf->skb = NULL;
++ tx_buf->flags = 0;
++ tx_buf->data = NULL;
+ }
+
+ static void setup_tx_buf(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf,
+@@ -1013,7 +1024,7 @@ static void setup_tx_buf(struct mtk_eth
+ dma_unmap_addr_set(tx_buf, dma_addr1, mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len1, size);
+ } else {
+- tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC;
++ tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
+ txd->txd1 = mapped_addr;
+ txd->txd2 = TX_DMA_PLEN0(size);
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+@@ -1189,7 +1200,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ soc->txrx.txd_size);
+ if (new_desc)
+ memset(tx_buf, 0, sizeof(*tx_buf));
+- tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC;
++ tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
+ tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
+ tx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 :
+ MTK_TX_FLAGS_FPORT1;
+@@ -1203,7 +1214,8 @@ static int mtk_tx_map(struct sk_buff *sk
+ }
+
+ /* store skb to cleanup */
+- itx_buf->skb = skb;
++ itx_buf->type = MTK_TYPE_SKB;
++ itx_buf->data = skb;
+
+ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
+ if (k & 0x1)
+@@ -1415,13 +1427,14 @@ static struct page_pool *mtk_create_page
+ .pool_size = size,
+ .nid = NUMA_NO_NODE,
+ .dev = eth->dma_dev,
+- .dma_dir = DMA_FROM_DEVICE,
+ .offset = MTK_PP_HEADROOM,
+ .max_len = MTK_PP_MAX_BUF_SIZE,
+ };
+ struct page_pool *pp;
+ int err;
+
++ pp_params.dma_dir = rcu_access_pointer(eth->prog) ? DMA_BIDIRECTIONAL
++ : DMA_FROM_DEVICE;
+ pp = page_pool_create(&pp_params);
+ if (IS_ERR(pp))
+ return pp;
+@@ -1467,6 +1480,122 @@ static void mtk_rx_put_buff(struct mtk_r
+ skb_free_frag(data);
+ }
+
++static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf,
++ struct net_device *dev, bool dma_map)
++{
++ const struct mtk_soc_data *soc = eth->soc;
++ struct mtk_tx_ring *ring = &eth->tx_ring;
++ struct mtk_tx_dma_desc_info txd_info = {
++ .size = xdpf->len,
++ .first = true,
++ .last = true,
++ };
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_tx_dma *txd, *txd_pdma;
++ int err = 0, index = 0, n_desc = 1;
++ struct mtk_tx_buf *tx_buf;
++
++ if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
++ return -EBUSY;
++
++ if (unlikely(atomic_read(&ring->free_count) <= 1))
++ return -EBUSY;
++
++ spin_lock(&eth->page_lock);
++
++ txd = ring->next_free;
++ if (txd == ring->last_free) {
++ err = -ENOMEM;
++ goto out;
++ }
++
++ tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size);
++ memset(tx_buf, 0, sizeof(*tx_buf));
++
++ if (dma_map) { /* ndo_xdp_xmit */
++ txd_info.addr = dma_map_single(eth->dma_dev, xdpf->data,
++ txd_info.size, DMA_TO_DEVICE);
++ if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) {
++ err = -ENOMEM;
++ goto out;
++ }
++ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
++ } else {
++ struct page *page = virt_to_head_page(xdpf->data);
++
++ txd_info.addr = page_pool_get_dma_addr(page) +
++ sizeof(*xdpf) + xdpf->headroom;
++ dma_sync_single_for_device(eth->dma_dev, txd_info.addr,
++ txd_info.size,
++ DMA_BIDIRECTIONAL);
++ }
++ mtk_tx_set_dma_desc(dev, txd, &txd_info);
++
++ tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1;
++
++ txd_pdma = qdma_to_pdma(ring, txd);
++ setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, txd_info.size,
++ index++);
++
++ /* store xdpf for cleanup */
++ tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX;
++ tx_buf->data = xdpf;
++
++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
++ if (index & 1)
++ txd_pdma->txd2 |= TX_DMA_LS0;
++ else
++ txd_pdma->txd2 |= TX_DMA_LS1;
++ }
++
++ ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
++ atomic_sub(n_desc, &ring->free_count);
++
++ /* make sure that all changes to the dma ring are flushed before we
++ * continue
++ */
++ wmb();
++
++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
++ mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr);
++ } else {
++ int idx;
++
++ idx = txd_to_idx(ring, txd, soc->txrx.txd_size);
++ mtk_w32(eth, NEXT_DESP_IDX(idx, ring->dma_size),
++ MT7628_TX_CTX_IDX0);
++ }
++out:
++ spin_unlock(&eth->page_lock);
++
++ return err;
++}
++
++static int mtk_xdp_xmit(struct net_device *dev, int num_frame,
++ struct xdp_frame **frames, u32 flags)
++{
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_hw_stats *hw_stats = mac->hw_stats;
++ struct mtk_eth *eth = mac->hw;
++ int i, nxmit = 0;
++
++ if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
++ return -EINVAL;
++
++ for (i = 0; i < num_frame; i++) {
++ if (mtk_xdp_submit_frame(eth, frames[i], dev, true))
++ break;
++ nxmit++;
++ }
++
++ u64_stats_update_begin(&hw_stats->syncp);
++ hw_stats->xdp_stats.tx_xdp_xmit += nxmit;
++ hw_stats->xdp_stats.tx_xdp_xmit_errors += num_frame - nxmit;
++ u64_stats_update_end(&hw_stats->syncp);
++
++ return nxmit;
++}
++
+ static u32 mtk_xdp_run(struct mtk_eth *eth, struct mtk_rx_ring *ring,
+ struct xdp_buff *xdp, struct net_device *dev)
+ {
+@@ -1495,6 +1624,18 @@ static u32 mtk_xdp_run(struct mtk_eth *e
+
+ count = &hw_stats->xdp_stats.rx_xdp_redirect;
+ goto update_stats;
++ case XDP_TX: {
++ struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp);
++
++ if (mtk_xdp_submit_frame(eth, xdpf, dev, false)) {
++ count = &hw_stats->xdp_stats.rx_xdp_tx_errors;
++ act = XDP_DROP;
++ break;
++ }
++
++ count = &hw_stats->xdp_stats.rx_xdp_tx;
++ goto update_stats;
++ }
+ default:
+ bpf_warn_invalid_xdp_action(act);
+ fallthrough;
+@@ -1728,9 +1869,8 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ {
+ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+- struct mtk_tx_dma *desc;
+- struct sk_buff *skb;
+ struct mtk_tx_buf *tx_buf;
++ struct mtk_tx_dma *desc;
+ u32 cpu, dma;
+
+ cpu = ring->last_free_ptr;
+@@ -1751,15 +1891,21 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ if (tx_buf->flags & MTK_TX_FLAGS_FPORT1)
+ mac = 1;
+
+- skb = tx_buf->skb;
+- if (!skb)
++ if (!tx_buf->data)
+ break;
+
+- if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) {
++ if (tx_buf->type == MTK_TYPE_SKB &&
++ tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
++ struct sk_buff *skb = tx_buf->data;
++
+ bytes[mac] += skb->len;
+ done[mac]++;
+ budget--;
++ } else if (tx_buf->type == MTK_TYPE_XDP_TX ||
++ tx_buf->type == MTK_TYPE_XDP_NDO) {
++ budget--;
+ }
++
+ mtk_tx_unmap(eth, tx_buf, true);
+
+ ring->last_free = desc;
+@@ -1778,9 +1924,8 @@ static int mtk_poll_tx_pdma(struct mtk_e
+ unsigned int *done, unsigned int *bytes)
+ {
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+- struct mtk_tx_dma *desc;
+- struct sk_buff *skb;
+ struct mtk_tx_buf *tx_buf;
++ struct mtk_tx_dma *desc;
+ u32 cpu, dma;
+
+ cpu = ring->cpu_idx;
+@@ -1788,14 +1933,18 @@ static int mtk_poll_tx_pdma(struct mtk_e
+
+ while ((cpu != dma) && budget) {
+ tx_buf = &ring->buf[cpu];
+- skb = tx_buf->skb;
+- if (!skb)
++ if (!tx_buf->data)
+ break;
+
+- if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) {
++ if (tx_buf->type == MTK_TYPE_SKB &&
++ tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
++ struct sk_buff *skb = tx_buf->data;
+ bytes[0] += skb->len;
+ done[0]++;
+ budget--;
++ } else if (tx_buf->type == MTK_TYPE_XDP_TX ||
++ tx_buf->type == MTK_TYPE_XDP_NDO) {
++ budget--;
+ }
+
+ mtk_tx_unmap(eth, tx_buf, true);
+@@ -3478,6 +3627,7 @@ static const struct net_device_ops mtk_n
+ #endif
+ .ndo_setup_tc = mtk_eth_setup_tc,
+ .ndo_bpf = mtk_xdp,
++ .ndo_xdp_xmit = mtk_xdp_xmit,
+ };
+
+ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -694,6 +694,12 @@ enum mtk_dev_state {
+ MTK_RESETTING
+ };
+
++enum mtk_tx_buf_type {
++ MTK_TYPE_SKB,
++ MTK_TYPE_XDP_TX,
++ MTK_TYPE_XDP_NDO,
++};
++
+ /* struct mtk_tx_buf - This struct holds the pointers to the memory pointed at
+ * by the TX descriptor s
+ * @skb: The SKB pointer of the packet being sent
+@@ -703,7 +709,9 @@ enum mtk_dev_state {
+ * @dma_len1: The length of the second segment
+ */
+ struct mtk_tx_buf {
+- struct sk_buff *skb;
++ enum mtk_tx_buf_type type;
++ void *data;
++
+ u32 flags;
+ DEFINE_DMA_UNMAP_ADDR(dma_addr0);
+ DEFINE_DMA_UNMAP_LEN(dma_len0);
diff --git a/target/linux/generic/backport-6.1/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch b/target/linux/generic/backport-6.1/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch
new file mode 100644
index 0000000000..e93e0df544
--- /dev/null
+++ b/target/linux/generic/backport-6.1/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch
@@ -0,0 +1,95 @@
+From 84b9cd389036d4a262d8cee794d56c04095358a7 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 22 Jul 2022 09:19:40 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for
+ page_pool_get_stats
+
+Introduce support for the page_pool stats API into mtk_eth_soc driver.
+Report page_pool stats through ethtool.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/Kconfig | 1 +
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 37 +++++++++++++++++++--
+ 2 files changed, 35 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/Kconfig
++++ b/drivers/net/ethernet/mediatek/Kconfig
+@@ -17,6 +17,7 @@ config NET_MEDIATEK_SOC
+ select PHYLINK
+ select DIMLIB
+ select PAGE_POOL
++ select PAGE_POOL_STATS
+ help
+ This driver supports the gigabit ethernet MACs in the
+ MediaTek SoC family.
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3488,11 +3488,18 @@ static void mtk_get_strings(struct net_d
+ int i;
+
+ switch (stringset) {
+- case ETH_SS_STATS:
++ case ETH_SS_STATS: {
++ struct mtk_mac *mac = netdev_priv(dev);
++
+ for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) {
+ memcpy(data, mtk_ethtool_stats[i].str, ETH_GSTRING_LEN);
+ data += ETH_GSTRING_LEN;
+ }
++ if (mtk_page_pool_enabled(mac->hw))
++ page_pool_ethtool_stats_get_strings(data);
++ break;
++ }
++ default:
+ break;
+ }
+ }
+@@ -3500,13 +3507,35 @@ static void mtk_get_strings(struct net_d
+ static int mtk_get_sset_count(struct net_device *dev, int sset)
+ {
+ switch (sset) {
+- case ETH_SS_STATS:
+- return ARRAY_SIZE(mtk_ethtool_stats);
++ case ETH_SS_STATS: {
++ int count = ARRAY_SIZE(mtk_ethtool_stats);
++ struct mtk_mac *mac = netdev_priv(dev);
++
++ if (mtk_page_pool_enabled(mac->hw))
++ count += page_pool_ethtool_stats_get_count();
++ return count;
++ }
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+
++static void mtk_ethtool_pp_stats(struct mtk_eth *eth, u64 *data)
++{
++ struct page_pool_stats stats = {};
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(eth->rx_ring); i++) {
++ struct mtk_rx_ring *ring = &eth->rx_ring[i];
++
++ if (!ring->page_pool)
++ continue;
++
++ page_pool_get_stats(ring->page_pool, &stats);
++ }
++ page_pool_ethtool_stats_get(data, &stats);
++}
++
+ static void mtk_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+ {
+@@ -3534,6 +3563,8 @@ static void mtk_get_ethtool_stats(struct
+
+ for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++)
+ *data_dst++ = *(data_src + mtk_ethtool_stats[i].offset);
++ if (mtk_page_pool_enabled(mac->hw))
++ mtk_ethtool_pp_stats(mac->hw, data_dst);
+ } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
+ }
+
diff --git a/target/linux/generic/backport-6.1/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch b/target/linux/generic/backport-6.1/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch
new file mode 100644
index 0000000000..8e6895fe97
--- /dev/null
+++ b/target/linux/generic/backport-6.1/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch
@@ -0,0 +1,113 @@
+From b16fe6d82b71fa0dd5c957bc22d66a694976d6eb Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Wed, 27 Jul 2022 23:20:50 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce mtk_xdp_frame_map
+ utility routine
+
+This is a preliminary patch to add xdp multi-frag support to mtk_eth_soc
+driver
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 68 +++++++++++++--------
+ 1 file changed, 42 insertions(+), 26 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1480,6 +1480,41 @@ static void mtk_rx_put_buff(struct mtk_r
+ skb_free_frag(data);
+ }
+
++static int mtk_xdp_frame_map(struct mtk_eth *eth, struct net_device *dev,
++ struct mtk_tx_dma_desc_info *txd_info,
++ struct mtk_tx_dma *txd, struct mtk_tx_buf *tx_buf,
++ void *data, u16 headroom, int index, bool dma_map)
++{
++ struct mtk_tx_ring *ring = &eth->tx_ring;
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_tx_dma *txd_pdma;
++
++ if (dma_map) { /* ndo_xdp_xmit */
++ txd_info->addr = dma_map_single(eth->dma_dev, data,
++ txd_info->size, DMA_TO_DEVICE);
++ if (unlikely(dma_mapping_error(eth->dma_dev, txd_info->addr)))
++ return -ENOMEM;
++
++ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
++ } else {
++ struct page *page = virt_to_head_page(data);
++
++ txd_info->addr = page_pool_get_dma_addr(page) +
++ sizeof(struct xdp_frame) + headroom;
++ dma_sync_single_for_device(eth->dma_dev, txd_info->addr,
++ txd_info->size, DMA_BIDIRECTIONAL);
++ }
++ mtk_tx_set_dma_desc(dev, txd, txd_info);
++
++ tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1;
++
++ txd_pdma = qdma_to_pdma(ring, txd);
++ setup_tx_buf(eth, tx_buf, txd_pdma, txd_info->addr, txd_info->size,
++ index);
++
++ return 0;
++}
++
+ static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf,
+ struct net_device *dev, bool dma_map)
+ {
+@@ -1490,9 +1525,8 @@ static int mtk_xdp_submit_frame(struct m
+ .first = true,
+ .last = true,
+ };
+- struct mtk_mac *mac = netdev_priv(dev);
+- struct mtk_tx_dma *txd, *txd_pdma;
+ int err = 0, index = 0, n_desc = 1;
++ struct mtk_tx_dma *txd, *txd_pdma;
+ struct mtk_tx_buf *tx_buf;
+
+ if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
+@@ -1512,36 +1546,18 @@ static int mtk_xdp_submit_frame(struct m
+ tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size);
+ memset(tx_buf, 0, sizeof(*tx_buf));
+
+- if (dma_map) { /* ndo_xdp_xmit */
+- txd_info.addr = dma_map_single(eth->dma_dev, xdpf->data,
+- txd_info.size, DMA_TO_DEVICE);
+- if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) {
+- err = -ENOMEM;
+- goto out;
+- }
+- tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
+- } else {
+- struct page *page = virt_to_head_page(xdpf->data);
+-
+- txd_info.addr = page_pool_get_dma_addr(page) +
+- sizeof(*xdpf) + xdpf->headroom;
+- dma_sync_single_for_device(eth->dma_dev, txd_info.addr,
+- txd_info.size,
+- DMA_BIDIRECTIONAL);
+- }
+- mtk_tx_set_dma_desc(dev, txd, &txd_info);
+-
+- tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1;
+-
+- txd_pdma = qdma_to_pdma(ring, txd);
+- setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, txd_info.size,
+- index++);
++ err = mtk_xdp_frame_map(eth, dev, &txd_info, txd, tx_buf,
++ xdpf->data, xdpf->headroom, index,
++ dma_map);
++ if (err < 0)
++ goto out;
+
+ /* store xdpf for cleanup */
+ tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX;
+ tx_buf->data = xdpf;
+
+ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
++ txd_pdma = qdma_to_pdma(ring, txd);
+ if (index & 1)
+ txd_pdma->txd2 |= TX_DMA_LS0;
+ else
diff --git a/target/linux/generic/backport-6.1/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch b/target/linux/generic/backport-6.1/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch
new file mode 100644
index 0000000000..23e4a4dfcb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch
@@ -0,0 +1,218 @@
+From 155738a4f319538a09f734ce1f5a2eac3ada1de2 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Wed, 27 Jul 2022 23:20:51 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce xdp multi-frag support
+
+Add the capability to map non-linear xdp frames in XDP_TX and
+ndo_xdp_xmit callback.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 125 +++++++++++++-------
+ 1 file changed, 82 insertions(+), 43 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -988,23 +988,22 @@ static void mtk_tx_unmap(struct mtk_eth
+ }
+ }
+
+- if (tx_buf->type == MTK_TYPE_SKB) {
+- if (tx_buf->data &&
+- tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
++ if (tx_buf->data && tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
++ if (tx_buf->type == MTK_TYPE_SKB) {
+ struct sk_buff *skb = tx_buf->data;
+
+ if (napi)
+ napi_consume_skb(skb, napi);
+ else
+ dev_kfree_skb_any(skb);
+- }
+- } else if (tx_buf->data) {
+- struct xdp_frame *xdpf = tx_buf->data;
++ } else {
++ struct xdp_frame *xdpf = tx_buf->data;
+
+- if (napi && tx_buf->type == MTK_TYPE_XDP_TX)
+- xdp_return_frame_rx_napi(xdpf);
+- else
+- xdp_return_frame(xdpf);
++ if (napi && tx_buf->type == MTK_TYPE_XDP_TX)
++ xdp_return_frame_rx_napi(xdpf);
++ else
++ xdp_return_frame(xdpf);
++ }
+ }
+ tx_buf->flags = 0;
+ tx_buf->data = NULL;
+@@ -1507,6 +1506,8 @@ static int mtk_xdp_frame_map(struct mtk_
+ mtk_tx_set_dma_desc(dev, txd, txd_info);
+
+ tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1;
++ tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX;
++ tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
+
+ txd_pdma = qdma_to_pdma(ring, txd);
+ setup_tx_buf(eth, tx_buf, txd_pdma, txd_info->addr, txd_info->size,
+@@ -1518,43 +1519,69 @@ static int mtk_xdp_frame_map(struct mtk_
+ static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf,
+ struct net_device *dev, bool dma_map)
+ {
++ 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_tx_dma_desc_info txd_info = {
+ .size = xdpf->len,
+ .first = true,
+- .last = true,
++ .last = !xdp_frame_has_frags(xdpf),
+ };
+- int err = 0, index = 0, n_desc = 1;
+- struct mtk_tx_dma *txd, *txd_pdma;
+- struct mtk_tx_buf *tx_buf;
++ int err, index = 0, n_desc = 1, nr_frags;
++ struct mtk_tx_dma *htxd, *txd, *txd_pdma;
++ struct mtk_tx_buf *htx_buf, *tx_buf;
++ void *data = xdpf->data;
+
+ if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
+ return -EBUSY;
+
+- if (unlikely(atomic_read(&ring->free_count) <= 1))
++ nr_frags = unlikely(xdp_frame_has_frags(xdpf)) ? sinfo->nr_frags : 0;
++ if (unlikely(atomic_read(&ring->free_count) <= 1 + nr_frags))
+ return -EBUSY;
+
+ spin_lock(&eth->page_lock);
+
+ txd = ring->next_free;
+ if (txd == ring->last_free) {
+- err = -ENOMEM;
+- goto out;
++ spin_unlock(&eth->page_lock);
++ return -ENOMEM;
+ }
++ htxd = txd;
+
+ tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size);
+ memset(tx_buf, 0, sizeof(*tx_buf));
++ htx_buf = tx_buf;
+
+- err = mtk_xdp_frame_map(eth, dev, &txd_info, txd, tx_buf,
+- xdpf->data, xdpf->headroom, index,
+- dma_map);
+- if (err < 0)
+- goto out;
++ for (;;) {
++ err = mtk_xdp_frame_map(eth, dev, &txd_info, txd, tx_buf,
++ data, xdpf->headroom, index, dma_map);
++ if (err < 0)
++ goto unmap;
++
++ if (txd_info.last)
++ break;
+
++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) || (index & 0x1)) {
++ txd = mtk_qdma_phys_to_virt(ring, txd->txd2);
++ txd_pdma = qdma_to_pdma(ring, txd);
++ if (txd == ring->last_free)
++ goto unmap;
++
++ tx_buf = mtk_desc_to_tx_buf(ring, txd,
++ soc->txrx.txd_size);
++ memset(tx_buf, 0, sizeof(*tx_buf));
++ n_desc++;
++ }
++
++ 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;
++ data = skb_frag_address(&sinfo->frags[index]);
++
++ index++;
++ }
+ /* store xdpf for cleanup */
+- tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX;
+- tx_buf->data = xdpf;
++ htx_buf->data = xdpf;
+
+ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) {
+ txd_pdma = qdma_to_pdma(ring, txd);
+@@ -1581,7 +1608,24 @@ static int mtk_xdp_submit_frame(struct m
+ mtk_w32(eth, NEXT_DESP_IDX(idx, ring->dma_size),
+ MT7628_TX_CTX_IDX0);
+ }
+-out:
++
++ spin_unlock(&eth->page_lock);
++
++ return 0;
++
++unmap:
++ while (htxd != txd) {
++ txd_pdma = qdma_to_pdma(ring, htxd);
++ tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->txrx.txd_size);
++ mtk_tx_unmap(eth, tx_buf, false);
++
++ htxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA))
++ txd_pdma->txd2 = TX_DMA_DESP2_DEF;
++
++ htxd = mtk_qdma_phys_to_virt(ring, htxd->txd2);
++ }
++
+ spin_unlock(&eth->page_lock);
+
+ return err;
+@@ -1910,18 +1954,15 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ if (!tx_buf->data)
+ break;
+
+- if (tx_buf->type == MTK_TYPE_SKB &&
+- tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
+- struct sk_buff *skb = tx_buf->data;
++ if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
++ if (tx_buf->type == MTK_TYPE_SKB) {
++ struct sk_buff *skb = tx_buf->data;
+
+- bytes[mac] += skb->len;
+- done[mac]++;
+- budget--;
+- } else if (tx_buf->type == MTK_TYPE_XDP_TX ||
+- tx_buf->type == MTK_TYPE_XDP_NDO) {
++ bytes[mac] += skb->len;
++ done[mac]++;
++ }
+ budget--;
+ }
+-
+ mtk_tx_unmap(eth, tx_buf, true);
+
+ ring->last_free = desc;
+@@ -1952,17 +1993,15 @@ static int mtk_poll_tx_pdma(struct mtk_e
+ if (!tx_buf->data)
+ break;
+
+- if (tx_buf->type == MTK_TYPE_SKB &&
+- tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) {
+- struct sk_buff *skb = tx_buf->data;
+- bytes[0] += skb->len;
+- done[0]++;
+- budget--;
+- } else if (tx_buf->type == MTK_TYPE_XDP_TX ||
+- tx_buf->type == MTK_TYPE_XDP_NDO) {
++ 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]++;
++ }
+ budget--;
+ }
+-
+ mtk_tx_unmap(eth, tx_buf, true);
+
+ desc = ring->dma + cpu * eth->soc->txrx.txd_size;
diff --git a/target/linux/generic/backport-6.1/707-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch b/target/linux/generic/backport-6.1/707-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch
new file mode 100644
index 0000000000..9743a3f206
--- /dev/null
+++ b/target/linux/generic/backport-6.1/707-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch
@@ -0,0 +1,394 @@
+From 4765a9722e09765866e131ec31f7b9cf4c1f4854 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 19 Mar 2023 12:57:50 +0000
+Subject: [PATCH] net: pcs: add driver for MediaTek SGMII PCS
+
+The SGMII core found in several MediaTek SoCs is identical to what can
+also be found in MediaTek's MT7531 Ethernet switch IC.
+As this has not always been clear, both drivers developed different
+implementations to deal with the PCS.
+Recently Alexander Couzens pointed out this fact which lead to the
+development of this shared driver.
+
+Add a dedicated driver, mostly by copying the code now found in the
+Ethernet driver. The now redundant code will be removed by a follow-up
+commit.
+
+Suggested-by: Alexander Couzens <lynxis@fe80.eu>
+Suggested-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ MAINTAINERS | 8 +
+ drivers/net/pcs/Kconfig | 7 +
+ drivers/net/pcs/Makefile | 1 +
+ drivers/net/pcs/pcs-mtk-lynxi.c | 305 ++++++++++++++++++++++++++++++
+ include/linux/pcs/pcs-mtk-lynxi.h | 13 ++
+ 5 files changed, 334 insertions(+)
+ create mode 100644 drivers/net/pcs/pcs-mtk-lynxi.c
+ create mode 100644 include/linux/pcs/pcs-mtk-lynxi.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -11789,6 +11789,14 @@ L: netdev@vger.kernel.org
+ S: Maintained
+ F: drivers/net/ethernet/mediatek/
+
++MEDIATEK ETHERNET PCS DRIVER
++M: Alexander Couzens <lynxis@fe80.eu>
++M: Daniel Golle <daniel@makrotopia.org>
++L: netdev@vger.kernel.org
++S: Maintained
++F: drivers/net/pcs/pcs-mtk-lynxi.c
++F: include/linux/pcs/pcs-mtk-lynxi.h
++
+ MEDIATEK I2C CONTROLLER DRIVER
+ M: Qii Wang <qii.wang@mediatek.com>
+ L: linux-i2c@vger.kernel.org
+--- a/drivers/net/pcs/Kconfig
++++ b/drivers/net/pcs/Kconfig
+@@ -18,4 +18,11 @@ config PCS_LYNX
+ This module provides helpers to phylink for managing the Lynx PCS
+ which is part of the Layerscape and QorIQ Ethernet SERDES.
+
++config PCS_MTK_LYNXI
++ tristate
++ select REGMAP
++ help
++ This module provides helpers to phylink for managing the LynxI PCS
++ which is part of MediaTek's SoC and Ethernet switch ICs.
++
+ endmenu
+--- a/drivers/net/pcs/Makefile
++++ b/drivers/net/pcs/Makefile
+@@ -5,3 +5,4 @@ pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.
+
+ obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
+ obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
++obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o
+--- /dev/null
++++ b/drivers/net/pcs/pcs-mtk-lynxi.c
+@@ -0,0 +1,305 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2018-2019 MediaTek Inc.
++/* A library for MediaTek SGMII circuit
++ *
++ * Author: Sean Wang <sean.wang@mediatek.com>
++ * Author: Alexander Couzens <lynxis@fe80.eu>
++ * Author: Daniel Golle <daniel@makrotopia.org>
++ *
++ */
++
++#include <linux/mdio.h>
++#include <linux/of.h>
++#include <linux/pcs/pcs-mtk-lynxi.h>
++#include <linux/phylink.h>
++#include <linux/regmap.h>
++
++/* SGMII subsystem config registers */
++/* BMCR (low 16) BMSR (high 16) */
++#define SGMSYS_PCS_CONTROL_1 0x0
++#define SGMII_BMCR GENMASK(15, 0)
++#define SGMII_BMSR GENMASK(31, 16)
++
++#define SGMSYS_PCS_DEVICE_ID 0x4
++#define SGMII_LYNXI_DEV_ID 0x4d544950
++
++#define SGMSYS_PCS_ADVERTISE 0x8
++#define SGMII_ADVERTISE GENMASK(15, 0)
++#define SGMII_LPA GENMASK(31, 16)
++
++#define SGMSYS_PCS_SCRATCH 0x14
++#define SGMII_DEV_VERSION GENMASK(31, 16)
++
++/* Register to programmable link timer, the unit in 2 * 8ns */
++#define SGMSYS_PCS_LINK_TIMER 0x18
++#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
++#define SGMII_LINK_TIMER_VAL(ns) FIELD_PREP(SGMII_LINK_TIMER_MASK, \
++ ((ns) / 2 / 8))
++
++/* Register to control remote fault */
++#define SGMSYS_SGMII_MODE 0x20
++#define SGMII_IF_MODE_SGMII BIT(0)
++#define SGMII_SPEED_DUPLEX_AN BIT(1)
++#define SGMII_SPEED_MASK GENMASK(3, 2)
++#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
++#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
++#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
++#define SGMII_DUPLEX_HALF BIT(4)
++#define SGMII_REMOTE_FAULT_DIS BIT(8)
++
++/* Register to reset SGMII design */
++#define SGMSYS_RESERVED_0 0x34
++#define SGMII_SW_RESET BIT(0)
++
++/* Register to set SGMII speed, ANA RG_ Control Signals III */
++#define SGMII_PHY_SPEED_MASK GENMASK(3, 2)
++#define SGMII_PHY_SPEED_1_25G FIELD_PREP(SGMII_PHY_SPEED_MASK, 0)
++#define SGMII_PHY_SPEED_3_125G FIELD_PREP(SGMII_PHY_SPEED_MASK, 1)
++
++/* Register to power up QPHY */
++#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
++#define SGMII_PHYA_PWD BIT(4)
++
++/* Register to QPHY wrapper control */
++#define SGMSYS_QPHY_WRAP_CTRL 0xec
++#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
++#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
++
++/* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated
++ * data
++ * @regmap: The register map pointing at the range used to setup
++ * SGMII modes
++ * @dev: Pointer to device owning the PCS
++ * @ana_rgc3: The offset of register ANA_RGC3 relative to regmap
++ * @interface: Currently configured interface mode
++ * @pcs: Phylink PCS structure
++ * @flags: Flags indicating hardware properties
++ */
++struct mtk_pcs_lynxi {
++ struct regmap *regmap;
++ u32 ana_rgc3;
++ phy_interface_t interface;
++ struct phylink_pcs pcs;
++ u32 flags;
++};
++
++static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
++{
++ return container_of(pcs, struct mtk_pcs_lynxi, pcs);
++}
++
++static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
++ struct phylink_link_state *state)
++{
++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
++ unsigned int bm, adv;
++
++ /* Read the BMSR and LPA */
++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
++
++ phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
++ FIELD_GET(SGMII_LPA, adv));
++}
++
++static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface,
++ const unsigned long *advertising,
++ bool permit_pause_to_mac)
++{
++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
++ bool mode_changed = false, changed, use_an;
++ unsigned int rgc3, sgm_mode, bmcr;
++ int advertise, link_timer;
++
++ advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
++ advertising);
++ if (advertise < 0)
++ return advertise;
++
++ /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
++ * we assume that fixes it's speed at bitrate = line rate (in
++ * other words, 1000Mbps or 2500Mbps).
++ */
++ if (interface == PHY_INTERFACE_MODE_SGMII) {
++ sgm_mode = SGMII_IF_MODE_SGMII;
++ if (phylink_autoneg_inband(mode)) {
++ sgm_mode |= SGMII_REMOTE_FAULT_DIS |
++ SGMII_SPEED_DUPLEX_AN;
++ use_an = true;
++ } else {
++ use_an = false;
++ }
++ } else if (phylink_autoneg_inband(mode)) {
++ /* 1000base-X or 2500base-X autoneg */
++ sgm_mode = SGMII_REMOTE_FAULT_DIS;
++ use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
++ advertising);
++ } else {
++ /* 1000base-X or 2500base-X without autoneg */
++ sgm_mode = 0;
++ use_an = false;
++ }
++
++ if (use_an)
++ bmcr = BMCR_ANENABLE;
++ else
++ bmcr = 0;
++
++ if (mpcs->interface != interface) {
++ link_timer = phylink_get_link_timer_ns(interface);
++ if (link_timer < 0)
++ return link_timer;
++
++ /* PHYA power down */
++ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
++ SGMII_PHYA_PWD);
++
++ /* Reset SGMII PCS state */
++ regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
++ SGMII_SW_RESET);
++
++ if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
++ SGMII_PN_SWAP_MASK,
++ SGMII_PN_SWAP_TX_RX);
++
++ if (interface == PHY_INTERFACE_MODE_2500BASEX)
++ rgc3 = SGMII_PHY_SPEED_3_125G;
++ else
++ rgc3 = SGMII_PHY_SPEED_1_25G;
++
++ /* Configure the underlying interface speed */
++ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
++ SGMII_PHY_SPEED_MASK, rgc3);
++
++ /* Setup the link timer */
++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
++ SGMII_LINK_TIMER_VAL(link_timer));
++
++ mpcs->interface = interface;
++ mode_changed = true;
++ }
++
++ /* Update the advertisement, noting whether it has changed */
++ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
++ SGMII_ADVERTISE, advertise, &changed);
++
++ /* Update the sgmsys mode register */
++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
++ SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
++ SGMII_IF_MODE_SGMII, sgm_mode);
++
++ /* Update the BMCR */
++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
++ BMCR_ANENABLE, bmcr);
++
++ /* Release PHYA power down state
++ * Only removing bit SGMII_PHYA_PWD isn't enough.
++ * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
++ * prevents SGMII from working. The SGMII still shows link but no traffic
++ * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
++ * taken from a good working state of the SGMII interface.
++ * Unknown how much the QPHY needs but it is racy without a sleep.
++ * Tested on mt7622 & mt7986.
++ */
++ usleep_range(50, 100);
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
++
++ return changed || mode_changed;
++}
++
++static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
++{
++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
++
++ regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
++}
++
++static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode,
++ phy_interface_t interface, int speed,
++ int duplex)
++{
++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
++ unsigned int sgm_mode;
++
++ if (!phylink_autoneg_inband(mode)) {
++ /* Force the speed and duplex setting */
++ if (speed == SPEED_10)
++ sgm_mode = SGMII_SPEED_10;
++ else if (speed == SPEED_100)
++ sgm_mode = SGMII_SPEED_100;
++ else
++ sgm_mode = SGMII_SPEED_1000;
++
++ if (duplex != DUPLEX_FULL)
++ sgm_mode |= SGMII_DUPLEX_HALF;
++
++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
++ SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
++ sgm_mode);
++ }
++}
++
++static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = {
++ .pcs_get_state = mtk_pcs_lynxi_get_state,
++ .pcs_config = mtk_pcs_lynxi_config,
++ .pcs_an_restart = mtk_pcs_lynxi_restart_an,
++ .pcs_link_up = mtk_pcs_lynxi_link_up,
++};
++
++struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
++ struct regmap *regmap, u32 ana_rgc3,
++ u32 flags)
++{
++ struct mtk_pcs_lynxi *mpcs;
++ u32 id, ver;
++ int ret;
++
++ ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
++ if (ret < 0)
++ return NULL;
++
++ if (id != SGMII_LYNXI_DEV_ID) {
++ dev_err(dev, "unknown PCS device id %08x\n", id);
++ return NULL;
++ }
++
++ ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
++ if (ret < 0)
++ return NULL;
++
++ ver = FIELD_GET(SGMII_DEV_VERSION, ver);
++ if (ver != 0x1) {
++ dev_err(dev, "unknown PCS device version %04x\n", ver);
++ return NULL;
++ }
++
++ dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
++ ver);
++
++ mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
++ if (!mpcs)
++ return NULL;
++
++ mpcs->ana_rgc3 = ana_rgc3;
++ mpcs->regmap = regmap;
++ mpcs->flags = flags;
++ mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
++ mpcs->pcs.poll = true;
++ mpcs->interface = PHY_INTERFACE_MODE_NA;
++
++ return &mpcs->pcs;
++}
++EXPORT_SYMBOL(mtk_pcs_lynxi_create);
++
++void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs)
++{
++ if (!pcs)
++ return;
++
++ kfree(pcs_to_mtk_pcs_lynxi(pcs));
++}
++EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
++
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/include/linux/pcs/pcs-mtk-lynxi.h
+@@ -0,0 +1,13 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef __LINUX_PCS_MTK_LYNXI_H
++#define __LINUX_PCS_MTK_LYNXI_H
++
++#include <linux/phylink.h>
++#include <linux/regmap.h>
++
++#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
++struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
++ struct regmap *regmap,
++ u32 ana_rgc3, u32 flags);
++void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
++#endif
diff --git a/target/linux/generic/backport-6.1/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch b/target/linux/generic/backport-6.1/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch
new file mode 100644
index 0000000000..817b3e10fd
--- /dev/null
+++ b/target/linux/generic/backport-6.1/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch
@@ -0,0 +1,74 @@
+From 0cf731f9ebb5bf6f252055bebf4463a5c0bd490b Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Tue, 23 Aug 2022 14:24:07 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix hw hash reporting for
+ MTK_NETSYS_V2
+
+Properly report hw rx hash for mt7986 chipset accroding to the new dma
+descriptor layout.
+
+Fixes: 197c9e9b17b11 ("net: ethernet: mtk_eth_soc: introduce support for mt7986 chipset")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/091394ea4e705fbb35f828011d98d0ba33808f69.1661257293.git.lorenzo@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 22 +++++++++++----------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 5 +++++
+ 2 files changed, 17 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1846,10 +1846,19 @@ static int mtk_poll_rx(struct napi_struc
+ skb->dev = netdev;
+ bytes += skb->len;
+
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY;
++ if (hash != MTK_RXD5_FOE_ENTRY)
++ skb_set_hash(skb, jhash_1word(hash, 0),
++ PKT_HASH_TYPE_L4);
+ rxdcsum = &trxd.rxd3;
+- else
++ } else {
++ hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY;
++ if (hash != MTK_RXD4_FOE_ENTRY)
++ skb_set_hash(skb, jhash_1word(hash, 0),
++ PKT_HASH_TYPE_L4);
+ rxdcsum = &trxd.rxd4;
++ }
+
+ if (*rxdcsum & eth->soc->txrx.rx_dma_l4_valid)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+@@ -1857,16 +1866,9 @@ static int mtk_poll_rx(struct napi_struc
+ skb_checksum_none_assert(skb);
+ skb->protocol = eth_type_trans(skb, netdev);
+
+- hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY;
+- if (hash != MTK_RXD4_FOE_ENTRY) {
+- hash = jhash_1word(hash, 0);
+- skb_set_hash(skb, hash, PKT_HASH_TYPE_L4);
+- }
+-
+ reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4);
+ if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
+- mtk_ppe_check_skb(eth->ppe, skb,
+- trxd.rxd4 & MTK_RXD4_FOE_ENTRY);
++ mtk_ppe_check_skb(eth->ppe, skb, hash);
+
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -314,6 +314,11 @@
+ #define RX_DMA_L4_VALID_PDMA BIT(30) /* when PDMA is used */
+ #define RX_DMA_SPECIAL_TAG BIT(22)
+
++/* PDMA descriptor rxd5 */
++#define MTK_RXD5_FOE_ENTRY GENMASK(14, 0)
++#define MTK_RXD5_PPE_CPU_REASON GENMASK(22, 18)
++#define MTK_RXD5_SRC_PORT GENMASK(29, 26)
++
+ #define RX_DMA_GET_SPORT(x) (((x) >> 19) & 0xf)
+ #define RX_DMA_GET_SPORT_V2(x) (((x) >> 26) & 0x7)
+
diff --git a/target/linux/generic/backport-6.1/711-v6.0-01-net-ethernet-mtk_eth_soc-fix-off-by-one-check-of-ARR.patch b/target/linux/generic/backport-6.1/711-v6.0-01-net-ethernet-mtk_eth_soc-fix-off-by-one-check-of-ARR.patch
new file mode 100644
index 0000000000..0de8ab4376
--- /dev/null
+++ b/target/linux/generic/backport-6.1/711-v6.0-01-net-ethernet-mtk_eth_soc-fix-off-by-one-check-of-ARR.patch
@@ -0,0 +1,31 @@
+From: Tom Rix <trix@redhat.com>
+Date: Sat, 16 Jul 2022 17:46:54 -0400
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix off by one check of
+ ARRAY_SIZE
+
+In mtk_wed_tx_ring_setup(.., int idx, ..), idx is used as an index here
+ struct mtk_wed_ring *ring = &dev->tx_ring[idx];
+
+The bounds of idx are checked here
+ BUG_ON(idx > ARRAY_SIZE(dev->tx_ring));
+
+If idx is the size of the array, it will pass this check and overflow.
+So change the check to >= .
+
+Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)")
+Signed-off-by: Tom Rix <trix@redhat.com>
+Link: https://lore.kernel.org/r/20220716214654.1540240-1-trix@redhat.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -651,7 +651,7 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
+ * WDMA RX.
+ */
+
+- BUG_ON(idx > ARRAY_SIZE(dev->tx_ring));
++ BUG_ON(idx >= ARRAY_SIZE(dev->tx_ring));
+
+ if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE))
+ return -ENOMEM;
diff --git a/target/linux/generic/backport-6.1/711-v6.0-02-net-ethernet-mtk_ppe-fix-possible-NULL-pointer-deref.patch b/target/linux/generic/backport-6.1/711-v6.0-02-net-ethernet-mtk_ppe-fix-possible-NULL-pointer-deref.patch
new file mode 100644
index 0000000000..fc6e246468
--- /dev/null
+++ b/target/linux/generic/backport-6.1/711-v6.0-02-net-ethernet-mtk_ppe-fix-possible-NULL-pointer-deref.patch
@@ -0,0 +1,27 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Jul 2022 11:51:53 +0200
+Subject: [PATCH] net: ethernet: mtk_ppe: fix possible NULL pointer dereference
+ in mtk_flow_get_wdma_info
+
+odev pointer can be NULL in mtk_flow_offload_replace routine according
+to the flower action rules. Fix possible NULL pointer dereference in
+mtk_flow_get_wdma_info.
+
+Fixes: a333215e10cb5 ("net: ethernet: mtk_eth_soc: implement flow offloading to WED devices")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/4e1685bc4976e21e364055f6bee86261f8f9ee93.1658137753.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -93,6 +93,9 @@ mtk_flow_get_wdma_info(struct net_device
+ };
+ struct net_device_path path = {};
+
++ if (!ctx.dev)
++ return -ENODEV;
++
+ memcpy(ctx.daddr, addr, sizeof(ctx.daddr));
+
+ if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
diff --git a/target/linux/generic/backport-6.1/711-v6.0-03-net-ethernet-mtk-ppe-fix-traffic-offload-with-bridge.patch b/target/linux/generic/backport-6.1/711-v6.0-03-net-ethernet-mtk-ppe-fix-traffic-offload-with-bridge.patch
new file mode 100644
index 0000000000..c0720152d6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/711-v6.0-03-net-ethernet-mtk-ppe-fix-traffic-offload-with-bridge.patch
@@ -0,0 +1,64 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 22 Jul 2022 09:06:19 +0200
+Subject: [PATCH] net: ethernet: mtk-ppe: fix traffic offload with bridged wlan
+
+A typical flow offload scenario for OpenWrt users is routed traffic
+received by the wan interface that is redirected to a wlan device
+belonging to the lan bridge. Current implementation fails to
+fill wdma offload info in mtk_flow_get_wdma_info() since odev device is
+the local bridge. Fix the issue running dev_fill_forward_path routine in
+mtk_flow_get_wdma_info in order to identify the wlan device.
+
+Tested-by: Paolo Valerio <pvalerio@redhat.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -88,32 +88,28 @@ mtk_flow_offload_mangle_eth(const struct
+ static int
+ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)
+ {
+- struct net_device_path_ctx ctx = {
+- .dev = dev,
+- };
+- struct net_device_path path = {};
++ struct net_device_path_stack stack;
++ struct net_device_path *path;
++ int err;
+
+- if (!ctx.dev)
++ if (!dev)
+ return -ENODEV;
+
+- memcpy(ctx.daddr, addr, sizeof(ctx.daddr));
+-
+ if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
+ return -1;
+
+- if (!dev->netdev_ops->ndo_fill_forward_path)
+- return -1;
+-
+- if (dev->netdev_ops->ndo_fill_forward_path(&ctx, &path))
+- return -1;
++ err = dev_fill_forward_path(dev, addr, &stack);
++ if (err)
++ return err;
+
+- if (path.type != DEV_PATH_MTK_WDMA)
++ path = &stack.path[stack.num_paths - 1];
++ if (path->type != DEV_PATH_MTK_WDMA)
+ return -1;
+
+- info->wdma_idx = path.mtk_wdma.wdma_idx;
+- info->queue = path.mtk_wdma.queue;
+- info->bss = path.mtk_wdma.bss;
+- info->wcid = path.mtk_wdma.wcid;
++ info->wdma_idx = path->mtk_wdma.wdma_idx;
++ info->queue = path->mtk_wdma.queue;
++ info->bss = path->mtk_wdma.bss;
++ info->wcid = path->mtk_wdma.wcid;
+
+ return 0;
+ }
diff --git a/target/linux/generic/backport-6.1/711-v6.0-04-net-ethernet-mtk_eth_soc-remove-mtk_foe_entry_timest.patch b/target/linux/generic/backport-6.1/711-v6.0-04-net-ethernet-mtk_eth_soc-remove-mtk_foe_entry_timest.patch
new file mode 100644
index 0000000000..3c28e83551
--- /dev/null
+++ b/target/linux/generic/backport-6.1/711-v6.0-04-net-ethernet-mtk_eth_soc-remove-mtk_foe_entry_timest.patch
@@ -0,0 +1,33 @@
+From c9daab322313087afde8c46f41df3c628410ae20 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 5 Sep 2022 14:46:01 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: remove mtk_foe_entry_timestamp
+
+Get rid of mtk_foe_entry_timestamp routine since it is no longer used.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 11 -----------
+ 1 file changed, 11 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -302,17 +302,6 @@ mtk_ppe_check_skb(struct mtk_ppe *ppe, s
+ __mtk_ppe_check_skb(ppe, skb, hash);
+ }
+
+-static inline int
+-mtk_foe_entry_timestamp(struct mtk_ppe *ppe, u16 hash)
+-{
+- u32 ib1 = READ_ONCE(ppe->foe_table[hash].ib1);
+-
+- if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND)
+- return -1;
+-
+- return FIELD_GET(MTK_FOE_IB1_BIND_TIMESTAMP, ib1);
+-}
+-
+ int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
+ u8 pse_port, u8 *src_mac, u8 *dest_mac);
+ int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port);
diff --git a/target/linux/generic/backport-6.1/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch b/target/linux/generic/backport-6.1/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch
new file mode 100644
index 0000000000..f6fc734079
--- /dev/null
+++ b/target/linux/generic/backport-6.1/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch
@@ -0,0 +1,35 @@
+From 5e69163d3b9931098922b3fc2f8e786af8c1f37e Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Tue, 13 Sep 2022 15:03:05 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: enable XDP support just for
+ MT7986 SoC
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Disable page_pool/XDP support for MT7621 SoC in order fix a regression
+introduce adding XDP for MT7986 SoC. There is no a real use case for XDP
+on MT7621 since it is a low-end cpu. Moreover this patch reduces the
+memory footprint.
+
+Tested-by: Sergio Paracuellos <sergio.paracuellos@gmail.com>
+Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Fixes: 23233e577ef9 ("net: ethernet: mtk_eth_soc: rely on page_pool for single page buffers")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/2bf31e27b888c43228b0d84dd2ef5033338269e2.1663074002.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1413,7 +1413,7 @@ static void mtk_update_rx_cpu_idx(struct
+
+ static bool mtk_page_pool_enabled(struct mtk_eth *eth)
+ {
+- return !eth->hwlro;
++ return MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2);
+ }
+
+ static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth,
diff --git a/target/linux/generic/backport-6.1/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch b/target/linux/generic/backport-6.1/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch
new file mode 100644
index 0000000000..f8bbea6c85
--- /dev/null
+++ b/target/linux/generic/backport-6.1/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch
@@ -0,0 +1,127 @@
+From patchwork Thu Sep 8 19:33:38 2022
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: Lorenzo Bianconi <lorenzo@kernel.org>
+X-Patchwork-Id: 12970556
+X-Patchwork-Delegate: kuba@kernel.org
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+To: netdev@vger.kernel.org
+Cc: nbd@nbd.name, john@phrozen.org, sean.wang@mediatek.com,
+ Mark-MC.Lee@mediatek.com, davem@davemloft.net, edumazet@google.com,
+ kuba@kernel.org, pabeni@redhat.com, matthias.bgg@gmail.com,
+ linux-mediatek@lists.infradead.org, lorenzo.bianconi@redhat.com,
+ Bo.Jiao@mediatek.com, sujuan.chen@mediatek.com,
+ ryder.Lee@mediatek.com, evelyn.tsai@mediatek.com,
+ devicetree@vger.kernel.org, robh@kernel.org
+Subject: [PATCH net-next 03/12] net: ethernet: mtk_eth_soc: move gdma_to_ppe
+ and ppe_base definitions in mtk register map
+Date: Thu, 8 Sep 2022 21:33:37 +0200
+Message-Id:
+ <95938fc9cbe0223714be2658a49ca58e9baace00.1662661555.git.lorenzo@kernel.org>
+X-Mailer: git-send-email 2.37.3
+In-Reply-To: <cover.1662661555.git.lorenzo@kernel.org>
+References: <cover.1662661555.git.lorenzo@kernel.org>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+This is a preliminary patch to introduce mt7986 hw packet engine.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 15 +++++++++++----
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 3 ++-
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 2 --
+ 3 files changed, 13 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -73,6 +73,8 @@ static const struct mtk_reg_map mtk_reg_
+ .fq_blen = 0x1b2c,
+ },
+ .gdm1_cnt = 0x2400,
++ .gdma_to_ppe0 = 0x4444,
++ .ppe_base = 0x0c00,
+ };
+
+ static const struct mtk_reg_map mt7628_reg_map = {
+@@ -126,6 +128,8 @@ static const struct mtk_reg_map mt7986_r
+ .fq_blen = 0x472c,
+ },
+ .gdm1_cnt = 0x1c00,
++ .gdma_to_ppe0 = 0x3333,
++ .ppe_base = 0x2000,
+ };
+
+ /* strings used by ethtool */
+@@ -2925,6 +2929,7 @@ static int mtk_open(struct net_device *d
+
+ /* we run 2 netdevs on the same dma ring so we only bring it up once */
+ if (!refcount_read(&eth->dma_refcnt)) {
++ const struct mtk_soc_data *soc = eth->soc;
+ u32 gdm_config = MTK_GDMA_TO_PDMA;
+ int err;
+
+@@ -2934,15 +2939,15 @@ static int mtk_open(struct net_device *d
+ return err;
+ }
+
+- if (eth->soc->offload_version && mtk_ppe_start(eth->ppe) == 0)
+- gdm_config = MTK_GDMA_TO_PPE;
++ if (soc->offload_version && mtk_ppe_start(eth->ppe) == 0)
++ gdm_config = soc->reg_map->gdma_to_ppe0;
+
+ mtk_gdm_config(eth, gdm_config);
+
+ napi_enable(&eth->tx_napi);
+ napi_enable(&eth->rx_napi);
+ mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+- mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
++ mtk_rx_irq_enable(eth, soc->txrx.rx_irq_done_mask);
+ refcount_set(&eth->dma_refcnt, 1);
+ }
+ else
+@@ -4048,7 +4053,9 @@ static int mtk_probe(struct platform_dev
+ }
+
+ if (eth->soc->offload_version) {
+- eth->ppe = mtk_ppe_init(eth, eth->base + MTK_ETH_PPE_BASE, 2);
++ u32 ppe_addr = eth->soc->reg_map->ppe_base;
++
++ eth->ppe = mtk_ppe_init(eth, eth->base + ppe_addr, 2);
+ if (!eth->ppe) {
+ err = -ENOMEM;
+ goto err_free_dev;
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -105,7 +105,6 @@
+ #define MTK_GDMA_TCS_EN BIT(21)
+ #define MTK_GDMA_UCS_EN BIT(20)
+ #define MTK_GDMA_TO_PDMA 0x0
+-#define MTK_GDMA_TO_PPE 0x4444
+ #define MTK_GDMA_DROP_ALL 0x7777
+
+ /* Unicast Filter MAC Address Register - Low */
+@@ -953,6 +952,8 @@ struct mtk_reg_map {
+ u32 fq_blen; /* fq free page buffer length */
+ } qdma;
+ u32 gdm1_cnt;
++ u32 gdma_to_ppe0;
++ u32 ppe_base;
+ };
+
+ /* struct mtk_eth_data - This is the structure holding all differences
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -8,8 +8,6 @@
+ #include <linux/bitfield.h>
+ #include <linux/rhashtable.h>
+
+-#define MTK_ETH_PPE_BASE 0xc00
+-
+ #define MTK_PPE_ENTRIES_SHIFT 3
+ #define MTK_PPE_ENTRIES (1024 << MTK_PPE_ENTRIES_SHIFT)
+ #define MTK_PPE_HASH_MASK (MTK_PPE_ENTRIES - 1)
diff --git a/target/linux/generic/backport-6.1/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch b/target/linux/generic/backport-6.1/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch
new file mode 100644
index 0000000000..7bed2785c9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch
@@ -0,0 +1,199 @@
+From patchwork Thu Sep 8 19:33:38 2022
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: Lorenzo Bianconi <lorenzo@kernel.org>
+X-Patchwork-Id: 12970557
+X-Patchwork-Delegate: kuba@kernel.org
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+To: netdev@vger.kernel.org
+Cc: nbd@nbd.name, john@phrozen.org, sean.wang@mediatek.com,
+ Mark-MC.Lee@mediatek.com, davem@davemloft.net, edumazet@google.com,
+ kuba@kernel.org, pabeni@redhat.com, matthias.bgg@gmail.com,
+ linux-mediatek@lists.infradead.org, lorenzo.bianconi@redhat.com,
+ Bo.Jiao@mediatek.com, sujuan.chen@mediatek.com,
+ ryder.Lee@mediatek.com, evelyn.tsai@mediatek.com,
+ devicetree@vger.kernel.org, robh@kernel.org
+Subject: [PATCH net-next 04/12] net: ethernet: mtk_eth_soc: move ppe table
+ hash offset to mtk_soc_data structure
+Date: Thu, 8 Sep 2022 21:33:38 +0200
+Message-Id:
+ <cc263ffeaa3e1d7314e36a4f941e96d38e41a6bf.1662661555.git.lorenzo@kernel.org>
+X-Mailer: git-send-email 2.37.3
+In-Reply-To: <cover.1662661555.git.lorenzo@kernel.org>
+References: <cover.1662661555.git.lorenzo@kernel.org>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+This is a preliminary patch to introduce mt7986 hw packet engine.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+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>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++++
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 ++
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 24 +++++++++++++++------
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 2 +-
+ 4 files changed, 25 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4151,6 +4151,7 @@ static const struct mtk_soc_data mt7621_
+ .required_clks = MT7621_CLKS_BITMAP,
+ .required_pctl = false,
+ .offload_version = 2,
++ .hash_offset = 2,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4169,6 +4170,7 @@ static const struct mtk_soc_data mt7622_
+ .required_clks = MT7622_CLKS_BITMAP,
+ .required_pctl = false,
+ .offload_version = 2,
++ .hash_offset = 2,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4186,6 +4188,7 @@ static const struct mtk_soc_data mt7623_
+ .required_clks = MT7623_CLKS_BITMAP,
+ .required_pctl = true,
+ .offload_version = 2,
++ .hash_offset = 2,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4219,6 +4222,7 @@ static const struct mtk_soc_data mt7986_
+ .caps = MT7986_CAPS,
+ .required_clks = MT7986_CLKS_BITMAP,
+ .required_pctl = false,
++ .hash_offset = 4,
+ .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
+@@ -967,6 +967,7 @@ struct mtk_reg_map {
+ * the target SoC
+ * @required_pctl A bool value to show whether the SoC requires
+ * the extra setup for those pins used by GMAC.
++ * @hash_offset Flow table hash offset.
+ * @txd_size Tx DMA descriptor size.
+ * @rxd_size Rx DMA descriptor size.
+ * @rx_irq_done_mask Rx irq done register mask.
+@@ -981,6 +982,7 @@ struct mtk_soc_data {
+ u32 required_clks;
+ bool required_pctl;
+ u8 offload_version;
++ u8 hash_offset;
+ netdev_features_t hw_features;
+ struct {
+ u32 txd_size;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -88,7 +88,7 @@ static void mtk_ppe_cache_enable(struct
+ enable * MTK_PPE_CACHE_CTL_EN);
+ }
+
+-static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e)
++static u32 mtk_ppe_hash_entry(struct mtk_eth *eth, struct mtk_foe_entry *e)
+ {
+ u32 hv1, hv2, hv3;
+ u32 hash;
+@@ -122,7 +122,7 @@ static u32 mtk_ppe_hash_entry(struct mtk
+ hash = (hash >> 24) | ((hash & 0xffffff) << 8);
+ hash ^= hv1 ^ hv2 ^ hv3;
+ hash ^= hash >> 16;
+- hash <<= 1;
++ hash <<= (ffs(eth->soc->hash_offset) - 1);
+ hash &= MTK_PPE_ENTRIES - 1;
+
+ return hash;
+@@ -540,15 +540,16 @@ mtk_foe_entry_commit_l2(struct mtk_ppe *
+ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
++ const struct mtk_soc_data *soc = ppe->eth->soc;
+ u32 hash;
+
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
+ return mtk_foe_entry_commit_l2(ppe, entry);
+
+- hash = mtk_ppe_hash_entry(&entry->data);
++ hash = mtk_ppe_hash_entry(ppe->eth, &entry->data);
+ entry->hash = 0xffff;
+ spin_lock_bh(&ppe_lock);
+- hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]);
++ hlist_add_head(&entry->list, &ppe->foe_flow[hash / soc->hash_offset]);
+ spin_unlock_bh(&ppe_lock);
+
+ return 0;
+@@ -558,6 +559,7 @@ static void
+ mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
+ u16 hash)
+ {
++ const struct mtk_soc_data *soc = ppe->eth->soc;
+ struct mtk_flow_entry *flow_info;
+ struct mtk_foe_entry foe, *hwe;
+ struct mtk_foe_mac_info *l2;
+@@ -572,7 +574,8 @@ mtk_foe_entry_commit_subflow(struct mtk_
+ flow_info->l2_data.base_flow = entry;
+ flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
+ flow_info->hash = hash;
+- hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / 2]);
++ hlist_add_head(&flow_info->list,
++ &ppe->foe_flow[hash / soc->hash_offset]);
+ hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
+
+ hwe = &ppe->foe_table[hash];
+@@ -596,7 +599,8 @@ mtk_foe_entry_commit_subflow(struct mtk_
+
+ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
+ {
+- struct hlist_head *head = &ppe->foe_flow[hash / 2];
++ const struct mtk_soc_data *soc = ppe->eth->soc;
++ struct hlist_head *head = &ppe->foe_flow[hash / soc->hash_offset];
+ struct mtk_foe_entry *hwe = &ppe->foe_table[hash];
+ struct mtk_flow_entry *entry;
+ struct mtk_foe_bridge key = {};
+@@ -680,9 +684,11 @@ int mtk_foe_entry_idle_time(struct mtk_p
+ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+ int version)
+ {
++ const struct mtk_soc_data *soc = eth->soc;
+ struct device *dev = eth->dev;
+ struct mtk_foe_entry *foe;
+ struct mtk_ppe *ppe;
++ u32 foe_flow_size;
+
+ ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL);
+ if (!ppe)
+@@ -705,6 +711,12 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+
+ ppe->foe_table = foe;
+
++ foe_flow_size = (MTK_PPE_ENTRIES / soc->hash_offset) *
++ sizeof(*ppe->foe_flow);
++ ppe->foe_flow = devm_kzalloc(dev, foe_flow_size, GFP_KERNEL);
++ if (!ppe->foe_flow)
++ return NULL;
++
+ mtk_ppe_debugfs_init(ppe);
+
+ return ppe;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -270,7 +270,7 @@ struct mtk_ppe {
+ dma_addr_t foe_phys;
+
+ u16 foe_check_time[MTK_PPE_ENTRIES];
+- struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2];
++ struct hlist_head *foe_flow;
+
+ struct rhashtable l2_flows;
+
diff --git a/target/linux/generic/backport-6.1/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch b/target/linux/generic/backport-6.1/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch
new file mode 100644
index 0000000000..93860e4145
--- /dev/null
+++ b/target/linux/generic/backport-6.1/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch
@@ -0,0 +1,318 @@
+From patchwork Thu Sep 8 19:33:39 2022
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: Lorenzo Bianconi <lorenzo@kernel.org>
+X-Patchwork-Id: 12970559
+X-Patchwork-Delegate: kuba@kernel.org
+Return-Path: <netdev-owner@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+To: netdev@vger.kernel.org
+Cc: nbd@nbd.name, john@phrozen.org, sean.wang@mediatek.com,
+ Mark-MC.Lee@mediatek.com, davem@davemloft.net, edumazet@google.com,
+ kuba@kernel.org, pabeni@redhat.com, matthias.bgg@gmail.com,
+ linux-mediatek@lists.infradead.org, lorenzo.bianconi@redhat.com,
+ Bo.Jiao@mediatek.com, sujuan.chen@mediatek.com,
+ ryder.Lee@mediatek.com, evelyn.tsai@mediatek.com,
+ devicetree@vger.kernel.org, robh@kernel.org
+Subject: [PATCH net-next 05/12] net: ethernet: mtk_eth_soc: add the capability
+ to run multiple ppe
+Date: Thu, 8 Sep 2022 21:33:39 +0200
+Message-Id:
+ <dd0254775390eb031c67c448df8b19e87df58558.1662661555.git.lorenzo@kernel.org>
+X-Mailer: git-send-email 2.37.3
+In-Reply-To: <cover.1662661555.git.lorenzo@kernel.org>
+References: <cover.1662661555.git.lorenzo@kernel.org>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+mt7986 chipset support multiple packet engines for wlan <-> eth
+packet forwarding.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+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>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 35 ++++++++++++-------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +-
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 14 +++++---
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 9 +++--
+ .../net/ethernet/mediatek/mtk_ppe_debugfs.c | 8 ++---
+ .../net/ethernet/mediatek/mtk_ppe_offload.c | 13 +++----
+ 6 files changed, 48 insertions(+), 33 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1872,7 +1872,7 @@ static int mtk_poll_rx(struct napi_struc
+
+ reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4);
+ if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
+- mtk_ppe_check_skb(eth->ppe, skb, hash);
++ 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)) {
+@@ -2930,7 +2930,8 @@ static int mtk_open(struct net_device *d
+ /* we run 2 netdevs on the same dma ring so we only bring it up once */
+ if (!refcount_read(&eth->dma_refcnt)) {
+ const struct mtk_soc_data *soc = eth->soc;
+- u32 gdm_config = MTK_GDMA_TO_PDMA;
++ u32 gdm_config;
++ int i;
+ int err;
+
+ err = mtk_start_dma(eth);
+@@ -2939,8 +2940,11 @@ static int mtk_open(struct net_device *d
+ return err;
+ }
+
+- if (soc->offload_version && mtk_ppe_start(eth->ppe) == 0)
+- gdm_config = soc->reg_map->gdma_to_ppe0;
++ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
++ mtk_ppe_start(eth->ppe[i]);
++
++ gdm_config = soc->offload_version ? soc->reg_map->gdma_to_ppe0
++ : MTK_GDMA_TO_PDMA;
+
+ mtk_gdm_config(eth, gdm_config);
+
+@@ -2985,6 +2989,7 @@ static int mtk_stop(struct net_device *d
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
++ int i;
+
+ phylink_stop(mac->phylink);
+
+@@ -3012,8 +3017,8 @@ static int mtk_stop(struct net_device *d
+
+ mtk_dma_free(eth);
+
+- if (eth->soc->offload_version)
+- mtk_ppe_stop(eth->ppe);
++ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
++ mtk_ppe_stop(eth->ppe[i]);
+
+ return 0;
+ }
+@@ -4053,12 +4058,19 @@ static int mtk_probe(struct platform_dev
+ }
+
+ if (eth->soc->offload_version) {
+- u32 ppe_addr = eth->soc->reg_map->ppe_base;
++ u32 num_ppe;
+
+- eth->ppe = mtk_ppe_init(eth, eth->base + ppe_addr, 2);
+- if (!eth->ppe) {
+- err = -ENOMEM;
+- goto err_free_dev;
++ num_ppe = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1;
++ num_ppe = min_t(u32, ARRAY_SIZE(eth->ppe), num_ppe);
++ for (i = 0; i < num_ppe; i++) {
++ 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);
++ if (!eth->ppe[i]) {
++ err = -ENOMEM;
++ goto err_free_dev;
++ }
+ }
+
+ err = mtk_eth_offload_init(eth);
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1112,7 +1112,7 @@ struct mtk_eth {
+
+ int ip_align;
+
+- struct mtk_ppe *ppe;
++ struct mtk_ppe *ppe[2];
+ struct rhashtable flow_table;
+
+ struct bpf_prog __rcu *prog;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -682,7 +682,7 @@ int mtk_foe_entry_idle_time(struct mtk_p
+ }
+
+ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+- int version)
++ int version, int index)
+ {
+ const struct mtk_soc_data *soc = eth->soc;
+ struct device *dev = eth->dev;
+@@ -717,7 +717,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+ if (!ppe->foe_flow)
+ return NULL;
+
+- mtk_ppe_debugfs_init(ppe);
++ mtk_ppe_debugfs_init(ppe, index);
+
+ return ppe;
+ }
+@@ -738,10 +738,13 @@ static void mtk_ppe_init_foe_table(struc
+ ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC;
+ }
+
+-int mtk_ppe_start(struct mtk_ppe *ppe)
++void mtk_ppe_start(struct mtk_ppe *ppe)
+ {
+ u32 val;
+
++ if (!ppe)
++ return;
++
+ mtk_ppe_init_foe_table(ppe);
+ ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys);
+
+@@ -809,8 +812,6 @@ int mtk_ppe_start(struct mtk_ppe *ppe)
+ ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
+
+ ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
+-
+- return 0;
+ }
+
+ int mtk_ppe_stop(struct mtk_ppe *ppe)
+@@ -818,6 +819,9 @@ int mtk_ppe_stop(struct mtk_ppe *ppe)
+ u32 val;
+ int i;
+
++ if (!ppe)
++ return 0;
++
+ for (i = 0; i < MTK_PPE_ENTRIES; i++)
+ ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
+ MTK_FOE_STATE_INVALID);
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -247,6 +247,7 @@ struct mtk_flow_entry {
+ };
+ u8 type;
+ s8 wed_index;
++ u8 ppe_index;
+ u16 hash;
+ union {
+ struct mtk_foe_entry data;
+@@ -265,6 +266,7 @@ struct mtk_ppe {
+ struct device *dev;
+ void __iomem *base;
+ int version;
++ char dirname[5];
+
+ struct mtk_foe_entry *foe_table;
+ dma_addr_t foe_phys;
+@@ -277,8 +279,9 @@ struct mtk_ppe {
+ void *acct_table;
+ };
+
+-struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version);
+-int mtk_ppe_start(struct mtk_ppe *ppe);
++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
++ int version, int index);
++void mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
+
+ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
+@@ -317,6 +320,6 @@ int mtk_foe_entry_set_wdma(struct mtk_fo
+ 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);
+-int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index);
+
+ #endif
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -187,7 +187,7 @@ mtk_ppe_debugfs_foe_open_bind(struct ino
+ inode->i_private);
+ }
+
+-int mtk_ppe_debugfs_init(struct mtk_ppe *ppe)
++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index)
+ {
+ static const struct file_operations fops_all = {
+ .open = mtk_ppe_debugfs_foe_open_all,
+@@ -195,17 +195,17 @@ int mtk_ppe_debugfs_init(struct mtk_ppe
+ .llseek = seq_lseek,
+ .release = single_release,
+ };
+-
+ static const struct file_operations fops_bind = {
+ .open = mtk_ppe_debugfs_foe_open_bind,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ };
+-
+ struct dentry *root;
+
+- root = debugfs_create_dir("mtk_ppe", NULL);
++ snprintf(ppe->dirname, sizeof(ppe->dirname), "ppe%d", index);
++
++ root = debugfs_create_dir(ppe->dirname, NULL);
+ if (!root)
+ return -ENOMEM;
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -434,7 +434,7 @@ mtk_flow_offload_replace(struct mtk_eth
+ memcpy(&entry->data, &foe, sizeof(entry->data));
+ entry->wed_index = wed_index;
+
+- err = mtk_foe_entry_commit(eth->ppe, entry);
++ err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry);
+ if (err < 0)
+ goto free;
+
+@@ -446,7 +446,7 @@ mtk_flow_offload_replace(struct mtk_eth
+ return 0;
+
+ clear:
+- mtk_foe_entry_clear(eth->ppe, entry);
++ mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry);
+ free:
+ kfree(entry);
+ if (wed_index >= 0)
+@@ -464,7 +464,7 @@ mtk_flow_offload_destroy(struct mtk_eth
+ if (!entry)
+ return -ENOENT;
+
+- mtk_foe_entry_clear(eth->ppe, entry);
++ mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry);
+ rhashtable_remove_fast(&eth->flow_table, &entry->node,
+ mtk_flow_ht_params);
+ if (entry->wed_index >= 0)
+@@ -485,7 +485,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
+ if (!entry)
+ return -ENOENT;
+
+- idle = mtk_foe_entry_idle_time(eth->ppe, entry);
++ idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
+ f->stats.lastused = jiffies - idle * HZ;
+
+ return 0;
+@@ -537,7 +537,7 @@ mtk_eth_setup_tc_block(struct net_device
+ struct flow_block_cb *block_cb;
+ flow_setup_cb_t *cb;
+
+- if (!eth->ppe || !eth->ppe->foe_table)
++ if (!eth->soc->offload_version)
+ return -EOPNOTSUPP;
+
+ if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+@@ -590,8 +590,5 @@ int mtk_eth_setup_tc(struct net_device *
+
+ int mtk_eth_offload_init(struct mtk_eth *eth)
+ {
+- if (!eth->ppe || !eth->ppe->foe_table)
+- return 0;
+-
+ return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);
+ }
diff --git a/target/linux/generic/backport-6.1/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch b/target/linux/generic/backport-6.1/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch
new file mode 100644
index 0000000000..a4b285632e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch
@@ -0,0 +1,80 @@
+From 0dcbe607cec32ccae23b02a641b8bd6191a328ae Mon Sep 17 00:00:00 2001
+Message-Id: <0dcbe607cec32ccae23b02a641b8bd6191a328ae.1662243796.git.lorenzo@kernel.org>
+In-Reply-To: <43a21841ce0175d29f23c34a65ceaaf9dd7eb8b7.1662243796.git.lorenzo@kernel.org>
+References: <43a21841ce0175d29f23c34a65ceaaf9dd7eb8b7.1662243796.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Tue, 23 Aug 2022 23:09:05 +0200
+Subject: [PATCH net-next 2/4] net: ethernet: mtk_eth_soc: move wdma_base
+ definitions in mtk register map
+
+This is a preliminary patch to introduce mt7986 wed support.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 16 ++++++++++------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 +---
+ 2 files changed, 11 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -75,6 +75,10 @@ static const struct mtk_reg_map mtk_reg_
+ .gdm1_cnt = 0x2400,
+ .gdma_to_ppe0 = 0x4444,
+ .ppe_base = 0x0c00,
++ .wdma_base = {
++ [0] = 0x2800,
++ [1] = 0x2c00,
++ },
+ };
+
+ static const struct mtk_reg_map mt7628_reg_map = {
+@@ -130,6 +134,10 @@ static const struct mtk_reg_map mt7986_r
+ .gdm1_cnt = 0x1c00,
+ .gdma_to_ppe0 = 0x3333,
+ .ppe_base = 0x2000,
++ .wdma_base = {
++ [0] = 0x4800,
++ [1] = 0x4c00,
++ },
+ };
+
+ /* strings used by ethtool */
+@@ -3970,16 +3978,12 @@ static int mtk_probe(struct platform_dev
+ for (i = 0;; i++) {
+ struct device_node *np = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,wed", i);
+- static const u32 wdma_regs[] = {
+- MTK_WDMA0_BASE,
+- MTK_WDMA1_BASE
+- };
+ void __iomem *wdma;
+
+- if (!np || i >= ARRAY_SIZE(wdma_regs))
++ if (!np || i >= ARRAY_SIZE(eth->soc->reg_map->wdma_base))
+ break;
+
+- wdma = eth->base + wdma_regs[i];
++ wdma = eth->base + eth->soc->reg_map->wdma_base[i];
+ mtk_wed_add_hw(np, eth, wdma, i);
+ }
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -268,9 +268,6 @@
+ #define TX_DMA_FPORT_MASK_V2 0xf
+ #define TX_DMA_SWC_V2 BIT(30)
+
+-#define MTK_WDMA0_BASE 0x2800
+-#define MTK_WDMA1_BASE 0x2c00
+-
+ /* QDMA descriptor txd4 */
+ #define TX_DMA_CHKSUM (0x7 << 29)
+ #define TX_DMA_TSO BIT(28)
+@@ -954,6 +951,7 @@ struct mtk_reg_map {
+ u32 gdm1_cnt;
+ u32 gdma_to_ppe0;
+ u32 ppe_base;
++ u32 wdma_base[2];
+ };
+
+ /* struct mtk_eth_data - This is the structure holding all differences
diff --git a/target/linux/generic/backport-6.1/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch b/target/linux/generic/backport-6.1/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch
new file mode 100644
index 0000000000..2bce63c4c0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch
@@ -0,0 +1,251 @@
+From e3c27d869fccc1f2b8d0b4cde4763ab223874e8c Mon Sep 17 00:00:00 2001
+Message-Id: <e3c27d869fccc1f2b8d0b4cde4763ab223874e8c.1662243796.git.lorenzo@kernel.org>
+In-Reply-To: <43a21841ce0175d29f23c34a65ceaaf9dd7eb8b7.1662243796.git.lorenzo@kernel.org>
+References: <43a21841ce0175d29f23c34a65ceaaf9dd7eb8b7.1662243796.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sun, 21 Aug 2022 17:51:17 +0200
+Subject: [PATCH net-next 3/4] net: ethernet: mtk_eth_soc: add foe_entry_size
+ to mtk_eth_soc
+
+Introduce foe_entry_size to mtk_eth_soc data structure since mt7986
+relies on a bigger mtk_foe_entry data structure.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 10 ++++
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 55 +++++++++++--------
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 2 +-
+ .../net/ethernet/mediatek/mtk_ppe_debugfs.c | 2 +-
+ 5 files changed, 48 insertions(+), 24 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4168,6 +4168,7 @@ static const struct mtk_soc_data mt7621_
+ .required_pctl = false,
+ .offload_version = 2,
+ .hash_offset = 2,
++ .foe_entry_size = sizeof(struct mtk_foe_entry),
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4187,6 +4188,7 @@ static const struct mtk_soc_data mt7622_
+ .required_pctl = false,
+ .offload_version = 2,
+ .hash_offset = 2,
++ .foe_entry_size = sizeof(struct mtk_foe_entry),
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4205,6 +4207,7 @@ static const struct mtk_soc_data mt7623_
+ .required_pctl = true,
+ .offload_version = 2,
+ .hash_offset = 2,
++ .foe_entry_size = sizeof(struct mtk_foe_entry),
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -966,6 +966,7 @@ struct mtk_reg_map {
+ * @required_pctl A bool value to show whether the SoC requires
+ * the extra setup for those pins used by GMAC.
+ * @hash_offset Flow table hash offset.
++ * @foe_entry_size Foe table entry size.
+ * @txd_size Tx DMA descriptor size.
+ * @rxd_size Rx DMA descriptor size.
+ * @rx_irq_done_mask Rx irq done register mask.
+@@ -981,6 +982,7 @@ struct mtk_soc_data {
+ bool required_pctl;
+ u8 offload_version;
+ u8 hash_offset;
++ u16 foe_entry_size;
+ netdev_features_t hw_features;
+ struct {
+ u32 txd_size;
+@@ -1141,6 +1143,14 @@ struct mtk_mac {
+ /* the struct describing the SoC. these are declared in the soc_xyz.c files */
+ extern const struct of_device_id of_mtk_match[];
+
++static inline struct mtk_foe_entry *
++mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash)
++{
++ const struct mtk_soc_data *soc = ppe->eth->soc;
++
++ return ppe->foe_table + hash * soc->foe_entry_size;
++}
++
+ /* read the hardware status register */
+ void mtk_stats_update_mac(struct mtk_mac *mac);
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -410,9 +410,10 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
+
+ hlist_del_init(&entry->list);
+ if (entry->hash != 0xffff) {
+- ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
+- ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
+- MTK_FOE_STATE_BIND);
++ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash);
++
++ hwe->ib1 &= ~MTK_FOE_IB1_STATE;
++ hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND);
+ dma_wmb();
+ }
+ entry->hash = 0xffff;
+@@ -451,7 +452,7 @@ mtk_flow_entry_update_l2(struct mtk_ppe
+ int cur_idle;
+ u32 ib1;
+
+- hwe = &ppe->foe_table[cur->hash];
++ hwe = mtk_foe_get_entry(ppe, cur->hash);
+ ib1 = READ_ONCE(hwe->ib1);
+
+ if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
+@@ -473,8 +474,8 @@ mtk_flow_entry_update_l2(struct mtk_ppe
+ static void
+ mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
++ struct mtk_foe_entry foe = {};
+ struct mtk_foe_entry *hwe;
+- struct mtk_foe_entry foe;
+
+ spin_lock_bh(&ppe_lock);
+
+@@ -486,8 +487,8 @@ mtk_flow_entry_update(struct mtk_ppe *pp
+ if (entry->hash == 0xffff)
+ goto out;
+
+- hwe = &ppe->foe_table[entry->hash];
+- memcpy(&foe, hwe, sizeof(foe));
++ hwe = mtk_foe_get_entry(ppe, entry->hash);
++ memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size);
+ if (!mtk_flow_entry_match(entry, &foe)) {
+ entry->hash = 0xffff;
+ goto out;
+@@ -511,8 +512,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+ entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
+ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
+
+- hwe = &ppe->foe_table[hash];
+- memcpy(&hwe->data, &entry->data, sizeof(hwe->data));
++ hwe = mtk_foe_get_entry(ppe, hash);
++ memcpy(&hwe->data, &entry->data, ppe->eth->soc->foe_entry_size);
+ wmb();
+ hwe->ib1 = entry->ib1;
+
+@@ -561,7 +562,7 @@ mtk_foe_entry_commit_subflow(struct mtk_
+ {
+ const struct mtk_soc_data *soc = ppe->eth->soc;
+ struct mtk_flow_entry *flow_info;
+- struct mtk_foe_entry foe, *hwe;
++ struct mtk_foe_entry foe = {}, *hwe;
+ struct mtk_foe_mac_info *l2;
+ u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP;
+ int type;
+@@ -578,8 +579,8 @@ mtk_foe_entry_commit_subflow(struct mtk_
+ &ppe->foe_flow[hash / soc->hash_offset]);
+ hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
+
+- hwe = &ppe->foe_table[hash];
+- memcpy(&foe, hwe, sizeof(foe));
++ hwe = mtk_foe_get_entry(ppe, hash);
++ memcpy(&foe, hwe, soc->foe_entry_size);
+ foe.ib1 &= ib1_mask;
+ foe.ib1 |= entry->data.ib1 & ~ib1_mask;
+
+@@ -601,7 +602,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+ {
+ const struct mtk_soc_data *soc = ppe->eth->soc;
+ struct hlist_head *head = &ppe->foe_flow[hash / soc->hash_offset];
+- struct mtk_foe_entry *hwe = &ppe->foe_table[hash];
++ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash);
+ struct mtk_flow_entry *entry;
+ struct mtk_foe_bridge key = {};
+ struct hlist_node *n;
+@@ -686,9 +687,9 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+ {
+ const struct mtk_soc_data *soc = eth->soc;
+ struct device *dev = eth->dev;
+- struct mtk_foe_entry *foe;
+ struct mtk_ppe *ppe;
+ u32 foe_flow_size;
++ void *foe;
+
+ ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL);
+ if (!ppe)
+@@ -704,7 +705,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+ ppe->dev = dev;
+ ppe->version = version;
+
+- foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
++ foe = dmam_alloc_coherent(ppe->dev,
++ MTK_PPE_ENTRIES * soc->foe_entry_size,
+ &ppe->foe_phys, GFP_KERNEL);
+ if (!foe)
+ return NULL;
+@@ -727,15 +729,21 @@ static void mtk_ppe_init_foe_table(struc
+ static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 };
+ int i, k;
+
+- memset(ppe->foe_table, 0, MTK_PPE_ENTRIES * sizeof(*ppe->foe_table));
++ memset(ppe->foe_table, 0,
++ MTK_PPE_ENTRIES * ppe->eth->soc->foe_entry_size);
+
+ if (!IS_ENABLED(CONFIG_SOC_MT7621))
+ return;
+
+ /* skip all entries that cross the 1024 byte boundary */
+- for (i = 0; i < MTK_PPE_ENTRIES; i += 128)
+- for (k = 0; k < ARRAY_SIZE(skip); k++)
+- ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC;
++ for (i = 0; i < MTK_PPE_ENTRIES; i += 128) {
++ for (k = 0; k < ARRAY_SIZE(skip); k++) {
++ struct mtk_foe_entry *hwe;
++
++ hwe = mtk_foe_get_entry(ppe, i + skip[k]);
++ hwe->ib1 |= MTK_FOE_IB1_STATIC;
++ }
++ }
+ }
+
+ void mtk_ppe_start(struct mtk_ppe *ppe)
+@@ -822,9 +830,12 @@ int mtk_ppe_stop(struct mtk_ppe *ppe)
+ if (!ppe)
+ return 0;
+
+- for (i = 0; i < MTK_PPE_ENTRIES; i++)
+- ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
+- MTK_FOE_STATE_INVALID);
++ for (i = 0; i < MTK_PPE_ENTRIES; i++) {
++ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, i);
++
++ hwe->ib1 = FIELD_PREP(MTK_FOE_IB1_STATE,
++ MTK_FOE_STATE_INVALID);
++ }
+
+ mtk_ppe_cache_enable(ppe, false);
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -268,7 +268,7 @@ struct mtk_ppe {
+ int version;
+ char dirname[5];
+
+- struct mtk_foe_entry *foe_table;
++ void *foe_table;
+ dma_addr_t foe_phys;
+
+ u16 foe_check_time[MTK_PPE_ENTRIES];
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -79,7 +79,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file
+ int i;
+
+ for (i = 0; i < MTK_PPE_ENTRIES; i++) {
+- struct mtk_foe_entry *entry = &ppe->foe_table[i];
++ struct mtk_foe_entry *entry = mtk_foe_get_entry(ppe, i);
+ struct mtk_foe_mac_info *l2;
+ struct mtk_flow_addr_info ai = {};
+ unsigned char h_source[ETH_ALEN];
diff --git a/target/linux/generic/backport-6.1/718-v6.0-net-ethernet-mtk_eth_soc-fix-typo-in-__mtk_foe_entry.patch b/target/linux/generic/backport-6.1/718-v6.0-net-ethernet-mtk_eth_soc-fix-typo-in-__mtk_foe_entry.patch
new file mode 100644
index 0000000000..77daf6c8bf
--- /dev/null
+++ b/target/linux/generic/backport-6.1/718-v6.0-net-ethernet-mtk_eth_soc-fix-typo-in-__mtk_foe_entry.patch
@@ -0,0 +1,27 @@
+From 12ff69304c83c679ca01ef3db963ab0db9de19fb Mon Sep 17 00:00:00 2001
+Message-Id: <12ff69304c83c679ca01ef3db963ab0db9de19fb.1662332102.git.lorenzo@kernel.org>
+In-Reply-To: <2a60545635c2705312299384f4e9fec2f2a3acd6.1662332102.git.lorenzo@kernel.org>
+References: <2a60545635c2705312299384f4e9fec2f2a3acd6.1662332102.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 5 Sep 2022 00:43:43 +0200
+Subject: [PATCH net-next 2/6] net: ethernet: mtk_eth_soc: fix typo in
+ __mtk_foe_entry_clear
+
+Set ib1 state to MTK_FOE_STATE_UNBIND in __mtk_foe_entry_clear routine.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -413,7 +413,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
+ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash);
+
+ hwe->ib1 &= ~MTK_FOE_IB1_STATE;
+- hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND);
++ hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_UNBIND);
+ dma_wmb();
+ }
+ entry->hash = 0xffff;
diff --git a/target/linux/generic/backport-6.1/719-v6.0-net-ethernet-mtk_eth_soc-check-max-allowed-value-in-.patch b/target/linux/generic/backport-6.1/719-v6.0-net-ethernet-mtk_eth_soc-check-max-allowed-value-in-.patch
new file mode 100644
index 0000000000..7ab6d486b2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/719-v6.0-net-ethernet-mtk_eth_soc-check-max-allowed-value-in-.patch
@@ -0,0 +1,28 @@
+From 4253e6e2b795a18ab534adcd5c313d3fc4150975 Mon Sep 17 00:00:00 2001
+Message-Id: <4253e6e2b795a18ab534adcd5c313d3fc4150975.1662332102.git.lorenzo@kernel.org>
+In-Reply-To: <2a60545635c2705312299384f4e9fec2f2a3acd6.1662332102.git.lorenzo@kernel.org>
+References: <2a60545635c2705312299384f4e9fec2f2a3acd6.1662332102.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 5 Sep 2022 00:48:52 +0200
+Subject: [PATCH net-next 3/6] net: ethernet: mtk_eth_soc: check max allowed
+ value in mtk_ppe_check_skb
+
+Check theoretical OOB accesses in mtk_ppe_check_skb routine
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -294,6 +294,9 @@ mtk_ppe_check_skb(struct mtk_ppe *ppe, s
+ if (!ppe)
+ return;
+
++ if (hash > MTK_PPE_HASH_MASK)
++ return;
++
+ now = (u16)jiffies;
+ diff = now - ppe->foe_check_time[hash];
+ if (diff < HZ / 10)
diff --git a/target/linux/generic/backport-6.1/720-v6.0-net-ethernet-mtk_eth_wed-add-mtk_wed_configure_irq-a.patch b/target/linux/generic/backport-6.1/720-v6.0-net-ethernet-mtk_eth_wed-add-mtk_wed_configure_irq-a.patch
new file mode 100644
index 0000000000..a8c88daf1f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/720-v6.0-net-ethernet-mtk_eth_wed-add-mtk_wed_configure_irq-a.patch
@@ -0,0 +1,189 @@
+From e5ecb4f619197b93fa682d722452dc8412864cdb Mon Sep 17 00:00:00 2001
+Message-Id: <e5ecb4f619197b93fa682d722452dc8412864cdb.1662886033.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 26 Aug 2022 01:12:57 +0200
+Subject: [PATCH net-next 1/5] net: ethernet: mtk_eth_wed: add
+ mtk_wed_configure_irq and mtk_wed_dma_{enable/disable}
+
+Introduce mtk_wed_configure_irq, mtk_wed_dma_enable and mtk_wed_dma_disable
+utility routines.
+This is a preliminary patch to introduce mt7986 wed support.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+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>
+---
+ drivers/net/ethernet/mediatek/mtk_wed.c | 87 +++++++++++++-------
+ drivers/net/ethernet/mediatek/mtk_wed_regs.h | 6 +-
+ 2 files changed, 64 insertions(+), 29 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -237,9 +237,30 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
+ }
+
+ static void
+-mtk_wed_stop(struct mtk_wed_device *dev)
++mtk_wed_dma_disable(struct mtk_wed_device *dev)
+ {
++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
++
++ wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
++
++ wed_clr(dev, MTK_WED_GLO_CFG,
++ MTK_WED_GLO_CFG_TX_DMA_EN |
++ MTK_WED_GLO_CFG_RX_DMA_EN);
++
+ regmap_write(dev->hw->mirror, dev->hw->index * 4, 0);
++ wdma_m32(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 |
++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES, 0);
++}
++
++static void
++mtk_wed_stop(struct mtk_wed_device *dev)
++{
++ mtk_wed_dma_disable(dev);
+ mtk_wed_set_ext_int(dev, false);
+
+ wed_clr(dev, MTK_WED_CTRL,
+@@ -252,15 +273,6 @@ 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);
+-
+- wed_clr(dev, MTK_WED_GLO_CFG,
+- MTK_WED_GLO_CFG_TX_DMA_EN |
+- MTK_WED_GLO_CFG_RX_DMA_EN);
+- wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
+- MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
+- MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
+- wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
+- MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
+ }
+
+ static void
+@@ -313,7 +325,10 @@ mtk_wed_hw_init_early(struct mtk_wed_dev
+ MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
+ wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set);
+
+- wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_INFO_PRERES);
++ wdma_set(dev, MTK_WDMA_GLO_CFG,
++ MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES |
++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
+
+ offset = dev->hw->index ? 0x04000400 : 0;
+ wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset);
+@@ -520,43 +535,38 @@ mtk_wed_wdma_ring_setup(struct mtk_wed_d
+ }
+
+ static void
+-mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
++mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask)
+ {
+- u32 wdma_mask;
+- u32 val;
+- 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);
+-
+- wdma_mask = FIELD_PREP(MTK_WDMA_INT_MASK_RX_DONE, GENMASK(1, 0));
+-
+- mtk_wed_hw_init(dev);
++ u32 wdma_mask = FIELD_PREP(MTK_WDMA_INT_MASK_RX_DONE, GENMASK(1, 0));
+
++ /* wed control cr set */
+ wed_set(dev, MTK_WED_CTRL,
+ MTK_WED_CTRL_WDMA_INT_AGENT_EN |
+ MTK_WED_CTRL_WPDMA_INT_AGENT_EN |
+ MTK_WED_CTRL_WED_TX_BM_EN |
+ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+
+- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, MTK_WED_PCIE_INT_TRIGGER_STATUS);
++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER,
++ MTK_WED_PCIE_INT_TRIGGER_STATUS);
+
+ wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER,
+ MTK_WED_WPDMA_INT_TRIGGER_RX_DONE |
+ MTK_WED_WPDMA_INT_TRIGGER_TX_DONE);
+
+- wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
+- MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
+-
++ /* initail wdma interrupt agent */
+ wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, wdma_mask);
+ wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
+
+ wdma_w32(dev, MTK_WDMA_INT_MASK, wdma_mask);
+ wdma_w32(dev, MTK_WDMA_INT_GRP2, wdma_mask);
+-
+ wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask);
+ wed_w32(dev, MTK_WED_INT_MASK, irq_mask);
++}
++
++static void
++mtk_wed_dma_enable(struct mtk_wed_device *dev)
++{
++ wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
+
+ wed_set(dev, MTK_WED_GLO_CFG,
+ MTK_WED_GLO_CFG_TX_DMA_EN |
+@@ -567,6 +577,26 @@ mtk_wed_start(struct mtk_wed_device *dev
+ wed_set(dev, MTK_WED_WDMA_GLO_CFG,
+ MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
+
++ wdma_set(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 |
++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
++}
++
++static void
++mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
++{
++ u32 val;
++ 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);
++
++ mtk_wed_hw_init(dev);
++ mtk_wed_configure_irq(dev, irq_mask);
++
+ mtk_wed_set_ext_int(dev, true);
+ val = dev->wlan.wpdma_phys |
+ MTK_PCIE_MIRROR_MAP_EN |
+@@ -577,6 +607,7 @@ mtk_wed_start(struct mtk_wed_device *dev
+ val |= BIT(0);
+ regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
+
++ mtk_wed_dma_enable(dev);
+ dev->running = true;
+ }
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -224,7 +224,11 @@ struct mtk_wdma_desc {
+ #define MTK_WDMA_RING_RX(_n) (0x100 + (_n) * 0x10)
+
+ #define MTK_WDMA_GLO_CFG 0x204
+-#define MTK_WDMA_GLO_CFG_RX_INFO_PRERES GENMASK(28, 26)
++#define MTK_WDMA_GLO_CFG_TX_DMA_EN BIT(0)
++#define MTK_WDMA_GLO_CFG_RX_DMA_EN BIT(2)
++#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)
+
+ #define MTK_WDMA_RESET_IDX 0x208
+ #define MTK_WDMA_RESET_IDX_TX GENMASK(3, 0)
diff --git a/target/linux/generic/backport-6.1/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch b/target/linux/generic/backport-6.1/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch
new file mode 100644
index 0000000000..bfca7b20e4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch
@@ -0,0 +1,942 @@
+From 463a71af080fbc77339bee2037fb1e081e3824f7 Mon Sep 17 00:00:00 2001
+Message-Id: <463a71af080fbc77339bee2037fb1e081e3824f7.1662886034.git.lorenzo@kernel.org>
+In-Reply-To: <e5ecb4f619197b93fa682d722452dc8412864cdb.1662886033.git.lorenzo@kernel.org>
+References: <e5ecb4f619197b93fa682d722452dc8412864cdb.1662886033.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 27 Aug 2022 16:15:14 +0200
+Subject: [PATCH net-next 2/5] net: ethernet: mtk_eth_wed: add wed support for
+ mt7986 chipset
+
+Introduce Wireless Etherne Dispatcher support on transmission side
+for mt7986 chipset
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+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>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 34 +-
+ drivers/net/ethernet/mediatek/mtk_wed.c | 371 ++++++++++++++----
+ drivers/net/ethernet/mediatek/mtk_wed.h | 8 +-
+ .../net/ethernet/mediatek/mtk_wed_debugfs.c | 3 +
+ drivers/net/ethernet/mediatek/mtk_wed_regs.h | 81 +++-
+ include/linux/soc/mediatek/mtk_wed.h | 8 +
+ 6 files changed, 408 insertions(+), 97 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3895,6 +3895,7 @@ void mtk_eth_set_dma_device(struct mtk_e
+
+ static int mtk_probe(struct platform_device *pdev)
+ {
++ struct resource *res = NULL;
+ struct device_node *mac_np;
+ struct mtk_eth *eth;
+ int err, i;
+@@ -3975,16 +3976,31 @@ static int mtk_probe(struct platform_dev
+ }
+ }
+
+- for (i = 0;; i++) {
+- struct device_node *np = of_parse_phandle(pdev->dev.of_node,
+- "mediatek,wed", i);
+- void __iomem *wdma;
+-
+- if (!np || i >= ARRAY_SIZE(eth->soc->reg_map->wdma_base))
+- break;
+-
+- wdma = eth->base + eth->soc->reg_map->wdma_base[i];
+- mtk_wed_add_hw(np, eth, wdma, i);
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res)
++ return -EINVAL;
++ }
++
++ if (eth->soc->offload_version) {
++ for (i = 0;; i++) {
++ struct device_node *np;
++ phys_addr_t wdma_phy;
++ u32 wdma_base;
++
++ if (i >= ARRAY_SIZE(eth->soc->reg_map->wdma_base))
++ break;
++
++ np = of_parse_phandle(pdev->dev.of_node,
++ "mediatek,wed", i);
++ if (!np)
++ break;
++
++ wdma_base = eth->soc->reg_map->wdma_base[i];
++ wdma_phy = res ? res->start + wdma_base : 0;
++ mtk_wed_add_hw(np, eth, eth->base + wdma_base,
++ wdma_phy, i);
++ }
+ }
+
+ for (i = 0; i < 3; i++) {
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -25,6 +25,11 @@
+
+ #define MTK_WED_TX_RING_SIZE 2048
+ #define MTK_WED_WDMA_RING_SIZE 1024
++#define MTK_WED_MAX_GROUP_SIZE 0x100
++#define MTK_WED_VLD_GROUP_SIZE 0x40
++#define MTK_WED_PER_GROUP_PKT 128
++
++#define MTK_WED_FBUF_SIZE 128
+
+ static struct mtk_wed_hw *hw_list[2];
+ static DEFINE_MUTEX(hw_lock);
+@@ -150,10 +155,17 @@ mtk_wed_buffer_alloc(struct mtk_wed_devi
+
+ desc->buf0 = cpu_to_le32(buf_phys);
+ desc->buf1 = cpu_to_le32(buf_phys + txd_size);
+- ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
+- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
+- MTK_WED_BUF_SIZE - txd_size) |
+- MTK_WDMA_DESC_CTRL_LAST_SEG1;
++
++ if (dev->hw->version == 1)
++ ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
++ MTK_WED_BUF_SIZE - txd_size) |
++ MTK_WDMA_DESC_CTRL_LAST_SEG1;
++ else
++ ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2,
++ MTK_WED_BUF_SIZE - txd_size) |
++ MTK_WDMA_DESC_CTRL_LAST_SEG0;
+ desc->ctrl = cpu_to_le32(ctrl);
+ desc->info = 0;
+ desc++;
+@@ -209,7 +221,7 @@ mtk_wed_free_ring(struct mtk_wed_device
+ if (!ring->desc)
+ return;
+
+- dma_free_coherent(dev->hw->dev, ring->size * sizeof(*ring->desc),
++ dma_free_coherent(dev->hw->dev, ring->size * ring->desc_size,
+ ring->desc, ring->desc_phys);
+ }
+
+@@ -229,6 +241,14 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
+ {
+ u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
+
++ if (dev->hw->version == 1)
++ mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
++ else
++ mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
++ MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH |
++ MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
++ MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR;
++
+ if (!dev->hw->num_flows)
+ mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
+
+@@ -237,6 +257,20 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
+ }
+
+ static void
++mtk_wed_set_512_support(struct mtk_wed_device *dev, bool enable)
++{
++ if (enable) {
++ wed_w32(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR);
++ wed_w32(dev, MTK_WED_TXP_DW1,
++ FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0103));
++ } else {
++ wed_w32(dev, MTK_WED_TXP_DW1,
++ FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0100));
++ wed_clr(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR);
++ }
++}
++
++static void
+ mtk_wed_dma_disable(struct mtk_wed_device *dev)
+ {
+ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
+@@ -249,12 +283,22 @@ mtk_wed_dma_disable(struct mtk_wed_devic
+ MTK_WED_GLO_CFG_TX_DMA_EN |
+ MTK_WED_GLO_CFG_RX_DMA_EN);
+
+- regmap_write(dev->hw->mirror, dev->hw->index * 4, 0);
+ wdma_m32(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 |
+- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES, 0);
++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES, 0);
++
++ 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);
++ } 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);
++
++ mtk_wed_set_512_support(dev, false);
++ }
+ }
+
+ static void
+@@ -293,7 +337,7 @@ mtk_wed_detach(struct mtk_wed_device *de
+ mtk_wed_free_buffer(dev);
+ mtk_wed_free_tx_rings(dev);
+
+- if (of_dma_is_coherent(wlan_node))
++ if (of_dma_is_coherent(wlan_node) && hw->hifsys)
+ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
+ BIT(hw->index), BIT(hw->index));
+
+@@ -308,14 +352,69 @@ mtk_wed_detach(struct mtk_wed_device *de
+ mutex_unlock(&hw_lock);
+ }
+
++#define PCIE_BASE_ADDR0 0x11280000
++static void
++mtk_wed_bus_init(struct mtk_wed_device *dev)
++{
++ struct device_node *np = dev->hw->eth->dev->of_node;
++ struct regmap *regs;
++ u32 val;
++
++ regs = syscon_regmap_lookup_by_phandle(np, "mediatek,wed-pcie");
++ if (IS_ERR(regs))
++ return;
++
++ regmap_update_bits(regs, 0, BIT(0), BIT(0));
++
++ wed_w32(dev, MTK_WED_PCIE_INT_CTRL,
++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2));
++
++ /* pcie interrupt control: pola/source selection */
++ wed_set(dev, MTK_WED_PCIE_INT_CTRL,
++ MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1));
++ wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
++
++ val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
++ val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
++ wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180);
++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184);
++
++ val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
++ val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
++
++ /* pcie interrupt status trigger register */
++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
++ wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER);
++
++ /* pola setting */
++ val = wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
++ wed_set(dev, MTK_WED_PCIE_INT_CTRL, MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA);
++}
++
++static void
++mtk_wed_set_wpdma(struct mtk_wed_device *dev)
++{
++ if (dev->hw->version == 1) {
++ wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys);
++ } 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);
++ }
++}
++
+ static void
+ mtk_wed_hw_init_early(struct mtk_wed_device *dev)
+ {
+ u32 mask, set;
+- u32 offset;
+
+ mtk_wed_stop(dev);
+ mtk_wed_reset(dev, MTK_WED_RESET_WED);
++ mtk_wed_set_wpdma(dev);
+
+ mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE |
+ MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE |
+@@ -325,17 +424,33 @@ mtk_wed_hw_init_early(struct mtk_wed_dev
+ MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
+ wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set);
+
+- wdma_set(dev, MTK_WDMA_GLO_CFG,
+- MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
+- MTK_WDMA_GLO_CFG_RX_INFO2_PRERES |
+- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
+-
+- offset = dev->hw->index ? 0x04000400 : 0;
+- wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset);
+- wed_w32(dev, MTK_WED_WDMA_OFFSET1, 0x29002800 + offset);
++ if (dev->hw->version == 1) {
++ u32 offset = dev->hw->index ? 0x04000400 : 0;
+
+- wed_w32(dev, MTK_WED_PCIE_CFG_BASE, MTK_PCIE_BASE(dev->hw->index));
+- wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys);
++ wdma_set(dev, MTK_WDMA_GLO_CFG,
++ MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES |
++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
++
++ wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset);
++ wed_w32(dev, MTK_WED_WDMA_OFFSET1, 0x29002800 + offset);
++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE,
++ MTK_PCIE_BASE(dev->hw->index));
++ } else {
++ wed_w32(dev, MTK_WED_WDMA_CFG_BASE, dev->hw->wdma_phy);
++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_ETH_DMAD_FMT);
++ wed_w32(dev, MTK_WED_WDMA_OFFSET0,
++ FIELD_PREP(MTK_WED_WDMA_OFST0_GLO_INTS,
++ MTK_WDMA_INT_STATUS) |
++ FIELD_PREP(MTK_WED_WDMA_OFST0_GLO_CFG,
++ MTK_WDMA_GLO_CFG));
++
++ wed_w32(dev, MTK_WED_WDMA_OFFSET1,
++ FIELD_PREP(MTK_WED_WDMA_OFST1_TX_CTRL,
++ MTK_WDMA_RING_TX(0)) |
++ FIELD_PREP(MTK_WED_WDMA_OFST1_RX_CTRL,
++ MTK_WDMA_RING_RX(0)));
++ }
+ }
+
+ static void
+@@ -355,37 +470,65 @@ mtk_wed_hw_init(struct mtk_wed_device *d
+
+ wed_w32(dev, MTK_WED_TX_BM_BASE, dev->buf_ring.desc_phys);
+
+- wed_w32(dev, MTK_WED_TX_BM_TKID,
+- FIELD_PREP(MTK_WED_TX_BM_TKID_START,
+- dev->wlan.token_start) |
+- FIELD_PREP(MTK_WED_TX_BM_TKID_END,
+- dev->wlan.token_start + dev->wlan.nbuf - 1));
+-
+ wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
+
+- wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
+- FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) |
+- MTK_WED_TX_BM_DYN_THR_HI);
++ if (dev->hw->version == 1) {
++ wed_w32(dev, MTK_WED_TX_BM_TKID,
++ FIELD_PREP(MTK_WED_TX_BM_TKID_START,
++ dev->wlan.token_start) |
++ FIELD_PREP(MTK_WED_TX_BM_TKID_END,
++ dev->wlan.token_start +
++ dev->wlan.nbuf - 1));
++ wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
++ FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) |
++ MTK_WED_TX_BM_DYN_THR_HI);
++ } else {
++ wed_w32(dev, MTK_WED_TX_BM_TKID_V2,
++ FIELD_PREP(MTK_WED_TX_BM_TKID_START,
++ dev->wlan.token_start) |
++ FIELD_PREP(MTK_WED_TX_BM_TKID_END,
++ dev->wlan.token_start +
++ dev->wlan.nbuf - 1));
++ wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
++ FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) |
++ MTK_WED_TX_BM_DYN_THR_HI_V2);
++ 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) |
++ FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM,
++ dev->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);
++ }
+
+ mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
+
+- wed_set(dev, MTK_WED_CTRL,
+- MTK_WED_CTRL_WED_TX_BM_EN |
+- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
++ 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
++ wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
+
+ wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE);
+ }
+
+ static void
+-mtk_wed_ring_reset(struct mtk_wdma_desc *desc, int size)
++mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size)
+ {
++ void *head = (void *)ring->desc;
+ int i;
+
+ for (i = 0; i < size; i++) {
+- desc[i].buf0 = 0;
+- desc[i].ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
+- desc[i].buf1 = 0;
+- desc[i].info = 0;
++ struct mtk_wdma_desc *desc;
++
++ desc = (struct mtk_wdma_desc *)(head + i * ring->desc_size);
++ desc->buf0 = 0;
++ desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
++ desc->buf1 = 0;
++ desc->info = 0;
+ }
+ }
+
+@@ -436,12 +579,10 @@ mtk_wed_reset_dma(struct mtk_wed_device
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++) {
+- struct mtk_wdma_desc *desc = dev->tx_ring[i].desc;
+-
+- if (!desc)
++ if (!dev->tx_ring[i].desc)
+ continue;
+
+- mtk_wed_ring_reset(desc, MTK_WED_TX_RING_SIZE);
++ mtk_wed_ring_reset(&dev->tx_ring[i], MTK_WED_TX_RING_SIZE);
+ }
+
+ if (mtk_wed_poll_busy(dev))
+@@ -498,16 +639,16 @@ mtk_wed_reset_dma(struct mtk_wed_device
+
+ static int
+ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring,
+- int size)
++ int size, u32 desc_size)
+ {
+- ring->desc = dma_alloc_coherent(dev->hw->dev,
+- size * sizeof(*ring->desc),
++ ring->desc = dma_alloc_coherent(dev->hw->dev, size * desc_size,
+ &ring->desc_phys, GFP_KERNEL);
+ if (!ring->desc)
+ return -ENOMEM;
+
++ ring->desc_size = desc_size;
+ ring->size = size;
+- mtk_wed_ring_reset(ring->desc, size);
++ mtk_wed_ring_reset(ring, size);
+
+ return 0;
+ }
+@@ -515,9 +656,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)
+ {
++ u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
+ struct mtk_wed_ring *wdma = &dev->tx_wdma[idx];
+
+- if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE))
++ if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size))
+ return -ENOMEM;
+
+ wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
+@@ -546,16 +688,41 @@ mtk_wed_configure_irq(struct mtk_wed_dev
+ MTK_WED_CTRL_WED_TX_BM_EN |
+ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+
+- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER,
+- MTK_WED_PCIE_INT_TRIGGER_STATUS);
++ if (dev->hw->version == 1) {
++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER,
++ MTK_WED_PCIE_INT_TRIGGER_STATUS);
++
++ wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER,
++ MTK_WED_WPDMA_INT_TRIGGER_RX_DONE |
++ MTK_WED_WPDMA_INT_TRIGGER_TX_DONE);
+
+- wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER,
+- MTK_WED_WPDMA_INT_TRIGGER_RX_DONE |
+- MTK_WED_WPDMA_INT_TRIGGER_TX_DONE);
++ wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
++ } else {
++ /* initail tx interrupt trigger */
++ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
++ MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
++ MTK_WED_WPDMA_INT_CTRL_TX0_DONE_CLR |
++ MTK_WED_WPDMA_INT_CTRL_TX1_DONE_EN |
++ MTK_WED_WPDMA_INT_CTRL_TX1_DONE_CLR |
++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX0_DONE_TRIG,
++ dev->wlan.tx_tbit[0]) |
++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX1_DONE_TRIG,
++ dev->wlan.tx_tbit[1]));
++
++ /* initail txfree interrupt trigger */
++ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX_FREE,
++ MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN |
++ MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_CLR |
++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG,
++ dev->wlan.txfree_tbit));
++
++ 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,
++ dev->wdma_idx));
++ }
+
+- /* initail wdma interrupt agent */
+ wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, wdma_mask);
+- wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
+
+ wdma_w32(dev, MTK_WDMA_INT_MASK, wdma_mask);
+ wdma_w32(dev, MTK_WDMA_INT_GRP2, wdma_mask);
+@@ -580,14 +747,28 @@ mtk_wed_dma_enable(struct mtk_wed_device
+ wdma_set(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 |
+- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
++
++ if (dev->hw->version == 1) {
++ wdma_set(dev, MTK_WDMA_GLO_CFG,
++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
++ } else {
++ wed_set(dev, MTK_WED_WPDMA_CTRL,
++ MTK_WED_WPDMA_CTRL_SDL1_FIXED);
++
++ 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);
++
++ 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);
++ }
+ }
+
+ static void
+ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+ {
+- u32 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
+@@ -598,14 +779,17 @@ mtk_wed_start(struct mtk_wed_device *dev
+ mtk_wed_configure_irq(dev, irq_mask);
+
+ mtk_wed_set_ext_int(dev, true);
+- val = dev->wlan.wpdma_phys |
+- MTK_PCIE_MIRROR_MAP_EN |
+- FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, dev->hw->index);
+-
+- if (dev->hw->index)
+- val |= BIT(1);
+- val |= BIT(0);
+- regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
++
++ if (dev->hw->version == 1) {
++ u32 val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN |
++ FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID,
++ dev->hw->index);
++
++ 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);
++ }
+
+ mtk_wed_dma_enable(dev);
+ dev->running = true;
+@@ -639,7 +823,9 @@ mtk_wed_attach(struct mtk_wed_device *de
+ goto out;
+ }
+
+- dev_info(&dev->wlan.pci_dev->dev, "attaching wed device %d\n", hw->index);
++ dev_info(&dev->wlan.pci_dev->dev,
++ "attaching wed device %d version %d\n",
++ hw->index, hw->version);
+
+ dev->hw = hw;
+ dev->dev = hw->dev;
+@@ -657,7 +843,9 @@ mtk_wed_attach(struct mtk_wed_device *de
+ }
+
+ mtk_wed_hw_init_early(dev);
+- regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, BIT(hw->index), 0);
++ if (hw->hifsys)
++ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
++ BIT(hw->index), 0);
+
+ out:
+ mutex_unlock(&hw_lock);
+@@ -684,7 +872,8 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
+
+ BUG_ON(idx >= ARRAY_SIZE(dev->tx_ring));
+
+- if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE))
++ if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE,
++ sizeof(*ring->desc)))
+ return -ENOMEM;
+
+ if (mtk_wed_wdma_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
+@@ -711,21 +900,21 @@ static int
+ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
+ {
+ struct mtk_wed_ring *ring = &dev->txfree_ring;
+- int i;
++ int i, index = dev->hw->version == 1;
+
+ /*
+ * For txfree event handling, the same DMA ring is shared between WED
+ * and WLAN. The WLAN driver accesses the ring index registers through
+ * WED
+ */
+- ring->reg_base = MTK_WED_RING_RX(1);
++ ring->reg_base = MTK_WED_RING_RX(index);
+ ring->wpdma = regs;
+
+ for (i = 0; i < 12; i += 4) {
+ u32 val = readl(regs + i);
+
+- wed_w32(dev, MTK_WED_RING_RX(1) + i, val);
+- wed_w32(dev, MTK_WED_WPDMA_RING_RX(1) + i, val);
++ wed_w32(dev, MTK_WED_RING_RX(index) + i, val);
++ wed_w32(dev, MTK_WED_WPDMA_RING_RX(index) + i, val);
+ }
+
+ return 0;
+@@ -734,11 +923,19 @@ mtk_wed_txfree_ring_setup(struct mtk_wed
+ static u32
+ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
+ {
+- u32 val;
++ u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
++
++ if (dev->hw->version == 1)
++ ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
++ else
++ ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
++ MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH |
++ MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
++ MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR;
+
+ val = wed_r32(dev, MTK_WED_EXT_INT_STATUS);
+ wed_w32(dev, MTK_WED_EXT_INT_STATUS, val);
+- val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK;
++ val &= ext_mask;
+ if (!dev->hw->num_flows)
+ val &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
+ if (val && net_ratelimit())
+@@ -813,7 +1010,8 @@ out:
+ }
+
+ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+- void __iomem *wdma, int index)
++ void __iomem *wdma, phys_addr_t wdma_phy,
++ int index)
+ {
+ static const struct mtk_wed_ops wed_ops = {
+ .attach = mtk_wed_attach,
+@@ -860,26 +1058,33 @@ void mtk_wed_add_hw(struct device_node *
+ hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+ if (!hw)
+ goto unlock;
++
+ hw->node = np;
+ hw->regs = regs;
+ hw->eth = eth;
+ hw->dev = &pdev->dev;
++ hw->wdma_phy = wdma_phy;
+ hw->wdma = wdma;
+ hw->index = index;
+ hw->irq = irq;
+- hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
+- "mediatek,pcie-mirror");
+- hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np,
+- "mediatek,hifsys");
+- if (IS_ERR(hw->mirror) || IS_ERR(hw->hifsys)) {
+- kfree(hw);
+- goto unlock;
+- }
++ hw->version = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1;
+
+- if (!index) {
+- regmap_write(hw->mirror, 0, 0);
+- regmap_write(hw->mirror, 4, 0);
++ if (hw->version == 1) {
++ hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
++ "mediatek,pcie-mirror");
++ hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np,
++ "mediatek,hifsys");
++ if (IS_ERR(hw->mirror) || IS_ERR(hw->hifsys)) {
++ kfree(hw);
++ goto unlock;
++ }
++
++ if (!index) {
++ regmap_write(hw->mirror, 0, 0);
++ regmap_write(hw->mirror, 4, 0);
++ }
+ }
++
+ mtk_wed_hw_add_debugfs(hw);
+
+ hw_list[index] = hw;
+--- a/drivers/net/ethernet/mediatek/mtk_wed.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
+@@ -18,11 +18,13 @@ struct mtk_wed_hw {
+ struct regmap *hifsys;
+ struct device *dev;
+ void __iomem *wdma;
++ phys_addr_t wdma_phy;
+ struct regmap *mirror;
+ struct dentry *debugfs_dir;
+ struct mtk_wed_device *wed_dev;
+ u32 debugfs_reg;
+ u32 num_flows;
++ u8 version;
+ char dirname[5];
+ int irq;
+ int index;
+@@ -101,14 +103,16 @@ wpdma_txfree_w32(struct mtk_wed_device *
+ }
+
+ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+- void __iomem *wdma, int index);
++ void __iomem *wdma, phys_addr_t wdma_phy,
++ int index);
+ void mtk_wed_exit(void);
+ int mtk_wed_flow_add(int index);
+ void mtk_wed_flow_remove(int index);
+ #else
+ static inline void
+ mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+- void __iomem *wdma, int index)
++ void __iomem *wdma, phys_addr_t wdma_phy,
++ int index)
+ {
+ }
+ static inline void
+--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
+@@ -116,6 +116,9 @@ wed_txinfo_show(struct seq_file *s, void
+ DUMP_WDMA(WDMA_GLO_CFG),
+ DUMP_WDMA_RING(WDMA_RING_RX(0)),
+ DUMP_WDMA_RING(WDMA_RING_RX(1)),
++
++ DUMP_STR("TX FREE"),
++ DUMP_WED(WED_RX_MIB(0)),
+ };
+ struct mtk_wed_hw *hw = s->private;
+ struct mtk_wed_device *dev = hw->wed_dev;
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -5,6 +5,7 @@
+ #define __MTK_WED_REGS_H
+
+ #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)
+ #define MTK_WDMA_DESC_CTRL_BURST BIT(16)
+ #define MTK_WDMA_DESC_CTRL_LEN0 GENMASK(29, 16)
+@@ -41,6 +42,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_CTRL_RESERVE_EN BIT(12)
+ #define MTK_WED_CTRL_RESERVE_BUSY BIT(13)
+ #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)
+
+ #define MTK_WED_EXT_INT_STATUS 0x020
+@@ -57,7 +59,8 @@ struct mtk_wdma_desc {
+ #define MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN BIT(19)
+ #define MTK_WED_EXT_INT_STATUS_RX_DRV_BM_DMAD_COHERENT BIT(20)
+ #define MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR BIT(21)
+-#define MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR BIT(22)
++#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_ERROR_MASK (MTK_WED_EXT_INT_STATUS_TF_LEN_ERR | \
+ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD | \
+@@ -65,8 +68,7 @@ struct mtk_wdma_desc {
+ MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR | \
+ MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR | \
+ MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN | \
+- MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR | \
+- MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR)
++ MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR)
+
+ #define MTK_WED_EXT_INT_MASK 0x028
+
+@@ -81,6 +83,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_TX_BM_BASE 0x084
+
+ #define MTK_WED_TX_BM_TKID 0x088
++#define MTK_WED_TX_BM_TKID_V2 0x0c8
+ #define MTK_WED_TX_BM_TKID_START GENMASK(15, 0)
+ #define MTK_WED_TX_BM_TKID_END GENMASK(31, 16)
+
+@@ -94,7 +97,25 @@ struct mtk_wdma_desc {
+
+ #define MTK_WED_TX_BM_DYN_THR 0x0a0
+ #define MTK_WED_TX_BM_DYN_THR_LO GENMASK(6, 0)
++#define MTK_WED_TX_BM_DYN_THR_LO_V2 GENMASK(8, 0)
+ #define MTK_WED_TX_BM_DYN_THR_HI GENMASK(22, 16)
++#define MTK_WED_TX_BM_DYN_THR_HI_V2 GENMASK(24, 16)
++
++#define MTK_WED_TX_TKID_CTRL 0x0c0
++#define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM GENMASK(6, 0)
++#define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM GENMASK(22, 16)
++#define MTK_WED_TX_TKID_CTRL_PAUSE BIT(28)
++
++#define MTK_WED_TX_TKID_DYN_THR 0x0e0
++#define MTK_WED_TX_TKID_DYN_THR_LO GENMASK(6, 0)
++#define MTK_WED_TX_TKID_DYN_THR_HI GENMASK(22, 16)
++
++#define MTK_WED_TXP_DW0 0x120
++#define MTK_WED_TXP_DW1 0x124
++#define MTK_WED_WPDMA_WRITE_TXP GENMASK(31, 16)
++#define MTK_WED_TXDP_CTRL 0x130
++#define MTK_WED_TXDP_DW9_OVERWR BIT(9)
++#define MTK_WED_RX_BM_TKID_MIB 0x1cc
+
+ #define MTK_WED_INT_STATUS 0x200
+ #define MTK_WED_INT_MASK 0x204
+@@ -125,6 +146,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RESET_IDX_RX GENMASK(17, 16)
+
+ #define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4)
++#define MTK_WED_RX_MIB(_n) (0x2e0 + (_n) * 4)
+
+ #define MTK_WED_RING_TX(_n) (0x300 + (_n) * 0x10)
+
+@@ -155,21 +177,62 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_GLO_CFG_BYTE_SWAP BIT(29)
+ #define MTK_WED_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31)
+
++/* CONFIG_MEDIATEK_NETSYS_V2 */
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC BIT(4)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_PKT_PROC BIT(5)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC BIT(6)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_CRX_SYNC BIT(7)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER GENMASK(18, 16)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNSUPPORT_FMT BIT(19)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UEVENT_PKT_FMT_CHK BIT(20)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR BIT(21)
++#define MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP BIT(24)
++#define MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV BIT(28)
++
+ #define MTK_WED_WPDMA_RESET_IDX 0x50c
+ #define MTK_WED_WPDMA_RESET_IDX_TX GENMASK(3, 0)
+ #define MTK_WED_WPDMA_RESET_IDX_RX GENMASK(17, 16)
+
++#define MTK_WED_WPDMA_CTRL 0x518
++#define MTK_WED_WPDMA_CTRL_SDL1_FIXED BIT(31)
++
+ #define MTK_WED_WPDMA_INT_CTRL 0x520
+ #define MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV BIT(21)
+
+ #define MTK_WED_WPDMA_INT_MASK 0x524
+
++#define MTK_WED_WPDMA_INT_CTRL_TX 0x530
++#define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN BIT(0)
++#define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_CLR BIT(1)
++#define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_TRIG GENMASK(6, 2)
++#define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_EN BIT(8)
++#define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_CLR BIT(9)
++#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_TX_FREE 0x538
++#define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN BIT(0)
++#define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_CLR BIT(1)
++#define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG GENMASK(6, 2)
++
+ #define MTK_WED_PCIE_CFG_BASE 0x560
+
++#define MTK_WED_PCIE_CFG_BASE 0x560
++#define MTK_WED_PCIE_CFG_INTM 0x564
++#define MTK_WED_PCIE_CFG_MSIS 0x568
+ #define MTK_WED_PCIE_INT_TRIGGER 0x570
+ #define MTK_WED_PCIE_INT_TRIGGER_STATUS BIT(16)
+
++#define MTK_WED_PCIE_INT_CTRL 0x57c
++#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA BIT(20)
++#define MTK_WED_PCIE_INT_CTRL_SRC_SEL GENMASK(17, 16)
++#define MTK_WED_PCIE_INT_CTRL_POLL_EN GENMASK(13, 12)
++
+ #define MTK_WED_WPDMA_CFG_BASE 0x580
++#define MTK_WED_WPDMA_CFG_INT_MASK 0x584
++#define MTK_WED_WPDMA_CFG_TX 0x588
++#define MTK_WED_WPDMA_CFG_TX_FREE 0x58c
+
+ #define MTK_WED_WPDMA_TX_MIB(_n) (0x5a0 + (_n) * 4)
+ #define MTK_WED_WPDMA_TX_COHERENT_MIB(_n) (0x5d0 + (_n) * 4)
+@@ -203,15 +266,24 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WDMA_RESET_IDX_RX GENMASK(17, 16)
+ #define MTK_WED_WDMA_RESET_IDX_DRV GENMASK(25, 24)
+
++#define MTK_WED_WDMA_INT_CLR 0xa24
++#define MTK_WED_WDMA_INT_CLR_RX_DONE GENMASK(17, 16)
++
+ #define MTK_WED_WDMA_INT_TRIGGER 0xa28
+ #define MTK_WED_WDMA_INT_TRIGGER_RX_DONE GENMASK(17, 16)
+
+ #define MTK_WED_WDMA_INT_CTRL 0xa2c
+ #define MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL GENMASK(17, 16)
+
++#define MTK_WED_WDMA_CFG_BASE 0xaa0
+ #define MTK_WED_WDMA_OFFSET0 0xaa4
+ #define MTK_WED_WDMA_OFFSET1 0xaa8
+
++#define MTK_WED_WDMA_OFST0_GLO_INTS GENMASK(15, 0)
++#define MTK_WED_WDMA_OFST0_GLO_CFG GENMASK(31, 16)
++#define MTK_WED_WDMA_OFST1_TX_CTRL GENMASK(15, 0)
++#define MTK_WED_WDMA_OFST1_RX_CTRL GENMASK(31, 16)
++
+ #define MTK_WED_WDMA_RX_MIB(_n) (0xae0 + (_n) * 4)
+ #define MTK_WED_WDMA_RX_RECYCLE_MIB(_n) (0xae8 + (_n) * 4)
+ #define MTK_WED_WDMA_RX_PROCESSED_MIB(_n) (0xaf0 + (_n) * 4)
+@@ -221,6 +293,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RING_OFS_CPU_IDX 0x08
+ #define MTK_WED_RING_OFS_DMA_IDX 0x0c
+
++#define MTK_WDMA_RING_TX(_n) (0x000 + (_n) * 0x10)
+ #define MTK_WDMA_RING_RX(_n) (0x100 + (_n) * 0x10)
+
+ #define MTK_WDMA_GLO_CFG 0x204
+@@ -234,6 +307,8 @@ struct mtk_wdma_desc {
+ #define MTK_WDMA_RESET_IDX_TX GENMASK(3, 0)
+ #define MTK_WDMA_RESET_IDX_RX GENMASK(17, 16)
+
++#define MTK_WDMA_INT_STATUS 0x220
++
+ #define MTK_WDMA_INT_MASK 0x228
+ #define MTK_WDMA_INT_MASK_TX_DONE GENMASK(3, 0)
+ #define MTK_WDMA_INT_MASK_RX_DONE GENMASK(17, 16)
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -14,6 +14,7 @@ struct mtk_wdma_desc;
+ struct mtk_wed_ring {
+ struct mtk_wdma_desc *desc;
+ dma_addr_t desc_phys;
++ u32 desc_size;
+ int size;
+
+ u32 reg_base;
+@@ -45,10 +46,17 @@ struct mtk_wed_device {
+ struct pci_dev *pci_dev;
+
+ u32 wpdma_phys;
++ u32 wpdma_int;
++ u32 wpdma_mask;
++ u32 wpdma_tx;
++ u32 wpdma_txfree;
+
+ u16 token_start;
+ unsigned int nbuf;
+
++ u8 tx_tbit[MTK_WED_TX_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);
diff --git a/target/linux/generic/backport-6.1/722-v6.0-net-ethernet-mtk_eth_wed-add-axi-bus-support.patch b/target/linux/generic/backport-6.1/722-v6.0-net-ethernet-mtk_eth_wed-add-axi-bus-support.patch
new file mode 100644
index 0000000000..70b7bad31f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/722-v6.0-net-ethernet-mtk_eth_wed-add-axi-bus-support.patch
@@ -0,0 +1,237 @@
+From 6e1df49f330dce7c58a39d6772f1385b6887bb03 Mon Sep 17 00:00:00 2001
+Message-Id: <6e1df49f330dce7c58a39d6772f1385b6887bb03.1662990860.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 8 Sep 2022 11:26:10 +0200
+Subject: [PATCH net-next] net: ethernet: mtk_eth_wed: add axi bus support
+
+Other than pcie bus, introduce support for axi bus to mtk wed driver.
+Axi bus is used to connect mt7986-wmac soc chip available on mt7986
+device.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+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>
+---
+ drivers/net/ethernet/mediatek/mtk_wed.c | 116 +++++++++++++------
+ drivers/net/ethernet/mediatek/mtk_wed_regs.h | 2 +
+ include/linux/soc/mediatek/mtk_wed.h | 11 +-
+ 3 files changed, 91 insertions(+), 38 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -85,11 +85,31 @@ static struct mtk_wed_hw *
+ mtk_wed_assign(struct mtk_wed_device *dev)
+ {
+ struct mtk_wed_hw *hw;
++ int i;
++
++ if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
++ hw = hw_list[pci_domain_nr(dev->wlan.pci_dev->bus)];
++ if (!hw)
++ return NULL;
++
++ if (!hw->wed_dev)
++ goto out;
++
++ if (hw->version == 1)
++ return NULL;
++
++ /* MT7986 WED devices do not have any pcie slot restrictions */
++ }
++ /* MT7986 PCIE or AXI */
++ for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
++ hw = hw_list[i];
++ if (hw && !hw->wed_dev)
++ goto out;
++ }
+
+- hw = hw_list[pci_domain_nr(dev->wlan.pci_dev->bus)];
+- if (!hw || hw->wed_dev)
+- return NULL;
++ return NULL;
+
++out:
+ hw->wed_dev = dev;
+ return hw;
+ }
+@@ -322,7 +342,6 @@ mtk_wed_stop(struct mtk_wed_device *dev)
+ static void
+ mtk_wed_detach(struct mtk_wed_device *dev)
+ {
+- struct device_node *wlan_node = dev->wlan.pci_dev->dev.of_node;
+ struct mtk_wed_hw *hw = dev->hw;
+
+ mutex_lock(&hw_lock);
+@@ -337,9 +356,14 @@ mtk_wed_detach(struct mtk_wed_device *de
+ mtk_wed_free_buffer(dev);
+ mtk_wed_free_tx_rings(dev);
+
+- if (of_dma_is_coherent(wlan_node) && hw->hifsys)
+- regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
+- BIT(hw->index), BIT(hw->index));
++ if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
++ struct device_node *wlan_node;
++
++ wlan_node = dev->wlan.pci_dev->dev.of_node;
++ if (of_dma_is_coherent(wlan_node) && hw->hifsys)
++ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
++ BIT(hw->index), BIT(hw->index));
++ }
+
+ if (!hw_list[!hw->index]->wed_dev &&
+ hw->eth->dma_dev != hw->eth->dev)
+@@ -356,40 +380,54 @@ mtk_wed_detach(struct mtk_wed_device *de
+ static void
+ mtk_wed_bus_init(struct mtk_wed_device *dev)
+ {
+- struct device_node *np = dev->hw->eth->dev->of_node;
+- struct regmap *regs;
+- u32 val;
+-
+- regs = syscon_regmap_lookup_by_phandle(np, "mediatek,wed-pcie");
+- if (IS_ERR(regs))
+- return;
++ switch (dev->wlan.bus_type) {
++ case MTK_WED_BUS_PCIE: {
++ struct device_node *np = dev->hw->eth->dev->of_node;
++ struct regmap *regs;
++ u32 val;
++
++ regs = syscon_regmap_lookup_by_phandle(np,
++ "mediatek,wed-pcie");
++ if (IS_ERR(regs))
++ break;
+
+- regmap_update_bits(regs, 0, BIT(0), BIT(0));
++ regmap_update_bits(regs, 0, BIT(0), BIT(0));
+
+- wed_w32(dev, MTK_WED_PCIE_INT_CTRL,
+- FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2));
++ wed_w32(dev, MTK_WED_PCIE_INT_CTRL,
++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2));
+
+- /* pcie interrupt control: pola/source selection */
+- wed_set(dev, MTK_WED_PCIE_INT_CTRL,
+- MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
+- FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1));
+- wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
+-
+- val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
+- val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
+- wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180);
+- wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184);
+-
+- val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
+- val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
+-
+- /* pcie interrupt status trigger register */
+- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
+- wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER);
+-
+- /* pola setting */
+- val = wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
+- wed_set(dev, MTK_WED_PCIE_INT_CTRL, MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA);
++ /* pcie interrupt control: pola/source selection */
++ wed_set(dev, MTK_WED_PCIE_INT_CTRL,
++ MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1));
++ wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
++
++ val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
++ val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
++ wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180);
++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184);
++
++ val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
++ val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
++
++ /* pcie interrupt status trigger register */
++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
++ wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER);
++
++ /* pola setting */
++ val = wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
++ wed_set(dev, MTK_WED_PCIE_INT_CTRL,
++ MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA);
++ break;
++ }
++ case MTK_WED_BUS_AXI:
++ wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
++ MTK_WED_WPDMA_INT_CTRL_SIG_SRC |
++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_SRC_SEL, 0));
++ break;
++ default:
++ break;
++ }
+ }
+
+ static void
+@@ -800,12 +838,14 @@ mtk_wed_attach(struct mtk_wed_device *de
+ __releases(RCU)
+ {
+ struct mtk_wed_hw *hw;
++ struct device *device;
+ int ret = 0;
+
+ RCU_LOCKDEP_WARN(!rcu_read_lock_held(),
+ "mtk_wed_attach without holding the RCU read lock");
+
+- if (pci_domain_nr(dev->wlan.pci_dev->bus) > 1 ||
++ if ((dev->wlan.bus_type == MTK_WED_BUS_PCIE &&
++ pci_domain_nr(dev->wlan.pci_dev->bus) > 1) ||
+ !try_module_get(THIS_MODULE))
+ ret = -ENODEV;
+
+@@ -823,8 +863,10 @@ mtk_wed_attach(struct mtk_wed_device *de
+ goto out;
+ }
+
+- dev_info(&dev->wlan.pci_dev->dev,
+- "attaching wed device %d version %d\n",
++ device = dev->wlan.bus_type == MTK_WED_BUS_PCIE
++ ? &dev->wlan.pci_dev->dev
++ : &dev->wlan.platform_dev->dev;
++ dev_info(device, "attaching wed device %d version %d\n",
+ hw->index, hw->version);
+
+ dev->hw = hw;
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -198,6 +198,8 @@ struct mtk_wdma_desc {
+
+ #define MTK_WED_WPDMA_INT_CTRL 0x520
+ #define MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV BIT(21)
++#define MTK_WED_WPDMA_INT_CTRL_SIG_SRC BIT(22)
++#define MTK_WED_WPDMA_INT_CTRL_SRC_SEL GENMASK(17, 16)
+
+ #define MTK_WED_WPDMA_INT_MASK 0x524
+
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -11,6 +11,11 @@
+ struct mtk_wed_hw;
+ struct mtk_wdma_desc;
+
++enum mtk_wed_bus_tye {
++ MTK_WED_BUS_PCIE,
++ MTK_WED_BUS_AXI,
++};
++
+ struct mtk_wed_ring {
+ struct mtk_wdma_desc *desc;
+ dma_addr_t desc_phys;
+@@ -43,7 +48,11 @@ struct mtk_wed_device {
+
+ /* filled by driver: */
+ struct {
+- struct pci_dev *pci_dev;
++ union {
++ struct platform_device *platform_dev;
++ struct pci_dev *pci_dev;
++ };
++ enum mtk_wed_bus_tye bus_type;
+
+ u32 wpdma_phys;
+ u32 wpdma_int;
diff --git a/target/linux/generic/backport-6.1/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch b/target/linux/generic/backport-6.1/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch
new file mode 100644
index 0000000000..fedcb6ccd8
--- /dev/null
+++ b/target/linux/generic/backport-6.1/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch
@@ -0,0 +1,882 @@
+From 93408c858e5dc01d97c55efa721268f63fde2ae5 Mon Sep 17 00:00:00 2001
+Message-Id: <93408c858e5dc01d97c55efa721268f63fde2ae5.1662886034.git.lorenzo@kernel.org>
+In-Reply-To: <e5ecb4f619197b93fa682d722452dc8412864cdb.1662886033.git.lorenzo@kernel.org>
+References: <e5ecb4f619197b93fa682d722452dc8412864cdb.1662886033.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 3 Sep 2022 18:34:09 +0200
+Subject: [PATCH net-next 4/5] net: ethernet: mtk_eth_soc: introduce flow
+ offloading support for mt7986
+
+Introduce hw flow offload support for mt7986 chipset. PPE is not enabled
+yet in mt7986 since mt76 support is not available yet.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+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>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 11 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 72 ++++++
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 213 +++++++++++-------
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 52 ++++-
+ .../net/ethernet/mediatek/mtk_ppe_offload.c | 49 ++--
+ drivers/net/ethernet/mediatek/mtk_ppe_regs.h | 8 +
+ 6 files changed, 289 insertions(+), 116 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1859,12 +1859,14 @@ static int mtk_poll_rx(struct napi_struc
+ bytes += skb->len;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON, trxd.rxd5);
+ hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY;
+ if (hash != MTK_RXD5_FOE_ENTRY)
+ skb_set_hash(skb, jhash_1word(hash, 0),
+ PKT_HASH_TYPE_L4);
+ rxdcsum = &trxd.rxd3;
+ } else {
++ reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4);
+ hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY;
+ if (hash != MTK_RXD4_FOE_ENTRY)
+ skb_set_hash(skb, jhash_1word(hash, 0),
+@@ -1878,7 +1880,6 @@ static int mtk_poll_rx(struct napi_struc
+ skb_checksum_none_assert(skb);
+ skb->protocol = eth_type_trans(skb, netdev);
+
+- reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4);
+ if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
+ mtk_ppe_check_skb(eth->ppe[0], skb, hash);
+
+@@ -4184,7 +4185,7 @@ static const struct mtk_soc_data mt7621_
+ .required_pctl = false,
+ .offload_version = 2,
+ .hash_offset = 2,
+- .foe_entry_size = sizeof(struct mtk_foe_entry),
++ .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4204,7 +4205,7 @@ static const struct mtk_soc_data mt7622_
+ .required_pctl = false,
+ .offload_version = 2,
+ .hash_offset = 2,
+- .foe_entry_size = sizeof(struct mtk_foe_entry),
++ .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4223,7 +4224,7 @@ static const struct mtk_soc_data mt7623_
+ .required_pctl = true,
+ .offload_version = 2,
+ .hash_offset = 2,
+- .foe_entry_size = sizeof(struct mtk_foe_entry),
++ .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
+ .txrx = {
+ .txd_size = sizeof(struct mtk_tx_dma),
+ .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4255,9 +4256,11 @@ static const struct mtk_soc_data mt7986_
+ .reg_map = &mt7986_reg_map,
+ .ana_rgc3 = 0x128,
+ .caps = MT7986_CAPS,
++ .hw_features = MTK_HW_FEATURES,
+ .required_clks = MT7986_CLKS_BITMAP,
+ .required_pctl = false,
+ .hash_offset = 4,
++ .foe_entry_size = sizeof(struct mtk_foe_entry),
+ .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
+@@ -1151,6 +1151,78 @@ mtk_foe_get_entry(struct mtk_ppe *ppe, u
+ return ppe->foe_table + hash * soc->foe_entry_size;
+ }
+
++static inline u32 mtk_get_ib1_ts_mask(struct mtk_eth *eth)
++{
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ return MTK_FOE_IB1_BIND_TIMESTAMP_V2;
++
++ return MTK_FOE_IB1_BIND_TIMESTAMP;
++}
++
++static inline u32 mtk_get_ib1_ppoe_mask(struct mtk_eth *eth)
++{
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ return MTK_FOE_IB1_BIND_PPPOE_V2;
++
++ return MTK_FOE_IB1_BIND_PPPOE;
++}
++
++static inline u32 mtk_get_ib1_vlan_tag_mask(struct mtk_eth *eth)
++{
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ return MTK_FOE_IB1_BIND_VLAN_TAG_V2;
++
++ return MTK_FOE_IB1_BIND_VLAN_TAG;
++}
++
++static inline u32 mtk_get_ib1_vlan_layer_mask(struct mtk_eth *eth)
++{
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ return MTK_FOE_IB1_BIND_VLAN_LAYER_V2;
++
++ return MTK_FOE_IB1_BIND_VLAN_LAYER;
++}
++
++static inline u32 mtk_prep_ib1_vlan_layer(struct mtk_eth *eth, u32 val)
++{
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ return FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER_V2, val);
++
++ return FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, val);
++}
++
++static inline u32 mtk_get_ib1_vlan_layer(struct mtk_eth *eth, u32 val)
++{
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ return FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER_V2, val);
++
++ return FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, val);
++}
++
++static inline u32 mtk_get_ib1_pkt_type_mask(struct mtk_eth *eth)
++{
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ return MTK_FOE_IB1_PACKET_TYPE_V2;
++
++ return MTK_FOE_IB1_PACKET_TYPE;
++}
++
++static inline u32 mtk_get_ib1_pkt_type(struct mtk_eth *eth, u32 val)
++{
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ return FIELD_GET(MTK_FOE_IB1_PACKET_TYPE_V2, val);
++
++ return FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, val);
++}
++
++static inline u32 mtk_get_ib2_multicast_mask(struct mtk_eth *eth)
++{
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ return MTK_FOE_IB2_MULTICAST_V2;
++
++ return MTK_FOE_IB2_MULTICAST;
++}
++
+ /* read the hardware status register */
+ void mtk_stats_update_mac(struct mtk_mac *mac);
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -56,7 +56,7 @@ static u32 ppe_clear(struct mtk_ppe *ppe
+
+ static u32 mtk_eth_timestamp(struct mtk_eth *eth)
+ {
+- return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
++ return mtk_r32(eth, 0x0010) & mtk_get_ib1_ts_mask(eth);
+ }
+
+ static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
+@@ -93,7 +93,7 @@ static u32 mtk_ppe_hash_entry(struct mtk
+ u32 hv1, hv2, hv3;
+ u32 hash;
+
+- switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) {
++ switch (mtk_get_ib1_pkt_type(eth, e->ib1)) {
+ case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
+ case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
+ hv1 = e->ipv4.orig.ports;
+@@ -129,9 +129,9 @@ static u32 mtk_ppe_hash_entry(struct mtk
+ }
+
+ static inline struct mtk_foe_mac_info *
+-mtk_foe_entry_l2(struct mtk_foe_entry *entry)
++mtk_foe_entry_l2(struct mtk_eth *eth, struct mtk_foe_entry *entry)
+ {
+- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
+
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
+ return &entry->bridge.l2;
+@@ -143,9 +143,9 @@ mtk_foe_entry_l2(struct mtk_foe_entry *e
+ }
+
+ static inline u32 *
+-mtk_foe_entry_ib2(struct mtk_foe_entry *entry)
++mtk_foe_entry_ib2(struct mtk_eth *eth, struct mtk_foe_entry *entry)
+ {
+- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
+
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
+ return &entry->bridge.ib2;
+@@ -156,27 +156,38 @@ mtk_foe_entry_ib2(struct mtk_foe_entry *
+ return &entry->ipv4.ib2;
+ }
+
+-int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
+- u8 pse_port, u8 *src_mac, u8 *dest_mac)
++int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ int type, int l4proto, u8 pse_port, u8 *src_mac,
++ u8 *dest_mac)
+ {
+ struct mtk_foe_mac_info *l2;
+ u32 ports_pad, val;
+
+ memset(entry, 0, sizeof(*entry));
+
+- 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) |
+- MTK_FOE_IB1_BIND_TTL |
+- MTK_FOE_IB1_BIND_CACHE;
+- entry->ib1 = val;
+-
+- val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) |
+- FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) |
+- FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port);
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) |
++ FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE_V2, type) |
++ FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) |
++ MTK_FOE_IB1_BIND_CACHE_V2 | MTK_FOE_IB1_BIND_TTL_V2;
++ entry->ib1 = val;
++
++ val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, pse_port) |
++ FIELD_PREP(MTK_FOE_IB2_PORT_AG_V2, 0xf);
++ } else {
++ 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) |
++ MTK_FOE_IB1_BIND_CACHE | MTK_FOE_IB1_BIND_TTL;
++ 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_AG, 0x1f);
++ }
+
+ if (is_multicast_ether_addr(dest_mac))
+- val |= MTK_FOE_IB2_MULTICAST;
++ val |= mtk_get_ib2_multicast_mask(eth);
+
+ ports_pad = 0xa5a5a500 | (l4proto & 0xff);
+ if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE)
+@@ -210,24 +221,30 @@ int mtk_foe_entry_prepare(struct mtk_foe
+ return 0;
+ }
+
+-int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port)
++int mtk_foe_entry_set_pse_port(struct mtk_eth *eth,
++ struct mtk_foe_entry *entry, u8 port)
+ {
+- u32 *ib2 = mtk_foe_entry_ib2(entry);
+- u32 val;
++ u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
++ u32 val = *ib2;
+
+- val = *ib2;
+- val &= ~MTK_FOE_IB2_DEST_PORT;
+- val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port);
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ val &= ~MTK_FOE_IB2_DEST_PORT_V2;
++ val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, port);
++ } else {
++ val &= ~MTK_FOE_IB2_DEST_PORT;
++ val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port);
++ }
+ *ib2 = val;
+
+ return 0;
+ }
+
+-int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress,
++int mtk_foe_entry_set_ipv4_tuple(struct mtk_eth *eth,
++ struct mtk_foe_entry *entry, bool egress,
+ __be32 src_addr, __be16 src_port,
+ __be32 dest_addr, __be16 dest_port)
+ {
+- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
+ struct mtk_ipv4_tuple *t;
+
+ switch (type) {
+@@ -262,11 +279,12 @@ int mtk_foe_entry_set_ipv4_tuple(struct
+ return 0;
+ }
+
+-int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
++int mtk_foe_entry_set_ipv6_tuple(struct mtk_eth *eth,
++ struct mtk_foe_entry *entry,
+ __be32 *src_addr, __be16 src_port,
+ __be32 *dest_addr, __be16 dest_port)
+ {
+- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
+ u32 *src, *dest;
+ int i;
+
+@@ -297,39 +315,41 @@ int mtk_foe_entry_set_ipv6_tuple(struct
+ return 0;
+ }
+
+-int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port)
++int mtk_foe_entry_set_dsa(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ int port)
+ {
+- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
+
+ l2->etype = BIT(port);
+
+- if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER))
+- entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++ if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth)))
++ entry->ib1 |= mtk_prep_ib1_vlan_layer(eth, 1);
+ else
+ l2->etype |= BIT(8);
+
+- entry->ib1 &= ~MTK_FOE_IB1_BIND_VLAN_TAG;
++ entry->ib1 &= ~mtk_get_ib1_vlan_tag_mask(eth);
+
+ return 0;
+ }
+
+-int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid)
++int mtk_foe_entry_set_vlan(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ int vid)
+ {
+- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
+
+- switch (FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)) {
++ switch (mtk_prep_ib1_vlan_layer(eth, entry->ib1)) {
+ case 0:
+- entry->ib1 |= MTK_FOE_IB1_BIND_VLAN_TAG |
+- FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++ entry->ib1 |= mtk_get_ib1_vlan_tag_mask(eth) |
++ mtk_prep_ib1_vlan_layer(eth, 1);
+ l2->vlan1 = vid;
+ return 0;
+ case 1:
+- if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) {
++ if (!(entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth))) {
+ l2->vlan1 = vid;
+ l2->etype |= BIT(8);
+ } else {
+ l2->vlan2 = vid;
+- entry->ib1 += FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1);
++ entry->ib1 += mtk_prep_ib1_vlan_layer(eth, 1);
+ }
+ return 0;
+ default:
+@@ -337,34 +357,42 @@ int mtk_foe_entry_set_vlan(struct mtk_fo
+ }
+ }
+
+-int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid)
++int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ int sid)
+ {
+- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
+
+- if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER) ||
+- (entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG))
++ if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth)) ||
++ (entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth)))
+ l2->etype = ETH_P_PPP_SES;
+
+- entry->ib1 |= MTK_FOE_IB1_BIND_PPPOE;
++ entry->ib1 |= mtk_get_ib1_ppoe_mask(eth);
+ l2->pppoe_id = sid;
+
+ return 0;
+ }
+
+-int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+- int bss, int wcid)
++int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ int wdma_idx, int txq, int bss, int wcid)
+ {
+- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
+- u32 *ib2 = mtk_foe_entry_ib2(entry);
++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
++ u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
+
+- *ib2 &= ~MTK_FOE_IB2_PORT_MG;
+- *ib2 |= MTK_FOE_IB2_WDMA_WINFO;
+- if (wdma_idx)
+- *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX;
+-
+- l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) |
+- FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) |
+- FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq);
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
++ *ib2 |= FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq) |
++ MTK_FOE_IB2_WDMA_WINFO_V2;
++ l2->winfo = FIELD_PREP(MTK_FOE_WINFO_WCID, wcid) |
++ FIELD_PREP(MTK_FOE_WINFO_BSS, bss);
++ } else {
++ *ib2 &= ~MTK_FOE_IB2_PORT_MG;
++ *ib2 |= MTK_FOE_IB2_WDMA_WINFO;
++ if (wdma_idx)
++ *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX;
++ l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) |
++ FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) |
++ FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq);
++ }
+
+ return 0;
+ }
+@@ -376,14 +404,15 @@ static inline bool mtk_foe_entry_usable(
+ }
+
+ static bool
+-mtk_flow_entry_match(struct mtk_flow_entry *entry, struct mtk_foe_entry *data)
++mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry,
++ struct mtk_foe_entry *data)
+ {
+ int type, len;
+
+ if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP)
+ return false;
+
+- type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
++ type = mtk_get_ib1_pkt_type(eth, entry->data.ib1);
+ if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
+ len = offsetof(struct mtk_foe_entry, ipv6._rsv);
+ else
+@@ -427,14 +456,12 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
+
+ static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1)
+ {
+- u16 timestamp;
+- u16 now;
+-
+- now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
+- timestamp = ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
++ u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
++ u16 now = mtk_eth_timestamp(ppe->eth);
++ u16 timestamp = ib1 & ib1_ts_mask;
+
+ if (timestamp > now)
+- return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
++ return ib1_ts_mask + 1 - timestamp + now;
+ else
+ return now - timestamp;
+ }
+@@ -442,6 +469,7 @@ static int __mtk_foe_entry_idle_time(str
+ static void
+ mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
++ u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
+ struct mtk_flow_entry *cur;
+ struct mtk_foe_entry *hwe;
+ struct hlist_node *tmp;
+@@ -466,8 +494,8 @@ mtk_flow_entry_update_l2(struct mtk_ppe
+ continue;
+
+ idle = cur_idle;
+- entry->data.ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
+- entry->data.ib1 |= hwe->ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
++ entry->data.ib1 &= ~ib1_ts_mask;
++ entry->data.ib1 |= hwe->ib1 & ib1_ts_mask;
+ }
+ }
+
+@@ -489,7 +517,7 @@ mtk_flow_entry_update(struct mtk_ppe *pp
+
+ hwe = mtk_foe_get_entry(ppe, entry->hash);
+ memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size);
+- if (!mtk_flow_entry_match(entry, &foe)) {
++ if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) {
+ entry->hash = 0xffff;
+ goto out;
+ }
+@@ -504,16 +532,22 @@ static void
+ __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
+ u16 hash)
+ {
++ struct mtk_eth *eth = ppe->eth;
++ u16 timestamp = mtk_eth_timestamp(eth);
+ struct mtk_foe_entry *hwe;
+- u16 timestamp;
+
+- timestamp = mtk_eth_timestamp(ppe->eth);
+- timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP;
+- entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
+- entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP_V2;
++ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP_V2,
++ timestamp);
++ } else {
++ entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
++ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP,
++ timestamp);
++ }
+
+ hwe = mtk_foe_get_entry(ppe, hash);
+- memcpy(&hwe->data, &entry->data, ppe->eth->soc->foe_entry_size);
++ memcpy(&hwe->data, &entry->data, eth->soc->foe_entry_size);
+ wmb();
+ hwe->ib1 = entry->ib1;
+
+@@ -540,8 +574,8 @@ mtk_foe_entry_commit_l2(struct mtk_ppe *
+
+ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
+ const struct mtk_soc_data *soc = ppe->eth->soc;
++ int type = mtk_get_ib1_pkt_type(ppe->eth, entry->data.ib1);
+ u32 hash;
+
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
+@@ -564,7 +598,7 @@ mtk_foe_entry_commit_subflow(struct mtk_
+ struct mtk_flow_entry *flow_info;
+ struct mtk_foe_entry foe = {}, *hwe;
+ struct mtk_foe_mac_info *l2;
+- u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP;
++ u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP;
+ int type;
+
+ flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end),
+@@ -584,16 +618,16 @@ mtk_foe_entry_commit_subflow(struct mtk_
+ foe.ib1 &= ib1_mask;
+ foe.ib1 |= entry->data.ib1 & ~ib1_mask;
+
+- l2 = mtk_foe_entry_l2(&foe);
++ l2 = mtk_foe_entry_l2(ppe->eth, &foe);
+ memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
+
+- type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, foe.ib1);
++ type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1);
+ if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
+ memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
+ else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
+ l2->etype = ETH_P_IPV6;
+
+- *mtk_foe_entry_ib2(&foe) = entry->data.bridge.ib2;
++ *mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2;
+
+ __mtk_foe_entry_commit(ppe, &foe, hash);
+ }
+@@ -626,7 +660,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+ continue;
+ }
+
+- if (found || !mtk_flow_entry_match(entry, hwe)) {
++ if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) {
+ if (entry->hash != 0xffff)
+ entry->hash = 0xffff;
+ continue;
+@@ -771,6 +805,8 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
+ MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) |
+ FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM,
+ MTK_PPE_ENTRIES_SHIFT);
++ if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2))
++ val |= MTK_PPE_TB_CFG_INFO_SEL;
+ ppe_w32(ppe, MTK_PPE_TB_CFG, val);
+
+ ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK,
+@@ -778,15 +814,21 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
+
+ mtk_ppe_cache_enable(ppe, true);
+
+- val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG |
+- MTK_PPE_FLOW_CFG_IP4_UDP_FRAG |
+- MTK_PPE_FLOW_CFG_IP6_3T_ROUTE |
++ val = MTK_PPE_FLOW_CFG_IP6_3T_ROUTE |
+ MTK_PPE_FLOW_CFG_IP6_5T_ROUTE |
+ MTK_PPE_FLOW_CFG_IP6_6RD |
+ MTK_PPE_FLOW_CFG_IP4_NAT |
+ MTK_PPE_FLOW_CFG_IP4_NAPT |
+ MTK_PPE_FLOW_CFG_IP4_DSLITE |
+ MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
++ if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2))
++ val |= MTK_PPE_MD_TOAP_BYP_CRSN0 |
++ MTK_PPE_MD_TOAP_BYP_CRSN1 |
++ MTK_PPE_MD_TOAP_BYP_CRSN2 |
++ MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY;
++ else
++ val |= MTK_PPE_FLOW_CFG_IP4_TCP_FRAG |
++ MTK_PPE_FLOW_CFG_IP4_UDP_FRAG;
+ ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
+
+ val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) |
+@@ -820,6 +862,11 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
+ ppe_w32(ppe, MTK_PPE_GLO_CFG, val);
+
+ ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0);
++
++ if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2)) {
++ ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777);
++ ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
++ }
+ }
+
+ int mtk_ppe_stop(struct mtk_ppe *ppe)
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -32,6 +32,15 @@
+ #define MTK_FOE_IB1_UDP BIT(30)
+ #define MTK_FOE_IB1_STATIC BIT(31)
+
++/* CONFIG_MEDIATEK_NETSYS_V2 */
++#define MTK_FOE_IB1_BIND_TIMESTAMP_V2 GENMASK(7, 0)
++#define MTK_FOE_IB1_BIND_VLAN_LAYER_V2 GENMASK(16, 14)
++#define MTK_FOE_IB1_BIND_PPPOE_V2 BIT(17)
++#define MTK_FOE_IB1_BIND_VLAN_TAG_V2 BIT(18)
++#define MTK_FOE_IB1_BIND_CACHE_V2 BIT(20)
++#define MTK_FOE_IB1_BIND_TTL_V2 BIT(22)
++#define MTK_FOE_IB1_PACKET_TYPE_V2 GENMASK(27, 23)
++
+ enum {
+ MTK_PPE_PKT_TYPE_IPV4_HNAPT = 0,
+ MTK_PPE_PKT_TYPE_IPV4_ROUTE = 1,
+@@ -53,14 +62,25 @@ enum {
+
+ #define MTK_FOE_IB2_PORT_MG GENMASK(17, 12)
+
++#define MTK_FOE_IB2_RX_IDX GENMASK(18, 17)
+ #define MTK_FOE_IB2_PORT_AG GENMASK(23, 18)
+
+ #define MTK_FOE_IB2_DSCP GENMASK(31, 24)
+
++/* CONFIG_MEDIATEK_NETSYS_V2 */
++#define MTK_FOE_IB2_PORT_MG_V2 BIT(7)
++#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)
++#define MTK_FOE_IB2_PORT_AG_V2 GENMASK(23, 20)
++
+ #define MTK_FOE_VLAN2_WINFO_BSS GENMASK(5, 0)
+ #define MTK_FOE_VLAN2_WINFO_WCID GENMASK(13, 6)
+ #define MTK_FOE_VLAN2_WINFO_RING GENMASK(15, 14)
+
++#define MTK_FOE_WINFO_BSS GENMASK(5, 0)
++#define MTK_FOE_WINFO_WCID GENMASK(15, 6)
++
+ enum {
+ MTK_FOE_STATE_INVALID,
+ MTK_FOE_STATE_UNBIND,
+@@ -81,6 +101,9 @@ struct mtk_foe_mac_info {
+
+ u16 pppoe_id;
+ u16 src_mac_lo;
++
++ u16 minfo;
++ u16 winfo;
+ };
+
+ /* software-only entry type */
+@@ -198,7 +221,7 @@ struct mtk_foe_entry {
+ struct mtk_foe_ipv4_dslite dslite;
+ struct mtk_foe_ipv6 ipv6;
+ struct mtk_foe_ipv6_6rd ipv6_6rd;
+- u32 data[19];
++ u32 data[23];
+ };
+ };
+
+@@ -306,20 +329,27 @@ mtk_ppe_check_skb(struct mtk_ppe *ppe, s
+ __mtk_ppe_check_skb(ppe, skb, hash);
+ }
+
+-int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto,
+- u8 pse_port, u8 *src_mac, u8 *dest_mac);
+-int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port);
+-int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool orig,
++int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ int type, int l4proto, u8 pse_port, u8 *src_mac,
++ u8 *dest_mac);
++int mtk_foe_entry_set_pse_port(struct mtk_eth *eth,
++ struct mtk_foe_entry *entry, u8 port);
++int mtk_foe_entry_set_ipv4_tuple(struct mtk_eth *eth,
++ struct mtk_foe_entry *entry, bool orig,
+ __be32 src_addr, __be16 src_port,
+ __be32 dest_addr, __be16 dest_port);
+-int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry,
++int mtk_foe_entry_set_ipv6_tuple(struct mtk_eth *eth,
++ struct mtk_foe_entry *entry,
+ __be32 *src_addr, __be16 src_port,
+ __be32 *dest_addr, __be16 dest_port);
+-int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
+-int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
+-int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
+-int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+- int bss, int wcid);
++int mtk_foe_entry_set_dsa(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ int port);
++int mtk_foe_entry_set_vlan(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ int vid);
++int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry,
++ 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_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
+@@ -52,18 +52,19 @@ static const struct rhashtable_params mt
+ };
+
+ static int
+-mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data,
+- bool egress)
++mtk_flow_set_ipv4_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe,
++ struct mtk_flow_data *data, bool egress)
+ {
+- return mtk_foe_entry_set_ipv4_tuple(foe, egress,
++ return mtk_foe_entry_set_ipv4_tuple(eth, foe, egress,
+ data->v4.src_addr, data->src_port,
+ data->v4.dst_addr, data->dst_port);
+ }
+
+ static int
+-mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data)
++mtk_flow_set_ipv6_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe,
++ struct mtk_flow_data *data)
+ {
+- return mtk_foe_entry_set_ipv6_tuple(foe,
++ return mtk_foe_entry_set_ipv6_tuple(eth, foe,
+ data->v6.src_addr.s6_addr32, data->src_port,
+ data->v6.dst_addr.s6_addr32, data->dst_port);
+ }
+@@ -190,16 +191,29 @@ mtk_flow_set_output_device(struct mtk_et
+ int pse_port, dsa_port;
+
+ if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
+- mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss,
+- info.wcid);
+- pse_port = 3;
++ mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue,
++ info.bss, info.wcid);
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ switch (info.wdma_idx) {
++ case 0:
++ pse_port = 8;
++ break;
++ case 1:
++ pse_port = 9;
++ break;
++ default:
++ return -EINVAL;
++ }
++ } else {
++ pse_port = 3;
++ }
+ *wed_index = info.wdma_idx;
+ goto out;
+ }
+
+ dsa_port = mtk_flow_get_dsa_port(&dev);
+ if (dsa_port >= 0)
+- mtk_foe_entry_set_dsa(foe, dsa_port);
++ mtk_foe_entry_set_dsa(eth, foe, dsa_port);
+
+ if (dev == eth->netdev[0])
+ pse_port = 1;
+@@ -209,7 +223,7 @@ mtk_flow_set_output_device(struct mtk_et
+ return -EOPNOTSUPP;
+
+ out:
+- mtk_foe_entry_set_pse_port(foe, pse_port);
++ mtk_foe_entry_set_pse_port(eth, foe, pse_port);
+
+ return 0;
+ }
+@@ -333,9 +347,8 @@ mtk_flow_offload_replace(struct mtk_eth
+ !is_valid_ether_addr(data.eth.h_dest))
+ return -EINVAL;
+
+- err = mtk_foe_entry_prepare(&foe, offload_type, l4proto, 0,
+- data.eth.h_source,
+- data.eth.h_dest);
++ err = mtk_foe_entry_prepare(eth, &foe, offload_type, l4proto, 0,
++ data.eth.h_source, data.eth.h_dest);
+ if (err)
+ return err;
+
+@@ -360,7 +373,7 @@ mtk_flow_offload_replace(struct mtk_eth
+ data.v4.src_addr = addrs.key->src;
+ data.v4.dst_addr = addrs.key->dst;
+
+- mtk_flow_set_ipv4_addr(&foe, &data, false);
++ mtk_flow_set_ipv4_addr(eth, &foe, &data, false);
+ }
+
+ if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+@@ -371,7 +384,7 @@ mtk_flow_offload_replace(struct mtk_eth
+ data.v6.src_addr = addrs.key->src;
+ data.v6.dst_addr = addrs.key->dst;
+
+- mtk_flow_set_ipv6_addr(&foe, &data);
++ mtk_flow_set_ipv6_addr(eth, &foe, &data);
+ }
+
+ flow_action_for_each(i, act, &rule->action) {
+@@ -401,7 +414,7 @@ mtk_flow_offload_replace(struct mtk_eth
+ }
+
+ if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+- err = mtk_flow_set_ipv4_addr(&foe, &data, true);
++ err = mtk_flow_set_ipv4_addr(eth, &foe, &data, true);
+ if (err)
+ return err;
+ }
+@@ -413,10 +426,10 @@ mtk_flow_offload_replace(struct mtk_eth
+ if (data.vlan.proto != htons(ETH_P_8021Q))
+ return -EOPNOTSUPP;
+
+- mtk_foe_entry_set_vlan(&foe, data.vlan.id);
++ mtk_foe_entry_set_vlan(eth, &foe, data.vlan.id);
+ }
+ if (data.pppoe.num == 1)
+- mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
++ mtk_foe_entry_set_pppoe(eth, &foe, data.pppoe.sid);
+
+ err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest,
+ &wed_index);
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+@@ -21,6 +21,9 @@
+ #define MTK_PPE_GLO_CFG_BUSY BIT(31)
+
+ #define MTK_PPE_FLOW_CFG 0x204
++#define MTK_PPE_MD_TOAP_BYP_CRSN0 BIT(1)
++#define MTK_PPE_MD_TOAP_BYP_CRSN1 BIT(2)
++#define MTK_PPE_MD_TOAP_BYP_CRSN2 BIT(3)
+ #define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG BIT(6)
+ #define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG BIT(7)
+ #define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE BIT(8)
+@@ -54,6 +57,7 @@
+ #define MTK_PPE_TB_CFG_HASH_MODE GENMASK(15, 14)
+ #define MTK_PPE_TB_CFG_SCAN_MODE GENMASK(17, 16)
+ #define MTK_PPE_TB_CFG_HASH_DEBUG GENMASK(19, 18)
++#define MTK_PPE_TB_CFG_INFO_SEL BIT(20)
+
+ enum {
+ MTK_PPE_SCAN_MODE_DISABLED,
+@@ -112,6 +116,8 @@ enum {
+ #define MTK_PPE_DEFAULT_CPU_PORT 0x248
+ #define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n) (GENMASK(2, 0) << ((_n) * 4))
+
++#define MTK_PPE_DEFAULT_CPU_PORT1 0x24c
++
+ #define MTK_PPE_MTU_DROP 0x308
+
+ #define MTK_PPE_VLAN_MTU0 0x30c
+@@ -141,4 +147,6 @@ enum {
+ #define MTK_PPE_MIB_CACHE_CTL_EN BIT(0)
+ #define MTK_PPE_MIB_CACHE_CTL_FLUSH BIT(2)
+
++#define MTK_PPE_SBW_CTRL 0x374
++
+ #endif
diff --git a/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch b/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch
new file mode 100644
index 0000000000..e8bb85ac94
--- /dev/null
+++ b/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch
@@ -0,0 +1,26 @@
+From b94b02a270471337bef73c44fa3493a521e31a61 Mon Sep 17 00:00:00 2001
+Message-Id: <b94b02a270471337bef73c44fa3493a521e31a61.1662886034.git.lorenzo@kernel.org>
+In-Reply-To: <e5ecb4f619197b93fa682d722452dc8412864cdb.1662886033.git.lorenzo@kernel.org>
+References: <e5ecb4f619197b93fa682d722452dc8412864cdb.1662886033.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 5 Sep 2022 13:56:13 +0200
+Subject: [PATCH net-next 5/5] net: ethernet: mtk_eth_soc: enable flow
+ offloading support for mt7986
+
+Enable hw packet engine and wireless packet dispatcher for mt7986
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4259,6 +4259,7 @@ static const struct mtk_soc_data mt7986_
+ .hw_features = MTK_HW_FEATURES,
+ .required_clks = MT7986_CLKS_BITMAP,
+ .required_pctl = false,
++ .offload_version = 2,
+ .hash_offset = 4,
+ .foe_entry_size = sizeof(struct mtk_foe_entry),
+ .txrx = {
diff --git a/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-fix-wrong-use-of-new-helper.patch b/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-fix-wrong-use-of-new-helper.patch
new file mode 100644
index 0000000000..c4bd29365c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-fix-wrong-use-of-new-helper.patch
@@ -0,0 +1,46 @@
+From 40350ce3ae8701146aafd79c5f7b5582d9955e58 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 25 Sep 2022 15:12:35 +0100
+Subject: [PATCH 1/2] net: ethernet: mtk_eth_soc: fix wrong use of new helper
+ function
+To: linux-mediatek@lists.infradead.org,
+ netdev@vger.kernel.org,
+ Lorenzo Bianconi <lorenzo@kernel.org>
+Cc: Sujuan Chen <sujuan.chen@mediatek.com>,
+ Bo Jiao <Bo.Jiao@mediatek.com>,
+ 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>,
+ Chen Minqiang <ptpt52@gmail.com>
+
+In function mtk_foe_entry_set_vlan() the call to field accessor macro
+FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)
+has been wrongly replaced by
+mtk_prep_ib1_vlan_layer(eth, entry->ib1)
+
+Use correct helper function mtk_get_ib1_vlan_layer instead.
+
+Reported-by: Chen Minqiang <ptpt52@gmail.com>
+Fixes: 03a3180e5c09e1 ("net: ethernet: mtk_eth_soc: introduce flow offloading support for mt7986")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -337,7 +337,7 @@ int mtk_foe_entry_set_vlan(struct mtk_et
+ {
+ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
+
+- switch (mtk_prep_ib1_vlan_layer(eth, entry->ib1)) {
++ switch (mtk_get_ib1_vlan_layer(eth, entry->ib1)) {
+ case 0:
+ entry->ib1 |= mtk_get_ib1_vlan_tag_mask(eth) |
+ mtk_prep_ib1_vlan_layer(eth, 1);
diff --git a/target/linux/generic/backport-6.1/725-v6.0-net-ethernet-mtk_eth_soc-fix-usage-of-foe_entry_size.patch b/target/linux/generic/backport-6.1/725-v6.0-net-ethernet-mtk_eth_soc-fix-usage-of-foe_entry_size.patch
new file mode 100644
index 0000000000..bb02f401a2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/725-v6.0-net-ethernet-mtk_eth_soc-fix-usage-of-foe_entry_size.patch
@@ -0,0 +1,49 @@
+From fcf14c2c5deae8f8c3d25530bab10856f63f8a63 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 25 Sep 2022 15:18:54 +0100
+Subject: [PATCH 2/2] net: ethernet: mtk_eth_soc: fix usage of foe_entry_size
+To: linux-mediatek@lists.infradead.org,
+ netdev@vger.kernel.org,
+ Lorenzo Bianconi <lorenzo@kernel.org>
+Cc: Sujuan Chen <sujuan.chen@mediatek.com>,
+ Bo Jiao <Bo.Jiao@mediatek.com>,
+ 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>,
+ Chen Minqiang <ptpt52@gmail.com>
+
+As sizeof(hwe->data) can now longer be used as the actual size depends
+on foe_entry_size, in commit 9d8cb4c096ab02
+("net: ethernet: mtk_eth_soc: add foe_entry_size to mtk_eth_soc") the
+use of sizeof(hwe->data) is hence replaced.
+However, replacing it with ppe->eth->soc->foe_entry_size is wrong as
+foe_entry_size represents the size of the whole descriptor and not just
+the 'data' field.
+Fix this by subtracing the size of the only other field in the struct
+'ib1', so we actually end up with the correct size to be copied to the
+data field.
+
+Reported-by: Chen Minqiang <ptpt52@gmail.com>
+Fixes: 9d8cb4c096ab02 ("net: ethernet: mtk_eth_soc: add foe_entry_size to mtk_eth_soc")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -547,7 +547,7 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+ }
+
+ hwe = mtk_foe_get_entry(ppe, hash);
+- memcpy(&hwe->data, &entry->data, eth->soc->foe_entry_size);
++ memcpy(&hwe->data, &entry->data, eth->soc->foe_entry_size - sizeof(hwe->ib1));
+ wmb();
+ hwe->ib1 = entry->ib1;
+
diff --git a/target/linux/generic/backport-6.1/726-v6.0-net-ethernet-mtk_eth_soc-fix-mask-of-RX_DMA_GET_SPOR.patch b/target/linux/generic/backport-6.1/726-v6.0-net-ethernet-mtk_eth_soc-fix-mask-of-RX_DMA_GET_SPOR.patch
new file mode 100644
index 0000000000..27c719b663
--- /dev/null
+++ b/target/linux/generic/backport-6.1/726-v6.0-net-ethernet-mtk_eth_soc-fix-mask-of-RX_DMA_GET_SPOR.patch
@@ -0,0 +1,32 @@
+From c9da02bfb1112461e048d3b736afb1873f6f4ccf Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 27 Sep 2022 16:30:02 +0100
+Subject: [PATCH 1/1] net: ethernet: mtk_eth_soc: fix mask of
+ RX_DMA_GET_SPORT{,_V2}
+
+The bitmasks applied in RX_DMA_GET_SPORT and RX_DMA_GET_SPORT_V2 macros
+were swapped. Fix that.
+
+Reported-by: Chen Minqiang <ptpt52@gmail.com>
+Fixes: 160d3a9b192985 ("net: ethernet: mtk_eth_soc: introduce MTK_NETSYS_V2 support")
+Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://lore.kernel.org/r/YzMW+mg9UsaCdKRQ@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -315,8 +315,8 @@
+ #define MTK_RXD5_PPE_CPU_REASON GENMASK(22, 18)
+ #define MTK_RXD5_SRC_PORT GENMASK(29, 26)
+
+-#define RX_DMA_GET_SPORT(x) (((x) >> 19) & 0xf)
+-#define RX_DMA_GET_SPORT_V2(x) (((x) >> 26) & 0x7)
++#define RX_DMA_GET_SPORT(x) (((x) >> 19) & 0x7)
++#define RX_DMA_GET_SPORT_V2(x) (((x) >> 26) & 0xf)
+
+ /* PDMA V2 descriptor rxd3 */
+ #define RX_DMA_VTAG_V2 BIT(0)
diff --git a/target/linux/generic/backport-6.1/727-v6.1-net-ethernet-mtk_eth_soc-fix-state-in-__mtk_foe_entr.patch b/target/linux/generic/backport-6.1/727-v6.1-net-ethernet-mtk_eth_soc-fix-state-in-__mtk_foe_entr.patch
new file mode 100644
index 0000000000..11465c1c5b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/727-v6.1-net-ethernet-mtk_eth_soc-fix-state-in-__mtk_foe_entr.patch
@@ -0,0 +1,37 @@
+From ae3ed15da5889263de372ff9df2e83e16acca4cb Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Fri, 30 Sep 2022 01:56:53 +0100
+Subject: [PATCH 1/1] net: ethernet: mtk_eth_soc: fix state in
+ __mtk_foe_entry_clear
+
+Setting ib1 state to MTK_FOE_STATE_UNBIND in __mtk_foe_entry_clear
+routine as done by commit 0e80707d94e4c8 ("net: ethernet: mtk_eth_soc:
+fix typo in __mtk_foe_entry_clear") breaks flow offloading, at least
+on older MTK_NETSYS_V1 SoCs, OpenWrt users have confirmed the bug on
+MT7622 and MT7621 systems.
+Felix Fietkau suggested to use MTK_FOE_STATE_INVALID instead which
+works well on both, MTK_NETSYS_V1 and MTK_NETSYS_V2.
+
+Tested on MT7622 (Linksys E8450) and MT7986 (BananaPi BPI-R3).
+
+Suggested-by: Felix Fietkau <nbd@nbd.name>
+Fixes: 0e80707d94e4c8 ("net: ethernet: mtk_eth_soc: fix typo in __mtk_foe_entry_clear")
+Fixes: 33fc42de33278b ("net: ethernet: mtk_eth_soc: support creating mac address based offload entries")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://lore.kernel.org/r/YzY+1Yg0FBXcnrtc@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -442,7 +442,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
+ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash);
+
+ hwe->ib1 &= ~MTK_FOE_IB1_STATE;
+- hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_UNBIND);
++ hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
+ dma_wmb();
+ }
+ entry->hash = 0xffff;
diff --git a/target/linux/generic/backport-6.1/728-v6.1-01-net-ethernet-mtk_eth_soc-fix-possible-memory-leak-in.patch b/target/linux/generic/backport-6.1/728-v6.1-01-net-ethernet-mtk_eth_soc-fix-possible-memory-leak-in.patch
new file mode 100644
index 0000000000..b41318afd7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/728-v6.1-01-net-ethernet-mtk_eth_soc-fix-possible-memory-leak-in.patch
@@ -0,0 +1,73 @@
+From b3d0d98179d62f9d55635a600679c4fa362baf8d Mon Sep 17 00:00:00 2001
+From: Yang Yingliang <yangyingliang@huawei.com>
+Date: Mon, 17 Oct 2022 11:51:54 +0800
+Subject: [PATCH 1/3] net: ethernet: mtk_eth_soc: fix possible memory leak in
+ mtk_probe()
+
+If mtk_wed_add_hw() has been called, mtk_wed_exit() needs be called
+in error path or removing module to free the memory allocated in
+mtk_wed_add_hw().
+
+Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)")
+Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 17 ++++++++++++-----
+ 1 file changed, 12 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4011,19 +4011,23 @@ static int mtk_probe(struct platform_dev
+ eth->irq[i] = platform_get_irq(pdev, i);
+ if (eth->irq[i] < 0) {
+ dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
+- return -ENXIO;
++ err = -ENXIO;
++ goto err_wed_exit;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(eth->clks); i++) {
+ eth->clks[i] = devm_clk_get(eth->dev,
+ mtk_clks_source_name[i]);
+ if (IS_ERR(eth->clks[i])) {
+- if (PTR_ERR(eth->clks[i]) == -EPROBE_DEFER)
+- return -EPROBE_DEFER;
++ if (PTR_ERR(eth->clks[i]) == -EPROBE_DEFER) {
++ err = -EPROBE_DEFER;
++ goto err_wed_exit;
++ }
+ if (eth->soc->required_clks & BIT(i)) {
+ dev_err(&pdev->dev, "clock %s not found\n",
+ mtk_clks_source_name[i]);
+- return -EINVAL;
++ err = -EINVAL;
++ goto err_wed_exit;
+ }
+ eth->clks[i] = NULL;
+ }
+@@ -4034,7 +4038,7 @@ static int mtk_probe(struct platform_dev
+
+ err = mtk_hw_init(eth);
+ if (err)
+- return err;
++ goto err_wed_exit;
+
+ eth->hwlro = MTK_HAS_CAPS(eth->soc->caps, MTK_HWLRO);
+
+@@ -4132,6 +4136,8 @@ err_free_dev:
+ mtk_free_dev(eth);
+ err_deinit_hw:
+ mtk_hw_deinit(eth);
++err_wed_exit:
++ mtk_wed_exit();
+
+ return err;
+ }
+@@ -4151,6 +4157,7 @@ static int mtk_remove(struct platform_de
+ phylink_disconnect_phy(mac->phylink);
+ }
+
++ mtk_wed_exit();
+ mtk_hw_deinit(eth);
+
+ netif_napi_del(&eth->tx_napi);
diff --git a/target/linux/generic/backport-6.1/728-v6.1-02-net-ethernet-mtk_eth_wed-add-missing-put_device-in-m.patch b/target/linux/generic/backport-6.1/728-v6.1-02-net-ethernet-mtk_eth_wed-add-missing-put_device-in-m.patch
new file mode 100644
index 0000000000..ef5374dcc5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/728-v6.1-02-net-ethernet-mtk_eth_wed-add-missing-put_device-in-m.patch
@@ -0,0 +1,47 @@
+From 9d4f20a476ca57e4c9246eb1fa2a61bea2354720 Mon Sep 17 00:00:00 2001
+From: Yang Yingliang <yangyingliang@huawei.com>
+Date: Mon, 17 Oct 2022 11:51:55 +0800
+Subject: [PATCH 2/3] net: ethernet: mtk_eth_wed: add missing put_device() in
+ mtk_wed_add_hw()
+
+After calling get_device() in mtk_wed_add_hw(), in error path, put_device()
+needs be called.
+
+Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)")
+Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_wed.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -1084,11 +1084,11 @@ void mtk_wed_add_hw(struct device_node *
+ get_device(&pdev->dev);
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+- return;
++ goto err_put_device;
+
+ regs = syscon_regmap_lookup_by_phandle(np, NULL);
+ if (IS_ERR(regs))
+- return;
++ goto err_put_device;
+
+ rcu_assign_pointer(mtk_soc_wed_ops, &wed_ops);
+
+@@ -1131,8 +1131,14 @@ void mtk_wed_add_hw(struct device_node *
+
+ hw_list[index] = hw;
+
++ mutex_unlock(&hw_lock);
++
++ return;
++
+ unlock:
+ mutex_unlock(&hw_lock);
++err_put_device:
++ put_device(&pdev->dev);
+ }
+
+ void mtk_wed_exit(void)
diff --git a/target/linux/generic/backport-6.1/728-v6.1-03-net-ethernet-mtk_eth_wed-add-missing-of_node_put.patch b/target/linux/generic/backport-6.1/728-v6.1-03-net-ethernet-mtk_eth_wed-add-missing-of_node_put.patch
new file mode 100644
index 0000000000..0a452d4a7d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/728-v6.1-03-net-ethernet-mtk_eth_wed-add-missing-of_node_put.patch
@@ -0,0 +1,43 @@
+From e0bb4659e235770e6f53b3692e958591f49448f5 Mon Sep 17 00:00:00 2001
+From: Yang Yingliang <yangyingliang@huawei.com>
+Date: Mon, 17 Oct 2022 11:51:56 +0800
+Subject: [PATCH 3/3] net: ethernet: mtk_eth_wed: add missing of_node_put()
+
+The device_node pointer returned by of_parse_phandle() with refcount
+incremented, when finish using it, the refcount need be decreased.
+
+Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)")
+Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_wed.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -1079,7 +1079,7 @@ void mtk_wed_add_hw(struct device_node *
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+- return;
++ goto err_of_node_put;
+
+ get_device(&pdev->dev);
+ irq = platform_get_irq(pdev, 0);
+@@ -1139,6 +1139,8 @@ unlock:
+ mutex_unlock(&hw_lock);
+ err_put_device:
+ put_device(&pdev->dev);
++err_of_node_put:
++ of_node_put(np);
+ }
+
+ void mtk_wed_exit(void)
+@@ -1159,6 +1161,7 @@ void mtk_wed_exit(void)
+ hw_list[i] = NULL;
+ debugfs_remove(hw->debugfs_dir);
+ put_device(hw->dev);
++ of_node_put(hw->node);
+ kfree(hw);
+ }
+ }
diff --git a/target/linux/generic/backport-6.1/729-01-v6.1-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch b/target/linux/generic/backport-6.1/729-01-v6.1-net-ethernet-mtk_wed-introduce-wed-mcu-support.patch
new file mode 100644
index 0000000000..c48613929d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-01-v6.1-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 void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
++{
++ writel(val, wo->boot.addr + reg);
++}
++
++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;
++
++ skb = alloc_skb(length, GFP_KERNEL);
++ if (!skb)
++ return NULL;
++
++ 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 skb;
++}
++
++static struct sk_buff *
++mtk_wed_mcu_get_response(struct mtk_wed_wo *wo, unsigned long expires)
++{
++ 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);
++}
++
++void mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb)
++{
++ skb_queue_tail(&wo->mcu.res_q, skb);
++ wake_up(&wo->mcu.wait);
++}
++
++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));
++
++ dev_notice(wo->hw->dev, "%s\n", msg);
++ break;
++ }
++ 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 MTK_WED_WO_EVT_RXCNT_INFO:
++ break;
++ default:
++ break;
++ }
++
++ dev_kfree_skb(skb);
++}
++
++static int
++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)
++{
++ struct mtk_wed_mcu_hdr *hdr;
++
++ /* TODO: make it dynamic based on cmd */
++ wo->mcu.timeout = 20 * HZ;
++
++ hdr = (struct mtk_wed_mcu_hdr *)skb_push(skb, sizeof(*hdr));
++ hdr->cmd = cmd;
++ hdr->length = cpu_to_le16(skb->len);
++
++ if (wait_resp && wait_seq) {
++ u16 seq = ++wo->mcu.seq;
++
++ 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 int
++mtk_wed_mcu_parse_response(struct mtk_wed_wo *wo, struct sk_buff *skb,
++ int cmd, int seq)
++{
++ struct mtk_wed_mcu_hdr *hdr;
++
++ 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;
++
++ skb_pull(skb, sizeof(*hdr));
++ switch (cmd) {
++ case MTK_WED_WO_CMD_RXCNT_INFO:
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
++ const void *data, int len, bool wait_resp)
++{
++ unsigned long expires;
++ struct sk_buff *skb;
++ u16 seq;
++ int ret;
++
++ skb = mtk_wed_mcu_msg_alloc(data, len);
++ if (!skb)
++ return -ENOMEM;
++
++ mutex_lock(&wo->mcu.mutex);
++
++ ret = mtk_wed_mcu_skb_send_msg(wo, skb, id, cmd, &seq, wait_resp);
++ if (ret || !wait_resp)
++ goto unlock;
++
++ 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);
++
++unlock:
++ mutex_unlock(&wo->mcu.mutex);
++
++ return ret;
++}
++
++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;
++
++ index = of_property_match_string(wo->hw->node, "memory-region-names",
++ region->name);
++ if (index < 0)
++ return index;
++
++ np = of_parse_phandle(wo->hw->node, "memory-region", index);
++ if (!np)
++ return -ENODEV;
++
++ rmem = of_reserved_mem_lookup(np);
++ of_node_put(np);
++
++ if (!rmem)
++ return -ENODEV;
++
++ region->phy_addr = rmem->base;
++ region->size = rmem->size;
++ region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size);
++
++ return !region->addr ? -EINVAL : 0;
++}
++
++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;
++
++ 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;
++
++ while (region_ptr < trailer_ptr) {
++ u32 length;
++
++ fw_region = (const struct mtk_wed_fw_region *)region_ptr;
++ length = le32_to_cpu(fw_region->len);
++
++ 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;
++ }
++
++ return -EINVAL;
++}
++
++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;
++ }
++
++ 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/backport-6.1/729-02-v6.1-net-ethernet-mtk_wed-introduce-wed-wo-support.patch b/target/linux/generic/backport-6.1/729-02-v6.1-net-ethernet-mtk_wed-introduce-wed-wo-support.patch
new file mode 100644
index 0000000000..dbd7e30fbb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-02-v6.1-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
+--- /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
++mtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
++ gfp_t gfp, bool rx)
++{
++ enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
++ int n_buf = 0;
++
++ 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;
++
++ if (!buf)
++ break;
++
++ 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;
++ }
++
++ 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/backport-6.1/729-03-v6.1-net-ethernet-mtk_wed-rename-tx_wdma-array-in-rx_wdma.patch b/target/linux/generic/backport-6.1/729-03-v6.1-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/backport-6.1/729-03-v6.1-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/backport-6.1/729-04-v6.1-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch b/target/linux/generic/backport-6.1/729-04-v6.1-net-ethernet-mtk_wed-add-configure-wed-wo-support.patch
new file mode 100644
index 0000000000..4c34d0cb33
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-04-v6.1-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/backport-6.1/729-05-v6.1-net-ethernet-mtk_wed-add-rx-mib-counters.patch b/target/linux/generic/backport-6.1/729-05-v6.1-net-ethernet-mtk_wed-add-rx-mib-counters.patch
new file mode 100644
index 0000000000..bb1066dece
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-05-v6.1-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/backport-6.1/729-06-v6.1-net-ethernet-mtk_eth_soc-do-not-overwrite-mtu-config.patch b/target/linux/generic/backport-6.1/729-06-v6.1-net-ethernet-mtk_eth_soc-do-not-overwrite-mtu-config.patch
new file mode 100644
index 0000000000..003ca9bae6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-06-v6.1-net-ethernet-mtk_eth_soc-do-not-overwrite-mtu-config.patch
@@ -0,0 +1,98 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 17 Nov 2022 00:35:04 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: do not overwrite mtu
+ configuration running reset routine
+
+Restore user configured MTU running mtk_hw_init() during tx timeout routine
+since it will be overwritten after a hw reset.
+
+Reported-by: Felix Fietkau <nbd@nbd.name>
+Fixes: 9ea4d311509f ("net: ethernet: mediatek: add the whole ethernet reset into the reset process")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3176,6 +3176,30 @@ static void mtk_dim_tx(struct work_struc
+ dim->state = DIM_START_MEASURE;
+ }
+
++static void mtk_set_mcr_max_rx(struct mtk_mac *mac, u32 val)
++{
++ struct mtk_eth *eth = mac->hw;
++ u32 mcr_cur, mcr_new;
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
++ return;
++
++ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
++ mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
++
++ if (val <= 1518)
++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518);
++ else if (val <= 1536)
++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536);
++ else if (val <= 1552)
++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552);
++ else
++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048);
++
++ if (mcr_new != mcr_cur)
++ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
++}
++
+ static int mtk_hw_init(struct mtk_eth *eth)
+ {
+ u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
+@@ -3250,8 +3274,16 @@ static int mtk_hw_init(struct mtk_eth *e
+ * up with the more appropriate value when mtk_mac_config call is being
+ * invoked.
+ */
+- for (i = 0; i < MTK_MAC_COUNT; i++)
++ for (i = 0; i < MTK_MAC_COUNT; i++) {
++ struct net_device *dev = eth->netdev[i];
++
+ mtk_w32(eth, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(i));
++ if (dev) {
++ struct mtk_mac *mac = netdev_priv(dev);
++
++ mtk_set_mcr_max_rx(mac, dev->mtu + MTK_RX_ETH_HLEN);
++ }
++ }
+
+ /* Indicates CDM to parse the MTK special tag from CPU
+ * which also is working out for untag packets.
+@@ -3367,7 +3399,6 @@ static int mtk_change_mtu(struct net_dev
+ int length = new_mtu + MTK_RX_ETH_HLEN;
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+- u32 mcr_cur, mcr_new;
+
+ if (rcu_access_pointer(eth->prog) &&
+ length > MTK_PP_MAX_BUF_SIZE) {
+@@ -3375,23 +3406,7 @@ static int mtk_change_mtu(struct net_dev
+ return -EINVAL;
+ }
+
+- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
+- mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+- mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
+-
+- if (length <= 1518)
+- mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518);
+- else if (length <= 1536)
+- mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536);
+- else if (length <= 1552)
+- mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552);
+- else
+- mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048);
+-
+- if (mcr_new != mcr_cur)
+- mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
+- }
+-
++ mtk_set_mcr_max_rx(mac, length);
+ dev->mtu = new_mtu;
+
+ return 0;
diff --git a/target/linux/generic/backport-6.1/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch b/target/linux/generic/backport-6.1/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch
new file mode 100644
index 0000000000..342ffb99d2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch
@@ -0,0 +1,36 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 17 Nov 2022 00:58:46 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: remove cpu_relax in
+ mtk_pending_work
+
+Get rid of cpu_relax in mtk_pending_work routine since MTK_RESETTING is
+set only in mtk_pending_work() and it runs holding rtnl lock
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3437,11 +3437,8 @@ static void mtk_pending_work(struct work
+ rtnl_lock();
+
+ dev_dbg(eth->dev, "[%s][%d] reset\n", __func__, __LINE__);
++ set_bit(MTK_RESETTING, &eth->state);
+
+- while (test_and_set_bit_lock(MTK_RESETTING, &eth->state))
+- cpu_relax();
+-
+- dev_dbg(eth->dev, "[%s][%d] mtk_stop starts\n", __func__, __LINE__);
+ /* stop all devices to make sure that dma is properly shut down */
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+ if (!eth->netdev[i])
+@@ -3475,7 +3472,7 @@ static void mtk_pending_work(struct work
+
+ dev_dbg(eth->dev, "[%s][%d] reset done\n", __func__, __LINE__);
+
+- clear_bit_unlock(MTK_RESETTING, &eth->state);
++ clear_bit(MTK_RESETTING, &eth->state);
+
+ rtnl_unlock();
+ }
diff --git a/target/linux/generic/backport-6.1/729-08-v6.2-net-ethernet-mtk_eth_soc-fix-RSTCTRL_PPE-0-1-definit.patch b/target/linux/generic/backport-6.1/729-08-v6.2-net-ethernet-mtk_eth_soc-fix-RSTCTRL_PPE-0-1-definit.patch
new file mode 100644
index 0000000000..12aa3ebf6a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-08-v6.2-net-ethernet-mtk_eth_soc-fix-RSTCTRL_PPE-0-1-definit.patch
@@ -0,0 +1,63 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 17 Nov 2022 15:29:53 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix RSTCTRL_PPE{0,1} definitions
+
+Fix RSTCTRL_PPE0 and RSTCTRL_PPE1 register mask definitions for
+MTK_NETSYS_V2.
+Remove duplicated definitions.
+
+Fixes: 160d3a9b1929 ("net: ethernet: mtk_eth_soc: introduce MTK_NETSYS_V2 support")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3239,16 +3239,17 @@ static int mtk_hw_init(struct mtk_eth *e
+ return 0;
+ }
+
+- val = RSTCTRL_FE | RSTCTRL_PPE;
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0);
+-
+- val |= RSTCTRL_ETH;
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
+- val |= RSTCTRL_PPE1;
++ val = RSTCTRL_PPE0_V2;
++ } else {
++ val = RSTCTRL_PPE0;
+ }
+
+- ethsys_reset(eth, val);
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
++ val |= RSTCTRL_PPE1;
++
++ ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val);
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -445,18 +445,14 @@
+ /* ethernet reset control register */
+ #define ETHSYS_RSTCTRL 0x34
+ #define RSTCTRL_FE BIT(6)
+-#define RSTCTRL_PPE BIT(31)
+-#define RSTCTRL_PPE1 BIT(30)
++#define RSTCTRL_PPE0 BIT(31)
++#define RSTCTRL_PPE0_V2 BIT(30)
++#define RSTCTRL_PPE1 BIT(31)
+ #define RSTCTRL_ETH BIT(23)
+
+ /* ethernet reset check idle register */
+ #define ETHSYS_FE_RST_CHK_IDLE_EN 0x28
+
+-/* ethernet reset control register */
+-#define ETHSYS_RSTCTRL 0x34
+-#define RSTCTRL_FE BIT(6)
+-#define RSTCTRL_PPE BIT(31)
+-
+ /* ethernet dma channel agent map */
+ #define ETHSYS_DMA_AG_MAP 0x408
+ #define ETHSYS_DMA_AG_MAP_PDMA BIT(0)
diff --git a/target/linux/generic/backport-6.1/729-09-v6.2-net-ethernet-mtk_wed-add-wcid-overwritten-support-fo.patch b/target/linux/generic/backport-6.1/729-09-v6.2-net-ethernet-mtk_wed-add-wcid-overwritten-support-fo.patch
new file mode 100644
index 0000000000..d91d829911
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-09-v6.2-net-ethernet-mtk_wed-add-wcid-overwritten-support-fo.patch
@@ -0,0 +1,80 @@
+From: Sujuan Chen <sujuan.chen@mediatek.com>
+Date: Thu, 24 Nov 2022 11:18:14 +0800
+Subject: [PATCH] net: ethernet: mtk_wed: add wcid overwritten support for wed
+ v1
+
+All wed versions should enable the wcid overwritten feature,
+since the wcid size is controlled by the wlan driver.
+
+Tested-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Co-developed-by: Bo Jiao <bo.jiao@mediatek.com>
+Signed-off-by: Bo Jiao <bo.jiao@mediatek.com>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+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
+@@ -526,9 +526,9 @@ mtk_wed_dma_disable(struct mtk_wed_devic
+ 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);
+ }
++
++ mtk_wed_set_512_support(dev, false);
+ }
+
+ static void
+@@ -1297,9 +1297,10 @@ mtk_wed_start(struct mtk_wed_device *dev
+ if (mtk_wed_rro_cfg(dev))
+ return;
+
+- mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
+ }
+
++ mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
++
+ mtk_wed_dma_enable(dev);
+ dev->running = true;
+ }
+@@ -1365,11 +1366,13 @@ mtk_wed_attach(struct mtk_wed_device *de
+ }
+
+ mtk_wed_hw_init_early(dev);
+- if (hw->version == 1)
++ if (hw->version == 1) {
+ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
+ BIT(hw->index), 0);
+- else
++ } else {
++ dev->rev_id = wed_r32(dev, MTK_WED_REV_ID);
+ ret = mtk_wed_wo_init(hw);
++ }
+ out:
+ if (ret)
+ mtk_wed_detach(dev);
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -20,6 +20,8 @@ struct mtk_wdma_desc {
+ __le32 info;
+ } __packed __aligned(4);
+
++#define MTK_WED_REV_ID 0x004
++
+ #define MTK_WED_RESET 0x008
+ #define MTK_WED_RESET_TX_BM BIT(0)
+ #define MTK_WED_RESET_TX_FREE_AGENT BIT(4)
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -85,6 +85,9 @@ struct mtk_wed_device {
+ int irq;
+ u8 version;
+
++ /* used by wlan driver */
++ u32 rev_id;
++
+ 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;
diff --git a/target/linux/generic/backport-6.1/729-10-v6.2-net-ethernet-mtk_wed-return-status-value-in-mtk_wdma.patch b/target/linux/generic/backport-6.1/729-10-v6.2-net-ethernet-mtk_wed-return-status-value-in-mtk_wdma.patch
new file mode 100644
index 0000000000..d97bb715e0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-10-v6.2-net-ethernet-mtk_wed-return-status-value-in-mtk_wdma.patch
@@ -0,0 +1,85 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 24 Nov 2022 16:22:51 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: return status value in
+ mtk_wdma_rx_reset
+
+Move MTK_WDMA_RESET_IDX configuration in mtk_wdma_rx_reset routine.
+Increase poll timeout to 10ms in order to be aligned with vendor sdk.
+This is a preliminary patch to add Wireless Ethernet Dispatcher reset
+support.
+
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -101,17 +101,21 @@ mtk_wdma_read_reset(struct mtk_wed_devic
+ return wdma_r32(dev, MTK_WDMA_GLO_CFG);
+ }
+
+-static void
++static int
+ mtk_wdma_rx_reset(struct mtk_wed_device *dev)
+ {
+ u32 status, mask = MTK_WDMA_GLO_CFG_RX_DMA_BUSY;
+- int i;
++ int i, ret;
+
+ 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))
++ ret = readx_poll_timeout(mtk_wdma_read_reset, dev, status,
++ !(status & mask), 0, 10000);
++ if (ret)
+ dev_err(dev->hw->dev, "rx reset failed\n");
+
++ wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
++ wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
++
+ for (i = 0; i < ARRAY_SIZE(dev->rx_wdma); i++) {
+ if (dev->rx_wdma[i].desc)
+ continue;
+@@ -119,6 +123,8 @@ mtk_wdma_rx_reset(struct mtk_wed_device
+ wdma_w32(dev,
+ MTK_WDMA_RING_RX(i) + MTK_WED_RING_OFS_CPU_IDX, 0);
+ }
++
++ return ret;
+ }
+
+ static void
+@@ -565,9 +571,7 @@ mtk_wed_detach(struct mtk_wed_device *de
+
+ mtk_wed_stop(dev);
+
+- wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
+- wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
+-
++ mtk_wdma_rx_reset(dev);
+ 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);
+@@ -582,7 +586,6 @@ mtk_wed_detach(struct mtk_wed_device *de
+ 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) {
+@@ -1006,11 +1009,7 @@ mtk_wed_reset_dma(struct mtk_wed_device
+ wed_w32(dev, MTK_WED_RESET_IDX, 0);
+ }
+
+- 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);
++ mtk_wdma_rx_reset(dev);
+
+ if (busy) {
+ mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
diff --git a/target/linux/generic/backport-6.1/729-11-v6.2-net-ethernet-mtk_wed-move-MTK_WDMA_RESET_IDX_TX-conf.patch b/target/linux/generic/backport-6.1/729-11-v6.2-net-ethernet-mtk_wed-move-MTK_WDMA_RESET_IDX_TX-conf.patch
new file mode 100644
index 0000000000..10c1732c96
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-11-v6.2-net-ethernet-mtk_wed-move-MTK_WDMA_RESET_IDX_TX-conf.patch
@@ -0,0 +1,52 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 24 Nov 2022 16:22:52 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: move MTK_WDMA_RESET_IDX_TX
+ configuration in mtk_wdma_tx_reset
+
+Remove duplicated code. Increase poll timeout to 10ms in order to be
+aligned with vendor sdk.
+This is a preliminary patch to add Wireless Ethernet Dispatcher reset
+support.
+
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -135,16 +135,15 @@ mtk_wdma_tx_reset(struct mtk_wed_device
+
+ 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))
++ !(status & mask), 0, 10000))
+ 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_RESET_IDX, MTK_WDMA_RESET_IDX_TX);
++ wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
+
++ for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
+ wdma_w32(dev,
+ MTK_WDMA_RING_TX(i) + MTK_WED_RING_OFS_CPU_IDX, 0);
+- }
+ }
+
+ static void
+@@ -573,12 +572,6 @@ mtk_wed_detach(struct mtk_wed_device *de
+
+ mtk_wdma_rx_reset(dev);
+ 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_tx_buffer(dev);
+ mtk_wed_free_tx_rings(dev);
+
diff --git a/target/linux/generic/backport-6.1/729-12-v6.2-net-ethernet-mtk_wed-update-mtk_wed_stop.patch b/target/linux/generic/backport-6.1/729-12-v6.2-net-ethernet-mtk_wed-update-mtk_wed_stop.patch
new file mode 100644
index 0000000000..dfc0f8c3f3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-12-v6.2-net-ethernet-mtk_wed-update-mtk_wed_stop.patch
@@ -0,0 +1,98 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 24 Nov 2022 16:22:53 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: update mtk_wed_stop
+
+Update mtk_wed_stop routine and rename old mtk_wed_stop() to
+mtk_wed_deinit(). This is a preliminary patch to add Wireless Ethernet
+Dispatcher reset support.
+
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -539,14 +539,8 @@ mtk_wed_dma_disable(struct mtk_wed_devic
+ static void
+ mtk_wed_stop(struct mtk_wed_device *dev)
+ {
+- mtk_wed_dma_disable(dev);
+ mtk_wed_set_ext_int(dev, false);
+
+- wed_clr(dev, MTK_WED_CTRL,
+- MTK_WED_CTRL_WDMA_INT_AGENT_EN |
+- MTK_WED_CTRL_WPDMA_INT_AGENT_EN |
+- MTK_WED_CTRL_WED_TX_BM_EN |
+- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+ wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, 0);
+ wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, 0);
+ wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
+@@ -558,7 +552,27 @@ mtk_wed_stop(struct mtk_wed_device *dev)
+
+ 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
++mtk_wed_deinit(struct mtk_wed_device *dev)
++{
++ mtk_wed_stop(dev);
++ mtk_wed_dma_disable(dev);
++
++ wed_clr(dev, MTK_WED_CTRL,
++ MTK_WED_CTRL_WDMA_INT_AGENT_EN |
++ MTK_WED_CTRL_WPDMA_INT_AGENT_EN |
++ MTK_WED_CTRL_WED_TX_BM_EN |
++ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
++
++ if (dev->hw->version == 1)
++ return;
++
++ wed_clr(dev, MTK_WED_CTRL,
++ MTK_WED_CTRL_RX_ROUTE_QM_EN |
++ MTK_WED_CTRL_WED_RX_BM_EN |
++ MTK_WED_CTRL_RX_RRO_QM_EN);
+ }
+
+ static void
+@@ -568,7 +582,7 @@ mtk_wed_detach(struct mtk_wed_device *de
+
+ mutex_lock(&hw_lock);
+
+- mtk_wed_stop(dev);
++ mtk_wed_deinit(dev);
+
+ mtk_wdma_rx_reset(dev);
+ mtk_wed_reset(dev, MTK_WED_RESET_WED);
+@@ -677,7 +691,7 @@ mtk_wed_hw_init_early(struct mtk_wed_dev
+ {
+ u32 mask, set;
+
+- mtk_wed_stop(dev);
++ mtk_wed_deinit(dev);
+ mtk_wed_reset(dev, MTK_WED_RESET_WED);
+ mtk_wed_set_wpdma(dev);
+
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -234,6 +234,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_devic
+ (_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)
++#define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev)
++#define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev)
+ #else
+ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
+ {
+@@ -250,6 +252,8 @@ static inline bool mtk_wed_device_active
+ #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
++#define mtk_wed_device_stop(_dev) do {} while (0)
++#define mtk_wed_device_dma_reset(_dev) do {} while (0)
+ #endif
+
+ #endif
diff --git a/target/linux/generic/backport-6.1/729-13-v6.2-net-ethernet-mtk_wed-add-mtk_wed_rx_reset-routine.patch b/target/linux/generic/backport-6.1/729-13-v6.2-net-ethernet-mtk_wed-add-mtk_wed_rx_reset-routine.patch
new file mode 100644
index 0000000000..2205fea513
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-13-v6.2-net-ethernet-mtk_wed-add-mtk_wed_rx_reset-routine.patch
@@ -0,0 +1,309 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 24 Nov 2022 16:22:54 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: add mtk_wed_rx_reset routine
+
+Introduce mtk_wed_rx_reset routine in order to reset rx DMA for Wireless
+Ethernet Dispatcher available on MT7986 SoC.
+
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -951,42 +951,130 @@ mtk_wed_ring_reset(struct mtk_wed_ring *
+ }
+
+ static u32
+-mtk_wed_check_busy(struct mtk_wed_device *dev)
++mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
+ {
+- if (wed_r32(dev, MTK_WED_GLO_CFG) & MTK_WED_GLO_CFG_TX_DMA_BUSY)
+- return true;
+-
+- if (wed_r32(dev, MTK_WED_WPDMA_GLO_CFG) &
+- MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY)
+- return true;
+-
+- if (wed_r32(dev, MTK_WED_CTRL) & MTK_WED_CTRL_WDMA_INT_AGENT_BUSY)
+- return true;
+-
+- if (wed_r32(dev, MTK_WED_WDMA_GLO_CFG) &
+- MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY)
+- return true;
+-
+- if (wdma_r32(dev, MTK_WDMA_GLO_CFG) &
+- MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY)
+- return true;
+-
+- if (wed_r32(dev, MTK_WED_CTRL) &
+- (MTK_WED_CTRL_WED_TX_BM_BUSY | MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY))
+- return true;
+-
+- return false;
++ return !!(wed_r32(dev, reg) & mask);
+ }
+
+ static int
+-mtk_wed_poll_busy(struct mtk_wed_device *dev)
++mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
+ {
+ int sleep = 15000;
+ int timeout = 100 * sleep;
+ u32 val;
+
+ return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
+- timeout, false, dev);
++ timeout, false, dev, reg, mask);
++}
++
++static int
++mtk_wed_rx_reset(struct mtk_wed_device *dev)
++{
++ struct mtk_wed_wo *wo = dev->hw->wed_wo;
++ u8 val = MTK_WED_WO_STATE_SER_RESET;
++ int i, ret;
++
++ ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
++ MTK_WED_WO_CMD_CHANGE_STATE, &val,
++ sizeof(val), true);
++ if (ret)
++ return ret;
++
++ wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN);
++ ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
++ MTK_WED_WPDMA_RX_D_RX_DRV_BUSY);
++ if (ret) {
++ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
++ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV);
++ } else {
++ 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_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
++ MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE |
++ MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE);
++ wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
++ MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE |
++ MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE);
++
++ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
++ }
++
++ /* reset rro qm */
++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_RRO_QM_EN);
++ ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL,
++ MTK_WED_CTRL_RX_RRO_QM_BUSY);
++ if (ret) {
++ mtk_wed_reset(dev, MTK_WED_RESET_RX_RRO_QM);
++ } else {
++ 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);
++ }
++
++ /* reset route qm */
++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
++ ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL,
++ MTK_WED_CTRL_RX_ROUTE_QM_BUSY);
++ if (ret)
++ mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM);
++ else
++ wed_set(dev, MTK_WED_RTQM_GLO_CFG,
++ MTK_WED_RTQM_Q_RST);
++
++ /* reset tx wdma */
++ mtk_wdma_tx_reset(dev);
++
++ /* reset tx wdma drv */
++ wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN);
++ mtk_wed_poll_busy(dev, MTK_WED_CTRL,
++ MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
++ mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV);
++
++ /* reset wed rx dma */
++ ret = mtk_wed_poll_busy(dev, MTK_WED_GLO_CFG,
++ MTK_WED_GLO_CFG_RX_DMA_BUSY);
++ wed_clr(dev, MTK_WED_GLO_CFG, MTK_WED_GLO_CFG_RX_DMA_EN);
++ if (ret) {
++ mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA);
++ } else {
++ struct mtk_eth *eth = dev->hw->eth;
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ wed_set(dev, MTK_WED_RESET_IDX,
++ MTK_WED_RESET_IDX_RX_V2);
++ else
++ wed_set(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_RX);
++ wed_w32(dev, MTK_WED_RESET_IDX, 0);
++ }
++
++ /* reset rx bm */
++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
++ mtk_wed_poll_busy(dev, MTK_WED_CTRL,
++ MTK_WED_CTRL_WED_RX_BM_BUSY);
++ mtk_wed_reset(dev, MTK_WED_RESET_RX_BM);
++
++ /* wo change to enable state */
++ val = MTK_WED_WO_STATE_ENABLE;
++ ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
++ MTK_WED_WO_CMD_CHANGE_STATE, &val,
++ sizeof(val), true);
++ if (ret)
++ return ret;
++
++ /* wed_rx_ring_reset */
++ for (i = 0; i < ARRAY_SIZE(dev->rx_ring); i++) {
++ if (!dev->rx_ring[i].desc)
++ continue;
++
++ mtk_wed_ring_reset(&dev->rx_ring[i], MTK_WED_RX_RING_SIZE,
++ false);
++ }
++ mtk_wed_free_rx_buffer(dev);
++
++ return 0;
+ }
+
+ static void
+@@ -1004,19 +1092,23 @@ mtk_wed_reset_dma(struct mtk_wed_device
+ true);
+ }
+
+- if (mtk_wed_poll_busy(dev))
+- busy = mtk_wed_check_busy(dev);
+-
++ /* 1. reset WED tx DMA */
++ wed_clr(dev, MTK_WED_GLO_CFG, MTK_WED_GLO_CFG_TX_DMA_EN);
++ busy = mtk_wed_poll_busy(dev, MTK_WED_GLO_CFG,
++ MTK_WED_GLO_CFG_TX_DMA_BUSY);
+ if (busy) {
+ mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA);
+ } else {
+- wed_w32(dev, MTK_WED_RESET_IDX,
+- MTK_WED_RESET_IDX_TX |
+- MTK_WED_RESET_IDX_RX);
++ wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_TX);
+ wed_w32(dev, MTK_WED_RESET_IDX, 0);
+ }
+
+- mtk_wdma_rx_reset(dev);
++ /* 2. reset WDMA rx DMA */
++ busy = !!mtk_wdma_rx_reset(dev);
++ wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
++ if (!busy)
++ busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG,
++ MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY);
+
+ if (busy) {
+ mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
+@@ -1033,6 +1125,9 @@ mtk_wed_reset_dma(struct mtk_wed_device
+ MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE);
+ }
+
++ /* 3. reset WED WPDMA tx */
++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
++
+ for (i = 0; i < 100; i++) {
+ val = wed_r32(dev, MTK_WED_TX_BM_INTF);
+ if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40)
+@@ -1040,8 +1135,19 @@ mtk_wed_reset_dma(struct mtk_wed_device
+ }
+
+ mtk_wed_reset(dev, MTK_WED_RESET_TX_FREE_AGENT);
++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_BM_EN);
+ mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
+
++ /* 4. reset WED WPDMA tx */
++ busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG,
++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY);
++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
++ if (!busy)
++ busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG,
++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_BUSY);
++
+ if (busy) {
+ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
+ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV);
+@@ -1052,6 +1158,17 @@ mtk_wed_reset_dma(struct mtk_wed_device
+ MTK_WED_WPDMA_RESET_IDX_RX);
+ wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, 0);
+ }
++
++ dev->init_done = false;
++ if (dev->hw->version == 1)
++ return;
++
++ if (!busy) {
++ wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_WPDMA_IDX_RX);
++ wed_w32(dev, MTK_WED_RESET_IDX, 0);
++ }
++
++ mtk_wed_rx_reset(dev);
+ }
+
+ static int
+@@ -1274,6 +1391,9 @@ mtk_wed_start(struct mtk_wed_device *dev
+ {
+ int i;
+
++ if (mtk_wed_get_rx_capa(dev) && mtk_wed_rx_buffer_alloc(dev))
++ return;
++
+ 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);
+@@ -1362,10 +1482,6 @@ mtk_wed_attach(struct mtk_wed_device *de
+ 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;
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -24,11 +24,15 @@ struct mtk_wdma_desc {
+
+ #define MTK_WED_RESET 0x008
+ #define MTK_WED_RESET_TX_BM BIT(0)
++#define MTK_WED_RESET_RX_BM BIT(1)
+ #define MTK_WED_RESET_TX_FREE_AGENT BIT(4)
+ #define MTK_WED_RESET_WPDMA_TX_DRV BIT(8)
+ #define MTK_WED_RESET_WPDMA_RX_DRV BIT(9)
++#define MTK_WED_RESET_WPDMA_RX_D_DRV BIT(10)
+ #define MTK_WED_RESET_WPDMA_INT_AGENT BIT(11)
+ #define MTK_WED_RESET_WED_TX_DMA BIT(12)
++#define MTK_WED_RESET_WED_RX_DMA BIT(13)
++#define MTK_WED_RESET_WDMA_TX_DRV BIT(16)
+ #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)
+@@ -158,6 +162,8 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RESET_IDX 0x20c
+ #define MTK_WED_RESET_IDX_TX GENMASK(3, 0)
+ #define MTK_WED_RESET_IDX_RX GENMASK(17, 16)
++#define MTK_WED_RESET_IDX_RX_V2 GENMASK(7, 6)
++#define MTK_WED_RESET_WPDMA_IDX_RX GENMASK(31, 30)
+
+ #define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4)
+ #define MTK_WED_RX_MIB(_n) (0x2e0 + (_n) * 4)
+@@ -267,6 +273,9 @@ struct mtk_wdma_desc {
+
+ #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_RX_DRV_BUSY BIT(1)
++#define MTK_WED_WPDMA_RX_D_FSM_RETURN_IDLE BIT(3)
++#define MTK_WED_WPDMA_RX_D_RST_INIT_COMPLETE BIT(4)
+ #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)
+
diff --git a/target/linux/generic/backport-6.1/729-14-v6.2-net-ethernet-mtk_wed-add-reset-to-tx_ring_setup-call.patch b/target/linux/generic/backport-6.1/729-14-v6.2-net-ethernet-mtk_wed-add-reset-to-tx_ring_setup-call.patch
new file mode 100644
index 0000000000..602483bcb8
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-14-v6.2-net-ethernet-mtk_wed-add-reset-to-tx_ring_setup-call.patch
@@ -0,0 +1,103 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 24 Nov 2022 16:22:55 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: add reset to tx_ring_setup callback
+
+Introduce reset parameter to mtk_wed_tx_ring_setup signature.
+This is a preliminary patch to add Wireless Ethernet Dispatcher reset
+support.
+
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -1188,7 +1188,8 @@ mtk_wed_ring_alloc(struct mtk_wed_device
+ }
+
+ static int
+-mtk_wed_wdma_rx_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,
++ bool reset)
+ {
+ u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
+ struct mtk_wed_ring *wdma;
+@@ -1197,8 +1198,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we
+ return -EINVAL;
+
+ wdma = &dev->rx_wdma[idx];
+- if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size,
+- true))
++ if (!reset && 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,
+@@ -1396,7 +1397,7 @@ mtk_wed_start(struct mtk_wed_device *dev
+
+ 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_wdma_rx_ring_setup(dev, i, 16, false);
+
+ mtk_wed_hw_init(dev);
+ mtk_wed_configure_irq(dev, irq_mask);
+@@ -1505,7 +1506,8 @@ unlock:
+ }
+
+ static int
+-mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
++mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs,
++ bool reset)
+ {
+ struct mtk_wed_ring *ring = &dev->tx_ring[idx];
+
+@@ -1524,11 +1526,12 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
+ 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), true))
++ if (!reset && mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE,
++ sizeof(*ring->desc), true))
+ return -ENOMEM;
+
+- if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE))
++ if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE,
++ reset))
+ 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
+@@ -158,7 +158,7 @@ struct mtk_wed_device {
+ 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);
++ void __iomem *regs, bool reset);
+ int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring,
+ void __iomem *regs);
+ int (*txfree_ring_setup)(struct mtk_wed_device *dev,
+@@ -216,8 +216,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_devic
+ #define mtk_wed_device_active(_dev) !!(_dev)->ops
+ #define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev)
+ #define mtk_wed_device_start(_dev, _mask) (_dev)->ops->start(_dev, _mask)
+-#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) \
+- (_dev)->ops->tx_ring_setup(_dev, _ring, _regs)
++#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs, _reset) \
++ (_dev)->ops->tx_ring_setup(_dev, _ring, _regs, _reset)
+ #define mtk_wed_device_txfree_ring_setup(_dev, _regs) \
+ (_dev)->ops->txfree_ring_setup(_dev, _regs)
+ #define mtk_wed_device_reg_read(_dev, _reg) \
+@@ -243,7 +243,7 @@ static inline bool mtk_wed_device_active
+ }
+ #define mtk_wed_device_detach(_dev) do {} while (0)
+ #define mtk_wed_device_start(_dev, _mask) do {} while (0)
+-#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) -ENODEV
++#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs, _reset) -ENODEV
+ #define mtk_wed_device_txfree_ring_setup(_dev, _ring, _regs) -ENODEV
+ #define mtk_wed_device_reg_read(_dev, _reg) 0
+ #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0)
diff --git a/target/linux/generic/backport-6.1/729-15-v6.2-net-ethernet-mtk_wed-fix-sleep-while-atomic-in-mtk_w.patch b/target/linux/generic/backport-6.1/729-15-v6.2-net-ethernet-mtk_wed-fix-sleep-while-atomic-in-mtk_w.patch
new file mode 100644
index 0000000000..f9b11326b1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-15-v6.2-net-ethernet-mtk_wed-fix-sleep-while-atomic-in-mtk_w.patch
@@ -0,0 +1,103 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 1 Dec 2022 16:26:53 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: fix sleep while atomic in
+ mtk_wed_wo_queue_refill
+
+In order to fix the following sleep while atomic bug always alloc pages
+with GFP_ATOMIC in mtk_wed_wo_queue_refill since page_frag_alloc runs in
+spin_lock critical section.
+
+[ 9.049719] Hardware name: MediaTek MT7986a RFB (DT)
+[ 9.054665] Call trace:
+[ 9.057096] dump_backtrace+0x0/0x154
+[ 9.060751] show_stack+0x14/0x1c
+[ 9.064052] dump_stack_lvl+0x64/0x7c
+[ 9.067702] dump_stack+0x14/0x2c
+[ 9.071001] ___might_sleep+0xec/0x120
+[ 9.074736] __might_sleep+0x4c/0x9c
+[ 9.078296] __alloc_pages+0x184/0x2e4
+[ 9.082030] page_frag_alloc_align+0x98/0x1ac
+[ 9.086369] mtk_wed_wo_queue_refill+0x134/0x234
+[ 9.090974] mtk_wed_wo_init+0x174/0x2c0
+[ 9.094881] mtk_wed_attach+0x7c8/0x7e0
+[ 9.098701] mt7915_mmio_wed_init+0x1f0/0x3a0 [mt7915e]
+[ 9.103940] mt7915_pci_probe+0xec/0x3bc [mt7915e]
+[ 9.108727] pci_device_probe+0xac/0x13c
+[ 9.112638] really_probe.part.0+0x98/0x2f4
+[ 9.116807] __driver_probe_device+0x94/0x13c
+[ 9.121147] driver_probe_device+0x40/0x114
+[ 9.125314] __driver_attach+0x7c/0x180
+[ 9.129133] bus_for_each_dev+0x5c/0x90
+[ 9.132953] driver_attach+0x20/0x2c
+[ 9.136513] bus_add_driver+0x104/0x1fc
+[ 9.140333] driver_register+0x74/0x120
+[ 9.144153] __pci_register_driver+0x40/0x50
+[ 9.148407] mt7915_init+0x5c/0x1000 [mt7915e]
+[ 9.152848] do_one_initcall+0x40/0x25c
+[ 9.156669] do_init_module+0x44/0x230
+[ 9.160403] load_module+0x1f30/0x2750
+[ 9.164135] __do_sys_init_module+0x150/0x200
+[ 9.168475] __arm64_sys_init_module+0x18/0x20
+[ 9.172901] invoke_syscall.constprop.0+0x4c/0xe0
+[ 9.177589] do_el0_svc+0x48/0xe0
+[ 9.180889] el0_svc+0x14/0x50
+[ 9.183929] el0t_64_sync_handler+0x9c/0x120
+[ 9.188183] el0t_64_sync+0x158/0x15c
+
+Fixes: 799684448e3e ("net: ethernet: mtk_wed: introduce wed wo support")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
+Link: https://lore.kernel.org/r/67ca94bdd3d9eaeb86e52b3050fbca0bcf7bb02f.1669908312.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
+@@ -133,17 +133,18 @@ mtk_wed_wo_dequeue(struct mtk_wed_wo *wo
+
+ static int
+ mtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
+- gfp_t gfp, bool rx)
++ bool rx)
+ {
+ enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ int n_buf = 0;
+
+ 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;
++ void *buf;
+
++ buf = page_frag_alloc(&q->cache, q->buf_size, GFP_ATOMIC);
+ if (!buf)
+ break;
+
+@@ -215,7 +216,7 @@ mtk_wed_wo_rx_run_queue(struct mtk_wed_w
+ mtk_wed_mcu_rx_unsolicited_event(wo, skb);
+ }
+
+- if (mtk_wed_wo_queue_refill(wo, q, GFP_ATOMIC, true)) {
++ if (mtk_wed_wo_queue_refill(wo, q, true)) {
+ u32 index = (q->head - 1) % q->n_desc;
+
+ mtk_wed_wo_queue_kick(wo, q, index);
+@@ -432,7 +433,7 @@ mtk_wed_wo_hardware_init(struct mtk_wed_
+ if (ret)
+ goto error;
+
+- mtk_wed_wo_queue_refill(wo, &wo->q_tx, GFP_KERNEL, false);
++ mtk_wed_wo_queue_refill(wo, &wo->q_tx, false);
+ mtk_wed_wo_queue_reset(wo, &wo->q_tx);
+
+ regs.desc_base = MTK_WED_WO_CCIF_DUMMY5;
+@@ -446,7 +447,7 @@ mtk_wed_wo_hardware_init(struct mtk_wed_
+ if (ret)
+ goto error;
+
+- mtk_wed_wo_queue_refill(wo, &wo->q_rx, GFP_KERNEL, true);
++ mtk_wed_wo_queue_refill(wo, &wo->q_rx, true);
+ mtk_wed_wo_queue_reset(wo, &wo->q_rx);
+
+ /* rx queue irqmask */
diff --git a/target/linux/generic/backport-6.1/729-16-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-rx-qu.patch b/target/linux/generic/backport-6.1/729-16-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-rx-qu.patch
new file mode 100644
index 0000000000..fa6f56dbe7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-16-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-rx-qu.patch
@@ -0,0 +1,52 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Tue, 10 Jan 2023 10:31:26 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: get rid of queue lock for rx queue
+
+Queue spinlock is currently held in mtk_wed_wo_queue_rx_clean and
+mtk_wed_wo_queue_refill routines for MTK Wireless Ethernet Dispatcher
+MCU rx queue. mtk_wed_wo_queue_refill() is running during initialization
+and in rx tasklet while mtk_wed_wo_queue_rx_clean() is running in
+mtk_wed_wo_hw_deinit() during hw de-init phase after rx tasklet has been
+disabled. Since mtk_wed_wo_queue_rx_clean and mtk_wed_wo_queue_refill
+routines can't run concurrently get rid of spinlock for mcu rx queue.
+
+Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/36ec3b729542ea60898471d890796f745479ba32.1673342990.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
+@@ -138,7 +138,6 @@ mtk_wed_wo_queue_refill(struct mtk_wed_w
+ enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ int n_buf = 0;
+
+- spin_lock_bh(&q->lock);
+ while (q->queued < q->n_desc) {
+ struct mtk_wed_wo_queue_entry *entry;
+ dma_addr_t addr;
+@@ -172,7 +171,6 @@ mtk_wed_wo_queue_refill(struct mtk_wed_w
+ q->queued++;
+ n_buf++;
+ }
+- spin_unlock_bh(&q->lock);
+
+ return n_buf;
+ }
+@@ -316,7 +314,6 @@ mtk_wed_wo_queue_rx_clean(struct mtk_wed
+ {
+ struct page *page;
+
+- spin_lock_bh(&q->lock);
+ for (;;) {
+ void *buf = mtk_wed_wo_dequeue(wo, q, NULL, true);
+
+@@ -325,7 +322,6 @@ mtk_wed_wo_queue_rx_clean(struct mtk_wed
+
+ skb_free_frag(buf);
+ }
+- spin_unlock_bh(&q->lock);
+
+ if (!q->cache.va)
+ return;
diff --git a/target/linux/generic/backport-6.1/729-17-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-tx-qu.patch b/target/linux/generic/backport-6.1/729-17-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-tx-qu.patch
new file mode 100644
index 0000000000..9b1e4c3250
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-17-v6.3-net-ethernet-mtk_wed-get-rid-of-queue-lock-for-tx-qu.patch
@@ -0,0 +1,75 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 12 Jan 2023 10:21:29 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: get rid of queue lock for tx queue
+
+Similar to MTK Wireless Ethernet Dispatcher (WED) MCU rx queue,
+we do not need to protect WED MCU tx queue with a spin lock since
+the tx queue is accessed in the two following routines:
+- mtk_wed_wo_queue_tx_skb():
+ it is run at initialization and during mt7915 normal operation.
+ Moreover MCU messages are serialized through MCU mutex.
+- mtk_wed_wo_queue_tx_clean():
+ it runs just at mt7915 driver module unload when no more messages
+ are sent to the MCU.
+
+Remove tx queue spinlock.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/7bd0337b2a13ab1a63673b7c03fd35206b3b284e.1673515140.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
+@@ -258,7 +258,6 @@ mtk_wed_wo_queue_alloc(struct mtk_wed_wo
+ 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;
+@@ -290,7 +289,6 @@ mtk_wed_wo_queue_tx_clean(struct mtk_wed
+ 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];
+
+@@ -299,7 +297,6 @@ mtk_wed_wo_queue_tx_clean(struct mtk_wed
+ skb_free_frag(entry->buf);
+ entry->buf = NULL;
+ }
+- spin_unlock_bh(&q->lock);
+
+ if (!q->cache.va)
+ return;
+@@ -347,8 +344,6 @@ int mtk_wed_wo_queue_tx_skb(struct mtk_w
+ 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) {
+@@ -379,8 +374,6 @@ int mtk_wed_wo_queue_tx_skb(struct mtk_w
+ 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;
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
+@@ -211,7 +211,6 @@ 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;
diff --git a/target/linux/generic/backport-6.1/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch b/target/linux/generic/backport-6.1/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch
new file mode 100644
index 0000000000..45b85c06cb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch
@@ -0,0 +1,70 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 14 Jan 2023 18:01:28 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce mtk_hw_reset utility
+ routine
+
+This is a preliminary patch to add Wireless Ethernet Dispatcher reset
+support.
+
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3200,6 +3200,27 @@ static void mtk_set_mcr_max_rx(struct mt
+ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
+ }
+
++static void mtk_hw_reset(struct mtk_eth *eth)
++{
++ u32 val;
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0);
++ val = RSTCTRL_PPE0_V2;
++ } else {
++ val = RSTCTRL_PPE0;
++ }
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
++ val |= RSTCTRL_PPE1;
++
++ ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val);
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
++ 0x3ffffff);
++}
++
+ static int mtk_hw_init(struct mtk_eth *eth)
+ {
+ u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
+@@ -3239,22 +3260,9 @@ static int mtk_hw_init(struct mtk_eth *e
+ return 0;
+ }
+
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+- regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0);
+- val = RSTCTRL_PPE0_V2;
+- } else {
+- val = RSTCTRL_PPE0;
+- }
+-
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
+- val |= RSTCTRL_PPE1;
+-
+- ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val);
++ mtk_hw_reset(eth);
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+- regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN,
+- 0x3ffffff);
+-
+ /* Set FE to PDMAv2 if necessary */
+ val = mtk_r32(eth, MTK_FE_GLO_MISC);
+ mtk_w32(eth, val | BIT(4), MTK_FE_GLO_MISC);
diff --git a/target/linux/generic/backport-6.1/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch b/target/linux/generic/backport-6.1/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch
new file mode 100644
index 0000000000..c34e30c773
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch
@@ -0,0 +1,107 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 14 Jan 2023 18:01:29 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce mtk_hw_warm_reset
+ support
+
+Introduce mtk_hw_warm_reset utility routine. This is a preliminary patch
+to align reset procedure to vendor sdk and avoid to power down the chip
+during hw reset.
+
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3221,7 +3221,54 @@ static void mtk_hw_reset(struct mtk_eth
+ 0x3ffffff);
+ }
+
+-static int mtk_hw_init(struct mtk_eth *eth)
++static u32 mtk_hw_reset_read(struct mtk_eth *eth)
++{
++ u32 val;
++
++ regmap_read(eth->ethsys, ETHSYS_RSTCTRL, &val);
++ return val;
++}
++
++static void mtk_hw_warm_reset(struct mtk_eth *eth)
++{
++ u32 rst_mask, val;
++
++ regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, RSTCTRL_FE,
++ RSTCTRL_FE);
++ if (readx_poll_timeout_atomic(mtk_hw_reset_read, eth, val,
++ val & RSTCTRL_FE, 1, 1000)) {
++ dev_err(eth->dev, "warm reset failed\n");
++ mtk_hw_reset(eth);
++ return;
++ }
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0_V2;
++ else
++ rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0;
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
++ rst_mask |= RSTCTRL_PPE1;
++
++ regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, rst_mask, rst_mask);
++
++ udelay(1);
++ val = mtk_hw_reset_read(eth);
++ if (!(val & rst_mask))
++ dev_err(eth->dev, "warm reset stage0 failed %08x (%08x)\n",
++ val, rst_mask);
++
++ rst_mask |= RSTCTRL_FE;
++ regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, rst_mask, ~rst_mask);
++
++ udelay(1);
++ val = mtk_hw_reset_read(eth);
++ if (val & rst_mask)
++ dev_err(eth->dev, "warm reset stage1 failed %08x (%08x)\n",
++ val, rst_mask);
++}
++
++static int mtk_hw_init(struct mtk_eth *eth, bool reset)
+ {
+ u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
+ ETHSYS_DMA_AG_MAP_PPE;
+@@ -3260,7 +3307,12 @@ static int mtk_hw_init(struct mtk_eth *e
+ return 0;
+ }
+
+- mtk_hw_reset(eth);
++ msleep(100);
++
++ if (reset)
++ mtk_hw_warm_reset(eth);
++ else
++ mtk_hw_reset(eth);
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+ /* Set FE to PDMAv2 if necessary */
+@@ -3465,7 +3517,7 @@ static void mtk_pending_work(struct work
+ if (eth->dev->pins)
+ pinctrl_select_state(eth->dev->pins->p,
+ eth->dev->pins->default_state);
+- mtk_hw_init(eth);
++ mtk_hw_init(eth, true);
+
+ /* restart DMA and enable IRQs */
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+@@ -4057,7 +4109,7 @@ static int mtk_probe(struct platform_dev
+ eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
+ INIT_WORK(&eth->pending_work, mtk_pending_work);
+
+- err = mtk_hw_init(eth);
++ err = mtk_hw_init(eth, false);
+ if (err)
+ goto err_wed_exit;
+
diff --git a/target/linux/generic/backport-6.1/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch b/target/linux/generic/backport-6.1/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch
new file mode 100644
index 0000000000..29a5b62739
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch
@@ -0,0 +1,262 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 14 Jan 2023 18:01:30 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: align reset procedure to vendor
+ sdk
+
+Avoid to power-down the ethernet chip during hw reset and align reset
+procedure to vendor sdk.
+
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2786,14 +2786,29 @@ static void mtk_dma_free(struct mtk_eth
+ kfree(eth->scratch_head);
+ }
+
++static bool mtk_hw_reset_check(struct mtk_eth *eth)
++{
++ u32 val = mtk_r32(eth, MTK_INT_STATUS2);
++
++ return (val & MTK_FE_INT_FQ_EMPTY) || (val & MTK_FE_INT_RFIFO_UF) ||
++ (val & MTK_FE_INT_RFIFO_OV) || (val & MTK_FE_INT_TSO_FAIL) ||
++ (val & MTK_FE_INT_TSO_ALIGN) || (val & MTK_FE_INT_TSO_ILLEGAL);
++}
++
+ static void mtk_tx_timeout(struct net_device *dev, unsigned int txqueue)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
++ if (test_bit(MTK_RESETTING, &eth->state))
++ return;
++
++ if (!mtk_hw_reset_check(eth))
++ return;
++
+ eth->netdev[mac->id]->stats.tx_errors++;
+- netif_err(eth, tx_err, dev,
+- "transmit timed out\n");
++ netif_err(eth, tx_err, dev, "transmit timed out\n");
++
+ schedule_work(&eth->pending_work);
+ }
+
+@@ -3275,15 +3290,17 @@ static int mtk_hw_init(struct mtk_eth *e
+ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ int i, val, ret;
+
+- if (test_and_set_bit(MTK_HW_INIT, &eth->state))
++ if (!reset && test_and_set_bit(MTK_HW_INIT, &eth->state))
+ return 0;
+
+- pm_runtime_enable(eth->dev);
+- pm_runtime_get_sync(eth->dev);
++ if (!reset) {
++ pm_runtime_enable(eth->dev);
++ pm_runtime_get_sync(eth->dev);
+
+- ret = mtk_clk_enable(eth);
+- if (ret)
+- goto err_disable_pm;
++ ret = mtk_clk_enable(eth);
++ if (ret)
++ goto err_disable_pm;
++ }
+
+ if (eth->ethsys)
+ regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask,
+@@ -3409,8 +3426,10 @@ static int mtk_hw_init(struct mtk_eth *e
+ return 0;
+
+ err_disable_pm:
+- pm_runtime_put_sync(eth->dev);
+- pm_runtime_disable(eth->dev);
++ if (!reset) {
++ pm_runtime_put_sync(eth->dev);
++ pm_runtime_disable(eth->dev);
++ }
+
+ return ret;
+ }
+@@ -3489,30 +3508,53 @@ static int mtk_do_ioctl(struct net_devic
+ return -EOPNOTSUPP;
+ }
+
++static void mtk_prepare_for_reset(struct mtk_eth *eth)
++{
++ u32 val;
++ int i;
++
++ /* disabe FE P3 and P4 */
++ val = mtk_r32(eth, MTK_FE_GLO_CFG) | MTK_FE_LINK_DOWN_P3;
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
++ val |= MTK_FE_LINK_DOWN_P4;
++ mtk_w32(eth, val, MTK_FE_GLO_CFG);
++
++ /* adjust PPE configurations to prepare for reset */
++ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
++ mtk_ppe_prepare_reset(eth->ppe[i]);
++
++ /* disable NETSYS interrupts */
++ mtk_w32(eth, 0, MTK_FE_INT_ENABLE);
++
++ /* force link down GMAC */
++ for (i = 0; i < 2; i++) {
++ val = mtk_r32(eth, MTK_MAC_MCR(i)) & ~MAC_MCR_FORCE_LINK;
++ mtk_w32(eth, val, MTK_MAC_MCR(i));
++ }
++}
++
+ static void mtk_pending_work(struct work_struct *work)
+ {
+ struct mtk_eth *eth = container_of(work, struct mtk_eth, pending_work);
+- int err, i;
+ unsigned long restart = 0;
++ u32 val;
++ int i;
+
+ rtnl_lock();
+-
+- dev_dbg(eth->dev, "[%s][%d] reset\n", __func__, __LINE__);
+ set_bit(MTK_RESETTING, &eth->state);
+
++ mtk_prepare_for_reset(eth);
++
+ /* stop all devices to make sure that dma is properly shut down */
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+- if (!eth->netdev[i])
++ if (!eth->netdev[i] || !netif_running(eth->netdev[i]))
+ continue;
++
+ mtk_stop(eth->netdev[i]);
+ __set_bit(i, &restart);
+ }
+- dev_dbg(eth->dev, "[%s][%d] mtk_stop ends\n", __func__, __LINE__);
+
+- /* restart underlying hardware such as power, clock, pin mux
+- * and the connected phy
+- */
+- mtk_hw_deinit(eth);
++ usleep_range(15000, 16000);
+
+ if (eth->dev->pins)
+ pinctrl_select_state(eth->dev->pins->p,
+@@ -3523,15 +3565,19 @@ static void mtk_pending_work(struct work
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+ if (!test_bit(i, &restart))
+ continue;
+- err = mtk_open(eth->netdev[i]);
+- if (err) {
++
++ if (mtk_open(eth->netdev[i])) {
+ netif_alert(eth, ifup, eth->netdev[i],
+- "Driver up/down cycle failed, closing device.\n");
++ "Driver up/down cycle failed\n");
+ dev_close(eth->netdev[i]);
+ }
+ }
+
+- dev_dbg(eth->dev, "[%s][%d] reset done\n", __func__, __LINE__);
++ /* enabe FE P3 and P4 */
++ val = mtk_r32(eth, MTK_FE_GLO_CFG) & ~MTK_FE_LINK_DOWN_P3;
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1))
++ val &= ~MTK_FE_LINK_DOWN_P4;
++ mtk_w32(eth, val, MTK_FE_GLO_CFG);
+
+ clear_bit(MTK_RESETTING, &eth->state);
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -72,12 +72,24 @@
+ #define MTK_HW_LRO_REPLACE_DELTA 1000
+ #define MTK_HW_LRO_SDL_REMAIN_ROOM 1522
+
++/* Frame Engine Global Configuration */
++#define MTK_FE_GLO_CFG 0x00
++#define MTK_FE_LINK_DOWN_P3 BIT(11)
++#define MTK_FE_LINK_DOWN_P4 BIT(12)
++
+ /* Frame Engine Global Reset Register */
+ #define MTK_RST_GL 0x04
+ #define RST_GL_PSE BIT(0)
+
+ /* Frame Engine Interrupt Status Register */
+ #define MTK_INT_STATUS2 0x08
++#define MTK_FE_INT_ENABLE 0x0c
++#define MTK_FE_INT_FQ_EMPTY BIT(8)
++#define MTK_FE_INT_TSO_FAIL BIT(12)
++#define MTK_FE_INT_TSO_ILLEGAL BIT(13)
++#define MTK_FE_INT_TSO_ALIGN BIT(14)
++#define MTK_FE_INT_RFIFO_OV BIT(18)
++#define MTK_FE_INT_RFIFO_UF BIT(19)
+ #define MTK_GDM1_AF BIT(28)
+ #define MTK_GDM2_AF BIT(29)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -716,6 +716,33 @@ int mtk_foe_entry_idle_time(struct mtk_p
+ return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+ }
+
++int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
++{
++ if (!ppe)
++ return -EINVAL;
++
++ /* disable KA */
++ ppe_clear(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_KEEPALIVE);
++ ppe_clear(ppe, MTK_PPE_BIND_LMT1, MTK_PPE_NTU_KEEPALIVE);
++ ppe_w32(ppe, MTK_PPE_KEEPALIVE, 0);
++ usleep_range(10000, 11000);
++
++ /* set KA timer to maximum */
++ ppe_set(ppe, MTK_PPE_BIND_LMT1, MTK_PPE_NTU_KEEPALIVE);
++ ppe_w32(ppe, MTK_PPE_KEEPALIVE, 0xffffffff);
++
++ /* set KA tick select */
++ ppe_set(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_TICK_SEL);
++ ppe_set(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_KEEPALIVE);
++ usleep_range(10000, 11000);
++
++ /* disable scan mode */
++ ppe_clear(ppe, MTK_PPE_TB_CFG, MTK_PPE_TB_CFG_SCAN_MODE);
++ usleep_range(10000, 11000);
++
++ return mtk_ppe_wait_busy(ppe);
++}
++
+ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+ int version, int index)
+ {
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -306,6 +306,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+ int version, int index);
+ void mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
++int mtk_ppe_prepare_reset(struct mtk_ppe *ppe);
+
+ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+@@ -58,6 +58,12 @@
+ #define MTK_PPE_TB_CFG_SCAN_MODE GENMASK(17, 16)
+ #define MTK_PPE_TB_CFG_HASH_DEBUG GENMASK(19, 18)
+ #define MTK_PPE_TB_CFG_INFO_SEL BIT(20)
++#define MTK_PPE_TB_TICK_SEL BIT(24)
++
++#define MTK_PPE_BIND_LMT1 0x230
++#define MTK_PPE_NTU_KEEPALIVE GENMASK(23, 16)
++
++#define MTK_PPE_KEEPALIVE 0x234
+
+ enum {
+ MTK_PPE_SCAN_MODE_DISABLED,
diff --git a/target/linux/generic/backport-6.1/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch b/target/linux/generic/backport-6.1/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch
new file mode 100644
index 0000000000..cebb750bf6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch
@@ -0,0 +1,249 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 14 Jan 2023 18:01:31 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add dma checks to
+ mtk_hw_reset_check
+
+Introduce mtk_hw_check_dma_hang routine to monitor possible dma hangs.
+
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -50,6 +50,7 @@ static const struct mtk_reg_map mtk_reg_
+ .delay_irq = 0x0a0c,
+ .irq_status = 0x0a20,
+ .irq_mask = 0x0a28,
++ .adma_rx_dbg0 = 0x0a38,
+ .int_grp = 0x0a50,
+ },
+ .qdma = {
+@@ -79,6 +80,8 @@ static const struct mtk_reg_map mtk_reg_
+ [0] = 0x2800,
+ [1] = 0x2c00,
+ },
++ .pse_iq_sta = 0x0110,
++ .pse_oq_sta = 0x0118,
+ };
+
+ static const struct mtk_reg_map mt7628_reg_map = {
+@@ -109,6 +112,7 @@ static const struct mtk_reg_map mt7986_r
+ .delay_irq = 0x620c,
+ .irq_status = 0x6220,
+ .irq_mask = 0x6228,
++ .adma_rx_dbg0 = 0x6238,
+ .int_grp = 0x6250,
+ },
+ .qdma = {
+@@ -138,6 +142,8 @@ static const struct mtk_reg_map mt7986_r
+ [0] = 0x4800,
+ [1] = 0x4c00,
+ },
++ .pse_iq_sta = 0x0180,
++ .pse_oq_sta = 0x01a0,
+ };
+
+ /* strings used by ethtool */
+@@ -3283,6 +3289,102 @@ static void mtk_hw_warm_reset(struct mtk
+ val, rst_mask);
+ }
+
++static bool mtk_hw_check_dma_hang(struct mtk_eth *eth)
++{
++ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
++ bool gmac1_tx, gmac2_tx, gdm1_tx, gdm2_tx;
++ bool oq_hang, cdm1_busy, adma_busy;
++ bool wtx_busy, cdm_full, oq_free;
++ u32 wdidx, val, gdm1_fc, gdm2_fc;
++ bool qfsm_hang, qfwd_hang;
++ bool ret = false;
++
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
++ return false;
++
++ /* WDMA sanity checks */
++ wdidx = mtk_r32(eth, reg_map->wdma_base[0] + 0xc);
++
++ val = mtk_r32(eth, reg_map->wdma_base[0] + 0x204);
++ wtx_busy = FIELD_GET(MTK_TX_DMA_BUSY, val);
++
++ val = mtk_r32(eth, reg_map->wdma_base[0] + 0x230);
++ cdm_full = !FIELD_GET(MTK_CDM_TXFIFO_RDY, val);
++
++ oq_free = (!(mtk_r32(eth, reg_map->pse_oq_sta) & GENMASK(24, 16)) &&
++ !(mtk_r32(eth, reg_map->pse_oq_sta + 0x4) & GENMASK(8, 0)) &&
++ !(mtk_r32(eth, reg_map->pse_oq_sta + 0x10) & GENMASK(24, 16)));
++
++ if (wdidx == eth->reset.wdidx && wtx_busy && cdm_full && oq_free) {
++ if (++eth->reset.wdma_hang_count > 2) {
++ eth->reset.wdma_hang_count = 0;
++ ret = true;
++ }
++ goto out;
++ }
++
++ /* QDMA sanity checks */
++ qfsm_hang = !!mtk_r32(eth, reg_map->qdma.qtx_cfg + 0x234);
++ qfwd_hang = !mtk_r32(eth, reg_map->qdma.qtx_cfg + 0x308);
++
++ gdm1_tx = FIELD_GET(GENMASK(31, 16), mtk_r32(eth, MTK_FE_GDM1_FSM)) > 0;
++ gdm2_tx = FIELD_GET(GENMASK(31, 16), mtk_r32(eth, MTK_FE_GDM2_FSM)) > 0;
++ gmac1_tx = FIELD_GET(GENMASK(31, 24), mtk_r32(eth, MTK_MAC_FSM(0))) != 1;
++ gmac2_tx = FIELD_GET(GENMASK(31, 24), mtk_r32(eth, MTK_MAC_FSM(1))) != 1;
++ gdm1_fc = mtk_r32(eth, reg_map->gdm1_cnt + 0x24);
++ gdm2_fc = mtk_r32(eth, reg_map->gdm1_cnt + 0x64);
++
++ if (qfsm_hang && qfwd_hang &&
++ ((gdm1_tx && gmac1_tx && gdm1_fc < 1) ||
++ (gdm2_tx && gmac2_tx && gdm2_fc < 1))) {
++ if (++eth->reset.qdma_hang_count > 2) {
++ eth->reset.qdma_hang_count = 0;
++ ret = true;
++ }
++ goto out;
++ }
++
++ /* ADMA sanity checks */
++ oq_hang = !!(mtk_r32(eth, reg_map->pse_oq_sta) & GENMASK(8, 0));
++ cdm1_busy = !!(mtk_r32(eth, MTK_FE_CDM1_FSM) & GENMASK(31, 16));
++ adma_busy = !(mtk_r32(eth, reg_map->pdma.adma_rx_dbg0) & GENMASK(4, 0)) &&
++ !(mtk_r32(eth, reg_map->pdma.adma_rx_dbg0) & BIT(6));
++
++ if (oq_hang && cdm1_busy && adma_busy) {
++ if (++eth->reset.adma_hang_count > 2) {
++ eth->reset.adma_hang_count = 0;
++ ret = true;
++ }
++ goto out;
++ }
++
++ eth->reset.wdma_hang_count = 0;
++ eth->reset.qdma_hang_count = 0;
++ eth->reset.adma_hang_count = 0;
++out:
++ eth->reset.wdidx = wdidx;
++
++ return ret;
++}
++
++static void mtk_hw_reset_monitor_work(struct work_struct *work)
++{
++ struct delayed_work *del_work = to_delayed_work(work);
++ struct mtk_eth *eth = container_of(del_work, struct mtk_eth,
++ reset.monitor_work);
++
++ if (test_bit(MTK_RESETTING, &eth->state))
++ goto out;
++
++ /* DMA stuck checks */
++ if (mtk_hw_check_dma_hang(eth))
++ schedule_work(&eth->pending_work);
++
++out:
++ schedule_delayed_work(&eth->reset.monitor_work,
++ MTK_DMA_MONITOR_TIMEOUT);
++}
++
+ static int mtk_hw_init(struct mtk_eth *eth, bool reset)
+ {
+ u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
+@@ -3615,6 +3717,7 @@ static int mtk_cleanup(struct mtk_eth *e
+ mtk_unreg_dev(eth);
+ mtk_free_dev(eth);
+ cancel_work_sync(&eth->pending_work);
++ cancel_delayed_work_sync(&eth->reset.monitor_work);
+
+ return 0;
+ }
+@@ -4042,6 +4145,7 @@ static int mtk_probe(struct platform_dev
+
+ eth->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
+ INIT_WORK(&eth->rx_dim.work, mtk_dim_rx);
++ INIT_DELAYED_WORK(&eth->reset.monitor_work, mtk_hw_reset_monitor_work);
+
+ eth->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
+ INIT_WORK(&eth->tx_dim.work, mtk_dim_tx);
+@@ -4246,6 +4350,8 @@ static int mtk_probe(struct platform_dev
+ NAPI_POLL_WEIGHT);
+
+ platform_set_drvdata(pdev, eth);
++ schedule_delayed_work(&eth->reset.monitor_work,
++ MTK_DMA_MONITOR_TIMEOUT);
+
+ return 0;
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -256,6 +256,8 @@
+
+ #define MTK_RX_DONE_INT_V2 BIT(14)
+
++#define MTK_CDM_TXFIFO_RDY BIT(7)
++
+ /* QDMA Interrupt grouping registers */
+ #define MTK_RLS_DONE_INT BIT(0)
+
+@@ -538,6 +540,17 @@
+ #define MT7628_SDM_RBCNT (MT7628_SDM_OFFSET + 0x10c)
+ #define MT7628_SDM_CS_ERR (MT7628_SDM_OFFSET + 0x110)
+
++#define MTK_FE_CDM1_FSM 0x220
++#define MTK_FE_CDM2_FSM 0x224
++#define MTK_FE_CDM3_FSM 0x238
++#define MTK_FE_CDM4_FSM 0x298
++#define MTK_FE_CDM5_FSM 0x318
++#define MTK_FE_CDM6_FSM 0x328
++#define MTK_FE_GDM1_FSM 0x228
++#define MTK_FE_GDM2_FSM 0x22C
++
++#define MTK_MAC_FSM(x) (0x1010C + ((x) * 0x100))
++
+ struct mtk_rx_dma {
+ unsigned int rxd1;
+ unsigned int rxd2;
+@@ -934,6 +947,7 @@ struct mtk_reg_map {
+ u32 delay_irq; /* delay interrupt */
+ u32 irq_status; /* interrupt status */
+ u32 irq_mask; /* interrupt mask */
++ u32 adma_rx_dbg0;
+ u32 int_grp;
+ } pdma;
+ struct {
+@@ -960,6 +974,8 @@ struct mtk_reg_map {
+ u32 gdma_to_ppe0;
+ u32 ppe_base;
+ u32 wdma_base[2];
++ u32 pse_iq_sta;
++ u32 pse_oq_sta;
+ };
+
+ /* struct mtk_eth_data - This is the structure holding all differences
+@@ -1002,6 +1018,8 @@ struct mtk_soc_data {
+ } txrx;
+ };
+
++#define MTK_DMA_MONITOR_TIMEOUT msecs_to_jiffies(1000)
++
+ /* currently no SoC has more than 2 macs */
+ #define MTK_MAX_DEVS 2
+
+@@ -1124,6 +1142,14 @@ struct mtk_eth {
+ struct rhashtable flow_table;
+
+ struct bpf_prog __rcu *prog;
++
++ struct {
++ struct delayed_work monitor_work;
++ u32 wdidx;
++ u8 wdma_hang_count;
++ u8 qdma_hang_count;
++ u8 adma_hang_count;
++ } reset;
+ };
+
+ /* struct mtk_mac - the structure that holds the info about the MACs of the
diff --git a/target/linux/generic/backport-6.1/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch b/target/linux/generic/backport-6.1/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch
new file mode 100644
index 0000000000..2eca268670
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch
@@ -0,0 +1,124 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 14 Jan 2023 18:01:32 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: add reset/reset_complete callbacks
+
+Introduce reset and reset_complete wlan callback to schedule WLAN driver
+reset when ethernet/wed driver is resetting.
+
+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: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3646,6 +3646,11 @@ static void mtk_pending_work(struct work
+ set_bit(MTK_RESETTING, &eth->state);
+
+ mtk_prepare_for_reset(eth);
++ mtk_wed_fe_reset();
++ /* Run again reset preliminary configuration in order to avoid any
++ * possible race during FE reset since it can run releasing RTNL lock.
++ */
++ mtk_prepare_for_reset(eth);
+
+ /* stop all devices to make sure that dma is properly shut down */
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+@@ -3683,6 +3688,8 @@ static void mtk_pending_work(struct work
+
+ clear_bit(MTK_RESETTING, &eth->state);
+
++ mtk_wed_fe_reset_complete();
++
+ rtnl_unlock();
+ }
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -205,6 +205,48 @@ mtk_wed_wo_reset(struct mtk_wed_device *
+ iounmap(reg);
+ }
+
++void mtk_wed_fe_reset(void)
++{
++ int i;
++
++ mutex_lock(&hw_lock);
++
++ for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
++ struct mtk_wed_hw *hw = hw_list[i];
++ struct mtk_wed_device *dev = hw->wed_dev;
++ int err;
++
++ if (!dev || !dev->wlan.reset)
++ continue;
++
++ /* reset callback blocks until WLAN reset is completed */
++ err = dev->wlan.reset(dev);
++ if (err)
++ dev_err(dev->dev, "wlan reset failed: %d\n", err);
++ }
++
++ mutex_unlock(&hw_lock);
++}
++
++void mtk_wed_fe_reset_complete(void)
++{
++ int i;
++
++ mutex_lock(&hw_lock);
++
++ for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
++ struct mtk_wed_hw *hw = hw_list[i];
++ struct mtk_wed_device *dev = hw->wed_dev;
++
++ if (!dev || !dev->wlan.reset_complete)
++ continue;
++
++ dev->wlan.reset_complete(dev);
++ }
++
++ mutex_unlock(&hw_lock);
++}
++
+ static struct mtk_wed_hw *
+ mtk_wed_assign(struct mtk_wed_device *dev)
+ {
+--- a/drivers/net/ethernet/mediatek/mtk_wed.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
+@@ -128,6 +128,8 @@ void mtk_wed_add_hw(struct device_node *
+ void mtk_wed_exit(void);
+ int mtk_wed_flow_add(int index);
+ void mtk_wed_flow_remove(int index);
++void mtk_wed_fe_reset(void);
++void mtk_wed_fe_reset_complete(void);
+ #else
+ static inline void
+ mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+@@ -147,6 +149,13 @@ static inline void mtk_wed_flow_remove(i
+ {
+ }
+
++static inline void mtk_wed_fe_reset(void)
++{
++}
++
++static inline void mtk_wed_fe_reset_complete(void)
++{
++}
+ #endif
+
+ #ifdef CONFIG_DEBUG_FS
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -151,6 +151,8 @@ struct mtk_wed_device {
+ 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);
++ int (*reset)(struct mtk_wed_device *wed);
++ void (*reset_complete)(struct mtk_wed_device *wed);
+ } wlan;
+ #endif
+ };
diff --git a/target/linux/generic/backport-6.1/729-23-v6.3-net-ethernet-mtk_wed-add-reset-to-rx_ring_setup-call.patch b/target/linux/generic/backport-6.1/729-23-v6.3-net-ethernet-mtk_wed-add-reset-to-rx_ring_setup-call.patch
new file mode 100644
index 0000000000..cf81acf491
--- /dev/null
+++ b/target/linux/generic/backport-6.1/729-23-v6.3-net-ethernet-mtk_wed-add-reset-to-rx_ring_setup-call.patch
@@ -0,0 +1,106 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 5 Dec 2022 12:34:42 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: add reset to rx_ring_setup callback
+
+This patch adds reset parameter to mtk_wed_rx_ring_setup signature
+in order to align rx_ring_setup callback to tx_ring_setup one introduced
+in 'commit 23dca7a90017 ("net: ethernet: mtk_wed: add reset to
+tx_ring_setup callback")'
+
+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>
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+Link: https://lore.kernel.org/r/29c6e7a5469e784406cf3e2920351d1207713d05.1670239984.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -1259,7 +1259,8 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we
+ }
+
+ static int
+-mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size)
++mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
++ bool reset)
+ {
+ u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
+ struct mtk_wed_ring *wdma;
+@@ -1268,8 +1269,8 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we
+ return -EINVAL;
+
+ wdma = &dev->tx_wdma[idx];
+- if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size,
+- true))
++ if (!reset && 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,
+@@ -1279,6 +1280,9 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we
+ 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 (reset)
++ mtk_wed_ring_reset(wdma, MTK_WED_WDMA_RING_SIZE, true);
++
+ if (!idx) {
+ wed_w32(dev, MTK_WED_WDMA_RING_TX + MTK_WED_RING_OFS_BASE,
+ wdma->desc_phys);
+@@ -1618,18 +1622,20 @@ mtk_wed_txfree_ring_setup(struct mtk_wed
+ }
+
+ static int
+-mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
++mtk_wed_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs,
++ bool reset)
+ {
+ 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))
++ if (!reset && 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))
++ if (mtk_wed_wdma_tx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE,
++ reset))
+ return -ENOMEM;
+
+ ring->reg_base = MTK_WED_RING_RX_DATA(idx);
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -162,7 +162,7 @@ struct mtk_wed_ops {
+ int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring,
+ void __iomem *regs, bool reset);
+ int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring,
+- void __iomem *regs);
++ void __iomem *regs, bool reset);
+ int (*txfree_ring_setup)(struct mtk_wed_device *dev,
+ void __iomem *regs);
+ int (*msg_update)(struct mtk_wed_device *dev, int cmd_id,
+@@ -230,8 +230,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_devic
+ (_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_rx_ring_setup(_dev, _ring, _regs, _reset) \
++ (_dev)->ops->rx_ring_setup(_dev, _ring, _regs, _reset)
+ #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) \
+@@ -251,7 +251,7 @@ 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_rx_ring_setup(_dev, _ring, _regs, _reset) -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
+ #define mtk_wed_device_stop(_dev) do {} while (0)
diff --git a/target/linux/generic/backport-6.1/730-01-v6.3-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch b/target/linux/generic/backport-6.1/730-01-v6.3-net-ethernet-mtk_eth_soc-account-for-vlan-in-rx-head.patch
new file mode 100644
index 0000000000..45af898cf0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-01-v6.3-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
+
+The network stack assumes that devices can handle an extra VLAN tag without
+increasing the MTU
+
+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
+@@ -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/backport-6.1/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch b/target/linux/generic/backport-6.1/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch
new file mode 100644
index 0000000000..b3412df9e0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-02-v6.3-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
+@@ -901,7 +901,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;
+
+@@ -2155,19 +2155,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;
+
+- 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;
+@@ -2187,22 +2193,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;
+ }
+ }
+
+- 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
+@@ -2214,14 +2220,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);
+ }
+@@ -2239,7 +2245,7 @@ static void mtk_tx_clean(struct mtk_eth
+ int i;
+
+ 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;
+@@ -2247,14 +2253,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;
+ }
+
+ 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;
+ }
+@@ -2774,7 +2780,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/backport-6.1/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch b/target/linux/generic/backport-6.1/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch
new file mode 100644
index 0000000000..32dc5c3783
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-03-v6.3-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
+@@ -4428,7 +4428,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 = {
+@@ -4467,7 +4467,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
+@@ -175,6 +175,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) |
+@@ -182,7 +184,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);
+ }
+
diff --git a/target/linux/generic/backport-6.1/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch b/target/linux/generic/backport-6.1/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch
new file mode 100644
index 0000000000..cfbd510c8f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch
@@ -0,0 +1,654 @@
+From: Felix Fietkau <nbd@nbd.name>
+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>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -55,6 +55,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,
+@@ -62,6 +63,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,
+@@ -117,6 +119,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,
+@@ -134,6 +137,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,
+@@ -577,6 +581,75 @@ static void mtk_mac_link_down(struct phy
+ mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
+ }
+
++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;
++
++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA))
++ return;
++
++ 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;
++ }
++ }
++
++ ofs = MTK_QTX_OFFSET * idx;
++ mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
++}
++
+ static void mtk_mac_link_up(struct phylink_config *config,
+ struct phy_device *phy,
+ unsigned int mode, phy_interface_t interface,
+@@ -602,6 +675,8 @@ static void mtk_mac_link_up(struct phyli
+ break;
+ }
+
++ mtk_set_queue_speed(mac->hw, mac->id, speed);
++
+ /* Configure duplex */
+ if (duplex == DUPLEX_FULL)
+ mcr |= MAC_MCR_FORCE_DPX;
+@@ -1060,7 +1135,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);
+@@ -1094,9 +1170,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);
+@@ -1140,11 +1213,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;
+@@ -1152,8 +1226,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)
+@@ -1202,7 +1278,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,
+@@ -1241,7 +1317,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);
+@@ -1253,8 +1329,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;
+@@ -1323,7 +1398,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]);
+ }
+ }
+
+@@ -1347,7 +1422,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);
+@@ -1373,7 +1448,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);
+
+@@ -1540,10 +1615,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;
+@@ -1594,6 +1671,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++;
+@@ -1945,8 +2023,46 @@ rx_done:
+ return done;
+ }
+
++struct mtk_poll_state {
++ struct netdev_queue *txq;
++ unsigned int total;
++ unsigned int done;
++ unsigned int bytes;
++};
++
++static void
++mtk_poll_tx_done(struct mtk_eth *eth, struct mtk_poll_state *state, u8 mac,
++ struct sk_buff *skb)
++{
++ 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 int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget,
+- unsigned int *done, unsigned int *bytes)
++ struct mtk_poll_state *state)
+ {
+ const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+ struct mtk_tx_ring *ring = &eth->tx_ring;
+@@ -1976,12 +2092,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);
+@@ -1999,7 +2112,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
+ }
+
+ 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;
+@@ -2015,12 +2128,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);
+@@ -2041,26 +2150,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);
+@@ -2070,7 +2168,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)
+@@ -2156,6 +2254,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;
+@@ -2223,8 +2322,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);
+@@ -2904,7 +3020,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);
+@@ -2950,6 +3066,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)
++{
++ 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;
++}
++
+ static int mtk_open(struct net_device *dev)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+@@ -2994,7 +3149,8 @@ static int mtk_open(struct net_device *d
+ refcount_inc(&eth->dma_refcnt);
+
+ phylink_start(mac->phylink);
+- netif_start_queue(dev);
++ netif_tx_start_all_queues(dev);
++
+ return 0;
+ }
+
+@@ -3717,8 +3873,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]);
+ }
+
+@@ -3935,6 +4095,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)
++{
++ 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;
++}
++
+ static const struct ethtool_ops mtk_ethtool_ops = {
+ .get_link_ksettings = mtk_get_link_ksettings,
+ .set_link_ksettings = mtk_set_link_ksettings,
+@@ -3970,6 +4147,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,
+ };
+
+ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+@@ -3979,6 +4157,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");
+@@ -3996,7 +4175,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;
+@@ -4093,6 +4275,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
+@@ -215,8 +216,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)
+@@ -235,6 +254,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)
+@@ -265,8 +285,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
+
+@@ -296,6 +314,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)
+@@ -953,6 +972,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 */
+@@ -970,6 +990,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;
+@@ -1173,6 +1194,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/backport-6.1/730-05-v6.3-net-dsa-tag_mtk-assign-per-port-queues.patch b/target/linux/generic/backport-6.1/730-05-v6.3-net-dsa-tag_mtk-assign-per-port-queues.patch
new file mode 100644
index 0000000000..186df4bdc9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-05-v6.3-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
+
+Keeps traffic sent to the switch within link speed limits
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/dsa/tag_mtk.c
++++ b/net/dsa/tag_mtk.c
+@@ -25,6 +25,8 @@ static struct sk_buff *mtk_tag_xmit(stru
+ u8 xmit_tpid;
+ u8 *mtk_tag;
+
++ 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/backport-6.1/730-06-v6.3-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch b/target/linux/generic/backport-6.1/730-06-v6.3-net-ethernet-mediatek-ppe-assign-per-port-queues-for.patch
new file mode 100644
index 0000000000..8935eb673a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-06-v6.3-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
+
+Keeps traffic sent to the switch within link speed limits
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -405,6 +405,24 @@ static inline bool mtk_foe_entry_usable(
+ FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
+ }
+
++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);
++
++ 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;
++ }
++
++ return 0;
++}
++
+ 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
+@@ -68,7 +68,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)
+@@ -351,6 +353,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;
+
+ 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
+ }
+
+ dsa_port = mtk_flow_get_dsa_port(&dev);
+- if (dsa_port >= 0)
+- mtk_foe_entry_set_dsa(eth, foe, dsa_port);
+
+ if (dev == eth->netdev[0])
+ pse_port = 1;
+@@ -222,6 +220,14 @@ mtk_flow_set_output_device(struct mtk_et
+ else
+ return -EOPNOTSUPP;
+
++ if (dsa_port >= 0) {
++ mtk_foe_entry_set_dsa(eth, foe, dsa_port);
++ queue = 3 + dsa_port;
++ } else {
++ queue = pse_port - 1;
++ }
++ mtk_foe_entry_set_queue(eth, foe, queue);
++
+ out:
+ mtk_foe_entry_set_pse_port(eth, foe, pse_port);
+
diff --git a/target/linux/generic/backport-6.1/730-07-v6.3-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch b/target/linux/generic/backport-6.1/730-07-v6.3-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch
new file mode 100644
index 0000000000..44af9128b7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-07-v6.3-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
+
+Avoid some branches in the hot path on low-end devices with limited CPU power,
+and reduce code size
+
+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
+@@ -917,7 +917,13 @@ enum mkt_eth_capabilities {
+ #define MTK_MUX_GMAC12_TO_GEPHY_SGMII \
+ (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX)
+
+-#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/backport-6.1/730-08-v6.3-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch b/target/linux/generic/backport-6.1/730-08-v6.3-net-dsa-add-support-for-DSA-rx-offloading-via-metada.patch
new file mode 100644
index 0000000000..0478cb528e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-08-v6.3-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
+
+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: Felix Fietkau <nbd@nbd.name>
+---
+
+--- 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/backport-6.1/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch b/target/linux/generic/backport-6.1/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch
new file mode 100644
index 0000000000..beba07238f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-09-v6.3-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
+
+- 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: Felix Fietkau <nbd@nbd.name>
+---
+
+--- 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"
+@@ -1974,16 +1975,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));
+ }
++ }
++
++ /* 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);
+@@ -2800,15 +2807,30 @@ static netdev_features_t mtk_fix_feature
+
+ static int mtk_set_features(struct net_device *dev, netdev_features_t features)
+ {
+- 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);
+
+- if (!((dev->features ^ features) & NETIF_F_LRO))
++ /* Set RX VLAN offloading */
++ if (!(diff & NETIF_F_HW_VLAN_CTAG_RX))
+ return 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);
+
+- 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;
+ }
+
+ /* wait for DMA to finish whatever it is doing before we start using it again */
+@@ -3105,11 +3127,45 @@ found:
+ return NOTIFY_DONE;
+ }
+
++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);
++ }
+
+ err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
+ if (err) {
+@@ -3632,6 +3688,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);
++ }
+
+ /* Enable RX VLan Offloading */
+ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
+@@ -3865,6 +3925,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;
+ }
+
+--- 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"
+
++#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
+@@ -105,6 +108,9 @@
+ #define MTK_CDMQ_IG_CTRL 0x1400
+ #define MTK_CDMQ_STAG_EN BIT(0)
+
++/* 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)
+@@ -1166,6 +1172,8 @@ struct mtk_eth {
+
+ int ip_align;
+
++ struct metadata_dst *dsa_meta[MTK_MAX_DSA_PORTS];
++
+ struct mtk_ppe *ppe[2];
+ struct rhashtable flow_table;
+
diff --git a/target/linux/generic/backport-6.1/730-10-v6.3-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch b/target/linux/generic/backport-6.1/730-10-v6.3-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch
new file mode 100644
index 0000000000..0bf48b0780
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-10-v6.3-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
+@@ -3712,9 +3712,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
+@@ -140,6 +140,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/backport-6.1/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch b/target/linux/generic/backport-6.1/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch
new file mode 100644
index 0000000000..08bf608bc1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch
@@ -0,0 +1,42 @@
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Sat, 28 Jan 2023 12:42:32 +0300
+Subject: [PATCH] net: ethernet: mtk_eth_soc: disable hardware DSA untagging
+ for second MAC
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+According to my tests on MT7621AT and MT7623NI SoCs, hardware DSA untagging
+won't work on the second MAC. Therefore, disable this feature when the
+second MAC of the MT7621 and MT7623 SoCs is being used.
+
+Fixes: 2d7605a72906 ("net: ethernet: mtk_eth_soc: enable hardware DSA untagging")
+Link: https://lore.kernel.org/netdev/6249fc14-b38a-c770-36b4-5af6d41c21d3@arinc9.com/
+Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Link: https://lore.kernel.org/r/20230128094232.2451947-1-arinc.unal@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3143,7 +3143,8 @@ static int mtk_open(struct net_device *d
+ struct mtk_eth *eth = mac->hw;
+ int i, err;
+
+- if (mtk_uses_dsa(dev) && !eth->prog) {
++ if ((mtk_uses_dsa(dev) && !eth->prog) &&
++ !(mac->id == 1 && MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC1_TRGMII))) {
+ for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
+ struct metadata_dst *md_dst = eth->dsa_meta[i];
+
+@@ -3160,7 +3161,8 @@ static int mtk_open(struct net_device *d
+ }
+ } else {
+ /* Hardware special tag parsing needs to be disabled if at least
+- * one MAC does not use DSA.
++ * one MAC does not use DSA, or the second MAC of the MT7621 and
++ * MT7623 SoCs is being used.
+ */
+ u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
+ val &= ~MTK_CDMP_STAG_EN;
diff --git a/target/linux/generic/backport-6.1/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch b/target/linux/generic/backport-6.1/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch
new file mode 100644
index 0000000000..efab99ada0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch
@@ -0,0 +1,54 @@
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Sun, 5 Feb 2023 20:53:31 +0300
+Subject: [PATCH] net: ethernet: mtk_eth_soc: enable special tag when any MAC
+ uses DSA
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The special tag is only enabled when the first MAC uses DSA. However, it
+must be enabled when any MAC uses DSA. Change the check accordingly.
+
+This fixes hardware DSA untagging not working on the second MAC of the
+MT7621 and MT7623 SoCs, and likely other SoCs too. Therefore, remove the
+check that disables hardware DSA untagging for the second MAC of the MT7621
+and MT7623 SoCs.
+
+Fixes: a1f47752fd62 ("net: ethernet: mtk_eth_soc: disable hardware DSA untagging for second MAC")
+Co-developed-by: Richard van Schagen <richard@routerhints.com>
+Signed-off-by: Richard van Schagen <richard@routerhints.com>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3078,7 +3078,7 @@ static void mtk_gdm_config(struct mtk_et
+
+ val |= config;
+
+- if (!i && eth->netdev[0] && netdev_uses_dsa(eth->netdev[0]))
++ if (eth->netdev[i] && netdev_uses_dsa(eth->netdev[i]))
+ val |= MTK_GDMA_SPECIAL_TAG;
+
+ mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
+@@ -3143,8 +3143,7 @@ static int mtk_open(struct net_device *d
+ struct mtk_eth *eth = mac->hw;
+ int i, err;
+
+- if ((mtk_uses_dsa(dev) && !eth->prog) &&
+- !(mac->id == 1 && MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC1_TRGMII))) {
++ 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];
+
+@@ -3161,8 +3160,7 @@ static int mtk_open(struct net_device *d
+ }
+ } else {
+ /* Hardware special tag parsing needs to be disabled if at least
+- * one MAC does not use DSA, or the second MAC of the MT7621 and
+- * MT7623 SoCs is being used.
++ * one MAC does not use DSA.
+ */
+ u32 val = mtk_r32(eth, MTK_CDMP_IG_CTRL);
+ val &= ~MTK_CDMP_STAG_EN;
diff --git a/target/linux/generic/backport-6.1/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch b/target/linux/generic/backport-6.1/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch
new file mode 100644
index 0000000000..c392429b88
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch
@@ -0,0 +1,129 @@
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Tue, 7 Feb 2023 12:30:27 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix DSA TX tag hwaccel for switch
+ port 0
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Arınç reports that on his MT7621AT Unielec U7621-06 board and MT7623NI
+Bananapi BPI-R2, packets received by the CPU over mt7530 switch port 0
+(of which this driver acts as the DSA master) are not processed
+correctly by software. More precisely, they arrive without a DSA tag
+(in packet or in the hwaccel area - skb_metadata_dst()), so DSA cannot
+demux them towards the switch's interface for port 0. Traffic from other
+ports receives a skb_metadata_dst() with the correct port and is demuxed
+properly.
+
+Looking at mtk_poll_rx(), it becomes apparent that this driver uses the
+skb vlan hwaccel area:
+
+ union {
+ u32 vlan_all;
+ struct {
+ __be16 vlan_proto;
+ __u16 vlan_tci;
+ };
+ };
+
+as a temporary storage for the VLAN hwaccel tag, or the DSA hwaccel tag.
+If this is a DSA master it's a DSA hwaccel tag, and finally clears up
+the skb VLAN hwaccel header.
+
+I'm guessing that the problem is the (mis)use of API.
+skb_vlan_tag_present() looks like this:
+
+ #define skb_vlan_tag_present(__skb) (!!(__skb)->vlan_all)
+
+So if both vlan_proto and vlan_tci are zeroes, skb_vlan_tag_present()
+returns precisely false. I don't know for sure what is the format of the
+DSA hwaccel tag, but I surely know that lowermost 3 bits of vlan_proto
+are 0 when receiving from port 0:
+
+ unsigned int port = vlan_proto & GENMASK(2, 0);
+
+If the RX descriptor has no other bits set to non-zero values in
+RX_DMA_VTAG, then the call to __vlan_hwaccel_put_tag() will not, in
+fact, make the subsequent skb_vlan_tag_present() return true, because
+it's implemented like this:
+
+static inline void __vlan_hwaccel_put_tag(struct sk_buff *skb,
+ __be16 vlan_proto, u16 vlan_tci)
+{
+ skb->vlan_proto = vlan_proto;
+ skb->vlan_tci = vlan_tci;
+}
+
+What we need to do to fix this problem (assuming this is the problem) is
+to stop using skb->vlan_all as temporary storage for driver affairs, and
+just create some local variables that serve the same purpose, but
+hopefully better. Instead of calling skb_vlan_tag_present(), let's look
+at a boolean has_hwaccel_tag which we set to true when the RX DMA
+descriptors have something. Disambiguate based on netdev_uses_dsa()
+whether this is a VLAN or DSA hwaccel tag, and only call
+__vlan_hwaccel_put_tag() if we're certain it's a VLAN tag.
+
+Arınç confirms that the treatment works, so this validates the
+assumption.
+
+Link: https://lore.kernel.org/netdev/704f3a72-fc9e-714a-db54-272e17612637@arinc9.com/
+Fixes: 2d7605a72906 ("net: ethernet: mtk_eth_soc: enable hardware DSA untagging")
+Reported-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1830,7 +1830,9 @@ static int mtk_poll_rx(struct napi_struc
+
+ while (done < budget) {
+ unsigned int pktlen, *rxdcsum;
++ bool has_hwaccel_tag = false;
+ struct net_device *netdev;
++ u16 vlan_proto, vlan_tci;
+ dma_addr_t dma_addr;
+ u32 hash, reason;
+ int mac = 0;
+@@ -1970,27 +1972,29 @@ static int mtk_poll_rx(struct napi_struc
+
+ 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));
++ if (trxd.rxd3 & RX_DMA_VTAG_V2) {
++ vlan_proto = RX_DMA_VPID(trxd.rxd4);
++ vlan_tci = RX_DMA_VID(trxd.rxd4);
++ has_hwaccel_tag = true;
++ }
+ } else if (trxd.rxd2 & RX_DMA_VTAG) {
+- __vlan_hwaccel_put_tag(skb, htons(RX_DMA_VPID(trxd.rxd3)),
+- RX_DMA_VID(trxd.rxd3));
++ vlan_proto = RX_DMA_VPID(trxd.rxd3);
++ vlan_tci = RX_DMA_VID(trxd.rxd3);
++ has_hwaccel_tag = true;
+ }
+ }
+
+ /* 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 (has_hwaccel_tag && netdev_uses_dsa(netdev)) {
++ unsigned int port = vlan_proto & 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);
++ } else if (has_hwaccel_tag) {
++ __vlan_hwaccel_put_tag(skb, htons(vlan_proto), vlan_tci);
+ }
+
+ skb_record_rx_queue(skb, 0);
diff --git a/target/linux/generic/backport-6.1/730-15-v6.3-net-ethernet-mtk_wed-No-need-to-clear-memory-after-a.patch b/target/linux/generic/backport-6.1/730-15-v6.3-net-ethernet-mtk_wed-No-need-to-clear-memory-after-a.patch
new file mode 100644
index 0000000000..74a77ddaca
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-15-v6.3-net-ethernet-mtk_wed-No-need-to-clear-memory-after-a.patch
@@ -0,0 +1,26 @@
+From: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
+Date: Sun, 12 Feb 2023 07:51:51 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: No need to clear memory after a
+ dma_alloc_coherent() call
+
+dma_alloc_coherent() already clears the allocated memory, there is no need
+to explicitly call memset().
+
+Moreover, it is likely that the size in the memset() is incorrect and
+should be "size * sizeof(*ring->desc)".
+
+Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
+Link: https://lore.kernel.org/r/d5acce7dd108887832c9719f62c7201b4c83b3fb.1676184599.git.christophe.jaillet@wanadoo.fr
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -786,7 +786,6 @@ mtk_wed_rro_ring_alloc(struct mtk_wed_de
+
+ ring->desc_size = sizeof(*ring->desc);
+ ring->size = size;
+- memset(ring->desc, 0, size);
+
+ return 0;
+ }
diff --git a/target/linux/generic/backport-6.1/730-16-v6.3-net-ethernet-mtk_wed-fix-some-possible-NULL-pointer-.patch b/target/linux/generic/backport-6.1/730-16-v6.3-net-ethernet-mtk_wed-fix-some-possible-NULL-pointer-.patch
new file mode 100644
index 0000000000..e043a681da
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-16-v6.3-net-ethernet-mtk_wed-fix-some-possible-NULL-pointer-.patch
@@ -0,0 +1,61 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Wed, 7 Dec 2022 15:04:54 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: fix some possible NULL pointer
+ dereferences
+
+Fix possible NULL pointer dereference in mtk_wed_detach routine checking
+wo pointer is properly allocated before running mtk_wed_wo_reset() and
+mtk_wed_wo_deinit().
+Even if it is just a theoretical issue at the moment check wo pointer is
+not NULL in mtk_wed_mcu_msg_update.
+Moreover, honor mtk_wed_mcu_send_msg return value in mtk_wed_wo_reset()
+
+Fixes: 799684448e3e ("net: ethernet: mtk_wed: introduce wed wo support")
+Fixes: 4c5de09eb0d0 ("net: ethernet: mtk_wed: add configure wed wo support")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -174,9 +174,10 @@ mtk_wed_wo_reset(struct mtk_wed_device *
+ 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 (mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
++ MTK_WED_WO_CMD_CHANGE_STATE, &state,
++ sizeof(state), false))
++ return;
+
+ if (readx_poll_timeout(mtk_wed_wo_read_status, dev, val,
+ val == MTK_WED_WOIF_DISABLE_DONE,
+@@ -632,9 +633,11 @@ mtk_wed_detach(struct mtk_wed_device *de
+ mtk_wed_free_tx_rings(dev);
+
+ if (mtk_wed_get_rx_capa(dev)) {
+- mtk_wed_wo_reset(dev);
++ if (hw->wed_wo)
++ mtk_wed_wo_reset(dev);
+ mtk_wed_free_rx_rings(dev);
+- mtk_wed_wo_deinit(hw);
++ if (hw->wed_wo)
++ mtk_wed_wo_deinit(hw);
+ }
+
+ if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -207,6 +207,9 @@ int mtk_wed_mcu_msg_update(struct mtk_we
+ if (dev->hw->version == 1)
+ return 0;
+
++ if (WARN_ON(!wo))
++ return -ENODEV;
++
+ return mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, id, data, len,
+ true);
+ }
diff --git a/target/linux/generic/backport-6.1/730-17-v6.3-net-ethernet-mtk_wed-fix-possible-deadlock-if-mtk_we.patch b/target/linux/generic/backport-6.1/730-17-v6.3-net-ethernet-mtk_wed-fix-possible-deadlock-if-mtk_we.patch
new file mode 100644
index 0000000000..d1c5fb6656
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-17-v6.3-net-ethernet-mtk_wed-fix-possible-deadlock-if-mtk_we.patch
@@ -0,0 +1,58 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Wed, 7 Dec 2022 15:04:55 +0100
+Subject: [PATCH] net: ethernet: mtk_wed: fix possible deadlock if
+ mtk_wed_wo_init fails
+
+Introduce __mtk_wed_detach() in order to avoid a deadlock in
+mtk_wed_attach routine if mtk_wed_wo_init fails since both
+mtk_wed_attach and mtk_wed_detach run holding hw_lock mutex.
+
+Fixes: 4c5de09eb0d0 ("net: ethernet: mtk_wed: add configure wed wo support")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -619,12 +619,10 @@ mtk_wed_deinit(struct mtk_wed_device *de
+ }
+
+ static void
+-mtk_wed_detach(struct mtk_wed_device *dev)
++__mtk_wed_detach(struct mtk_wed_device *dev)
+ {
+ struct mtk_wed_hw *hw = dev->hw;
+
+- mutex_lock(&hw_lock);
+-
+ mtk_wed_deinit(dev);
+
+ mtk_wdma_rx_reset(dev);
+@@ -657,6 +655,13 @@ mtk_wed_detach(struct mtk_wed_device *de
+ module_put(THIS_MODULE);
+
+ hw->wed_dev = NULL;
++}
++
++static void
++mtk_wed_detach(struct mtk_wed_device *dev)
++{
++ mutex_lock(&hw_lock);
++ __mtk_wed_detach(dev);
+ mutex_unlock(&hw_lock);
+ }
+
+@@ -1545,8 +1550,10 @@ mtk_wed_attach(struct mtk_wed_device *de
+ ret = mtk_wed_wo_init(hw);
+ }
+ out:
+- if (ret)
+- mtk_wed_detach(dev);
++ if (ret) {
++ dev_err(dev->hw->dev, "failed to attach wed device\n");
++ __mtk_wed_detach(dev);
++ }
+ unlock:
+ mutex_unlock(&hw_lock);
+
diff --git a/target/linux/generic/backport-6.1/730-18-v6.3-net-ethernet-mtk_eth_soc-fix-tx-throughput-regressio.patch b/target/linux/generic/backport-6.1/730-18-v6.3-net-ethernet-mtk_eth_soc-fix-tx-throughput-regressio.patch
new file mode 100644
index 0000000000..e633f6f1fb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/730-18-v6.3-net-ethernet-mtk_eth_soc-fix-tx-throughput-regressio.patch
@@ -0,0 +1,31 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 24 Mar 2023 14:56:58 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix tx throughput regression with
+ direct 1G links
+
+Using the QDMA tx scheduler to throttle tx to line speed works fine for
+switch ports, but apparently caused a regression on non-switch ports.
+
+Based on a number of tests, it seems that this throttling can be safely
+dropped without re-introducing the issues on switch ports that the
+tx scheduling changes resolved.
+
+Link: https://lore.kernel.org/netdev/trinity-92c3826f-c2c8-40af-8339-bc6d0d3ffea4-1678213958520@3c-app-gmx-bs16/
+Fixes: f63959c7eec3 ("net: ethernet: mtk_eth_soc: implement multi-queue support for per-port queues")
+Reported-by: Frank Wunderlich <frank-w@public-files.de>
+Reported-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+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
+@@ -676,8 +676,6 @@ static void mtk_mac_link_up(struct phyli
+ break;
+ }
+
+- mtk_set_queue_speed(mac->hw, mac->id, speed);
+-
+ /* Configure duplex */
+ if (duplex == DUPLEX_FULL)
+ mcr |= MAC_MCR_FORCE_DPX;
diff --git a/target/linux/generic/backport-6.1/733-v6.2-01-net-ethernet-mtk_eth_soc-Avoid-truncating-allocation.patch b/target/linux/generic/backport-6.1/733-v6.2-01-net-ethernet-mtk_eth_soc-Avoid-truncating-allocation.patch
new file mode 100644
index 0000000000..460c5c2317
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-01-net-ethernet-mtk_eth_soc-Avoid-truncating-allocation.patch
@@ -0,0 +1,60 @@
+From f3eceaed9edd7c0e0d9fb057613131f92973626f Mon Sep 17 00:00:00 2001
+From: Kees Cook <keescook@chromium.org>
+Date: Fri, 27 Jan 2023 14:38:54 -0800
+Subject: [PATCH] net: ethernet: mtk_eth_soc: Avoid truncating allocation
+
+There doesn't appear to be a reason to truncate the allocation used for
+flow_info, so do a full allocation and remove the unused empty struct.
+GCC does not like having a reference to an object that has been
+partially allocated, as bounds checking may become impossible when
+such an object is passed to other code. Seen with GCC 13:
+
+../drivers/net/ethernet/mediatek/mtk_ppe.c: In function 'mtk_foe_entry_commit_subflow':
+../drivers/net/ethernet/mediatek/mtk_ppe.c:623:18: warning: array subscript 'struct mtk_flow_entry[0]' is partly outside array bounds of 'unsigned char[48]' [-Warray-bounds=]
+ 623 | flow_info->l2_data.base_flow = entry;
+ | ^~
+
+Cc: Felix Fietkau <nbd@nbd.name>
+Cc: John Crispin <john@phrozen.org>
+Cc: Sean Wang <sean.wang@mediatek.com>
+Cc: Mark Lee <Mark-MC.Lee@mediatek.com>
+Cc: Lorenzo Bianconi <lorenzo@kernel.org>
+Cc: "David S. Miller" <davem@davemloft.net>
+Cc: Eric Dumazet <edumazet@google.com>
+Cc: Jakub Kicinski <kuba@kernel.org>
+Cc: Paolo Abeni <pabeni@redhat.com>
+Cc: Matthias Brugger <matthias.bgg@gmail.com>
+Cc: netdev@vger.kernel.org
+Cc: linux-arm-kernel@lists.infradead.org
+Cc: linux-mediatek@lists.infradead.org
+Signed-off-by: Kees Cook <keescook@chromium.org>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Link: https://lore.kernel.org/r/20230127223853.never.014-kees@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 3 +--
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 1 -
+ 2 files changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -621,8 +621,7 @@ mtk_foe_entry_commit_subflow(struct mtk_
+ u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP;
+ int type;
+
+- flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end),
+- GFP_ATOMIC);
++ flow_info = kzalloc(sizeof(*flow_info), GFP_ATOMIC);
+ if (!flow_info)
+ return;
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -279,7 +279,6 @@ struct mtk_flow_entry {
+ struct {
+ struct mtk_flow_entry *base_flow;
+ struct hlist_node list;
+- struct {} end;
+ } l2_data;
+ };
+ struct rhash_head node;
diff --git a/target/linux/generic/backport-6.1/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch b/target/linux/generic/backport-6.1/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch
new file mode 100644
index 0000000000..68f3659367
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch
@@ -0,0 +1,55 @@
+From b6a709cb51f7bdc55c01cec886098a9753ce8c28 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:10:42 +0100
+Subject: [PATCH 01/10] net: mtk_eth_soc: add definitions for PCS
+
+As a result of help from Frank Wunderlich to investigate and test, we
+know a bit more about the PCS on the Mediatek platforms. Update the
+definitions from this investigation.
+
+This PCS appears similar, but not identical to the Lynx PCS.
+
+Although not included in this patch, but for future reference, the PHY
+ID registers at offset 4 read as 0x4d544950 'MTIP'.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -501,8 +501,10 @@
+ #define ETHSYS_DMA_AG_MAP_PPE BIT(2)
+
+ /* SGMII subsystem config registers */
+-/* Register to auto-negotiation restart */
++/* BMCR (low 16) BMSR (high 16) */
+ #define SGMSYS_PCS_CONTROL_1 0x0
++#define SGMII_BMCR GENMASK(15, 0)
++#define SGMII_BMSR GENMASK(31, 16)
+ #define SGMII_AN_RESTART BIT(9)
+ #define SGMII_ISOLATE BIT(10)
+ #define SGMII_AN_ENABLE BIT(12)
+@@ -512,13 +514,18 @@
+ #define SGMII_PCS_FAULT BIT(23)
+ #define SGMII_AN_EXPANSION_CLR BIT(30)
+
++#define SGMSYS_PCS_ADVERTISE 0x8
++#define SGMII_ADVERTISE GENMASK(15, 0)
++#define SGMII_LPA GENMASK(31, 16)
++
+ /* Register to programmable link timer, the unit in 2 * 8ns */
+ #define SGMSYS_PCS_LINK_TIMER 0x18
+-#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & GENMASK(19, 0))
++#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
++#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & SGMII_LINK_TIMER_MASK)
+
+ /* Register to control remote fault */
+ #define SGMSYS_SGMII_MODE 0x20
+-#define SGMII_IF_MODE_BIT0 BIT(0)
++#define SGMII_IF_MODE_SGMII BIT(0)
+ #define SGMII_SPEED_DUPLEX_AN BIT(1)
+ #define SGMII_SPEED_MASK GENMASK(3, 2)
+ #define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
diff --git a/target/linux/generic/backport-6.1/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch b/target/linux/generic/backport-6.1/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch
new file mode 100644
index 0000000000..4ea428c9d6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch
@@ -0,0 +1,74 @@
+From 5cf7797526ee81bea0f627bccaa3d887f48f53e0 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:10:47 +0100
+Subject: [PATCH 02/10] net: mtk_eth_soc: eliminate unnecessary error handling
+
+The functions called by the pcs_config() method always return zero, so
+there is no point trying to handle an error from these functions. Make
+these functions void, eliminate the "err" variable and simply return
+zero from the pcs_config() function itself.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 ++++++------------
+ 1 file changed, 6 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -20,7 +20,7 @@ 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 void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+ {
+ unsigned int val;
+
+@@ -39,16 +39,13 @@ static int mtk_pcs_setup_mode_an(struct
+ regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+ val &= ~SGMII_PHYA_PWD;
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
+-
+- return 0;
+-
+ }
+
+ /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
+ * fixed speed.
+ */
+-static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
+- phy_interface_t interface)
++static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
++ phy_interface_t interface)
+ {
+ unsigned int val;
+
+@@ -73,8 +70,6 @@ static int mtk_pcs_setup_mode_force(stru
+ regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+ val &= ~SGMII_PHYA_PWD;
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
+-
+- return 0;
+ }
+
+ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+@@ -83,15 +78,14 @@ static int mtk_pcs_config(struct phylink
+ bool permit_pause_to_mac)
+ {
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+- int err = 0;
+
+ /* Setup SGMIISYS with the determined property */
+ if (interface != PHY_INTERFACE_MODE_SGMII)
+- err = mtk_pcs_setup_mode_force(mpcs, interface);
++ mtk_pcs_setup_mode_force(mpcs, interface);
+ else if (phylink_autoneg_inband(mode))
+- err = mtk_pcs_setup_mode_an(mpcs);
++ mtk_pcs_setup_mode_an(mpcs);
+
+- return err;
++ return 0;
+ }
+
+ static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
diff --git a/target/linux/generic/backport-6.1/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch b/target/linux/generic/backport-6.1/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch
new file mode 100644
index 0000000000..64a4a72fa6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch
@@ -0,0 +1,46 @@
+From c000dca098002da193b98099df051c9ead0cacb4 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:10:52 +0100
+Subject: [PATCH 03/10] net: mtk_eth_soc: add pcs_get_state() implementation
+
+Add a pcs_get_state() implementation which uses the advertisements
+to compute the resulting link modes, and BMSR contents to determine
+negotiation and link status.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -19,6 +19,20 @@ static struct mtk_pcs *pcs_to_mtk_pcs(st
+ return container_of(pcs, struct mtk_pcs, pcs);
+ }
+
++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 bm, adv;
++
++ /* Read the BMSR and LPA */
++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
++
++ phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
++ FIELD_GET(SGMII_LPA, adv));
++}
++
+ /* For SGMII interface mode */
+ static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+ {
+@@ -117,6 +131,7 @@ static void mtk_pcs_link_up(struct phyli
+ }
+
+ static const struct phylink_pcs_ops mtk_pcs_ops = {
++ .pcs_get_state = mtk_pcs_get_state,
+ .pcs_config = mtk_pcs_config,
+ .pcs_an_restart = mtk_pcs_restart_an,
+ .pcs_link_up = mtk_pcs_link_up,
diff --git a/target/linux/generic/backport-6.1/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch b/target/linux/generic/backport-6.1/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch
new file mode 100644
index 0000000000..24610fe11e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch
@@ -0,0 +1,130 @@
+From 0d2351dc2768061689abd4de1529fa206bbd574e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:10:58 +0100
+Subject: [PATCH 04/10] net: mtk_eth_soc: convert mtk_sgmii to use
+ regmap_update_bits()
+
+mtk_sgmii does a lot of read-modify-write operations, for which there
+is a specific regmap function. Use this function instead of open-coding
+the operations.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 61 ++++++++++-------------
+ 1 file changed, 26 insertions(+), 35 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -36,23 +36,18 @@ static void mtk_pcs_get_state(struct phy
+ /* For SGMII interface mode */
+ static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+ {
+- unsigned int val;
+-
+ /* Setup the link timer and QPHY power up inside SGMIISYS */
+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+ SGMII_LINK_TIMER_DEFAULT);
+
+- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+- val |= SGMII_REMOTE_FAULT_DIS;
+- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+-
+- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+- 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_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
++ SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS);
++
++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
++ SGMII_AN_RESTART, SGMII_AN_RESTART);
++
++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
++ SGMII_PHYA_PWD, 0);
+ }
+
+ /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
+@@ -61,29 +56,26 @@ static void mtk_pcs_setup_mode_an(struct
+ static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
+ phy_interface_t interface)
+ {
+- unsigned int val;
++ unsigned int rgc3;
+
+- 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);
++ rgc3 = RG_PHY_SPEED_3_125G;
++
++ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
++ RG_PHY_SPEED_3_125G, rgc3);
+
+ /* Disable SGMII AN */
+- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+- val &= ~SGMII_AN_ENABLE;
+- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
++ SGMII_AN_ENABLE, 0);
+
+ /* Set the speed etc but leave the duplex unchanged */
+- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+- val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
+- val |= SGMII_SPEED_1000;
+- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
++ SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL,
++ SGMII_SPEED_1000);
+
+ /* 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_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
++ SGMII_PHYA_PWD, 0);
+ }
+
+ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+@@ -105,29 +97,28 @@ static int mtk_pcs_config(struct phylink
+ static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
+ {
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+- unsigned int val;
+
+- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+- val |= SGMII_AN_RESTART;
+- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
++ SGMII_AN_RESTART, SGMII_AN_RESTART);
+ }
+
+ static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface, int speed, int duplex)
+ {
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+- unsigned int val;
++ unsigned int sgm_mode;
+
+ if (!phy_interface_mode_is_8023z(interface))
+ return;
+
+ /* SGMII force duplex setting */
+- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+- val &= ~SGMII_DUPLEX_FULL;
+ if (duplex == DUPLEX_FULL)
+- val |= SGMII_DUPLEX_FULL;
++ sgm_mode = SGMII_DUPLEX_FULL;
++ else
++ sgm_mode = 0;
+
+- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
++ SGMII_DUPLEX_FULL, sgm_mode);
+ }
+
+ static const struct phylink_pcs_ops mtk_pcs_ops = {
diff --git a/target/linux/generic/backport-6.1/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch b/target/linux/generic/backport-6.1/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch
new file mode 100644
index 0000000000..ba76ca40ff
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch
@@ -0,0 +1,52 @@
+From 12198c3a410fe69843e335c1bbf6d4c2a4d48e4e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:11:03 +0100
+Subject: [PATCH 05/10] net: mtk_eth_soc: add out of band forcing of speed and
+ duplex in pcs_link_up
+
+Add support for forcing the link speed and duplex setting in the
+pcs_link_up() method for out of band modes, which will be useful when
+we finish converting the pcs_config() method. Until then, we still have
+to force duplex for 802.3z modes to work correctly.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 28 ++++++++++++++---------
+ 1 file changed, 17 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -108,17 +108,23 @@ static void mtk_pcs_link_up(struct phyli
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ unsigned int sgm_mode;
+
+- if (!phy_interface_mode_is_8023z(interface))
+- return;
++ if (!phylink_autoneg_inband(mode) ||
++ phy_interface_mode_is_8023z(interface)) {
++ /* Force the speed and duplex setting */
++ if (speed == SPEED_10)
++ sgm_mode = SGMII_SPEED_10;
++ else if (speed == SPEED_100)
++ sgm_mode = SGMII_SPEED_100;
++ else
++ sgm_mode = SGMII_SPEED_1000;
+
+- /* SGMII force duplex setting */
+- if (duplex == DUPLEX_FULL)
+- sgm_mode = SGMII_DUPLEX_FULL;
+- else
+- sgm_mode = 0;
++ if (duplex == DUPLEX_FULL)
++ sgm_mode |= SGMII_DUPLEX_FULL;
+
+- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+- SGMII_DUPLEX_FULL, sgm_mode);
++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
++ SGMII_DUPLEX_FULL | SGMII_SPEED_MASK,
++ sgm_mode);
++ }
+ }
+
+ static const struct phylink_pcs_ops mtk_pcs_ops = {
diff --git a/target/linux/generic/backport-6.1/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch b/target/linux/generic/backport-6.1/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch
new file mode 100644
index 0000000000..b76e159275
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch
@@ -0,0 +1,48 @@
+From 6f38fffe2179dd29612aea2c67c46ed6682b4e46 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:11:08 +0100
+Subject: [PATCH 06/10] net: mtk_eth_soc: move PHY power up
+
+The PHY power up is common to both configuration paths, so move it into
+the parent function. We need to do this for all serdes modes.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -45,9 +45,6 @@ static void mtk_pcs_setup_mode_an(struct
+
+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+ SGMII_AN_RESTART, SGMII_AN_RESTART);
+-
+- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+- SGMII_PHYA_PWD, 0);
+ }
+
+ /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
+@@ -72,10 +69,6 @@ static void mtk_pcs_setup_mode_force(str
+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+ SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL,
+ SGMII_SPEED_1000);
+-
+- /* Release PHYA power down state */
+- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+- SGMII_PHYA_PWD, 0);
+ }
+
+ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+@@ -91,6 +84,10 @@ static int mtk_pcs_config(struct phylink
+ else if (phylink_autoneg_inband(mode))
+ mtk_pcs_setup_mode_an(mpcs);
+
++ /* Release PHYA power down state */
++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
++ SGMII_PHYA_PWD, 0);
++
+ return 0;
+ }
+
diff --git a/target/linux/generic/backport-6.1/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch b/target/linux/generic/backport-6.1/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch
new file mode 100644
index 0000000000..cd9f0699b3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch
@@ -0,0 +1,48 @@
+From f752c0df13dfeb721c11d3debb79f08cf437344f Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:11:13 +0100
+Subject: [PATCH 07/10] net: mtk_eth_soc: move interface speed selection
+
+Move the selection of the underlying interface speed to the pcs_config
+function, so we always program the interface speed.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -53,14 +53,6 @@ static void mtk_pcs_setup_mode_an(struct
+ static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
+ phy_interface_t interface)
+ {
+- unsigned int rgc3;
+-
+- if (interface == PHY_INTERFACE_MODE_2500BASEX)
+- rgc3 = RG_PHY_SPEED_3_125G;
+-
+- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
+- RG_PHY_SPEED_3_125G, rgc3);
+-
+ /* Disable SGMII AN */
+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+ SGMII_AN_ENABLE, 0);
+@@ -77,6 +69,16 @@ static int mtk_pcs_config(struct phylink
+ bool permit_pause_to_mac)
+ {
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
++ unsigned int rgc3;
++
++ if (interface == PHY_INTERFACE_MODE_2500BASEX)
++ rgc3 = RG_PHY_SPEED_3_125G;
++ else
++ rgc3 = 0;
++
++ /* Configure the underlying interface speed */
++ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
++ RG_PHY_SPEED_3_125G, rgc3);
+
+ /* Setup SGMIISYS with the determined property */
+ if (interface != PHY_INTERFACE_MODE_SGMII)
diff --git a/target/linux/generic/backport-6.1/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch b/target/linux/generic/backport-6.1/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch
new file mode 100644
index 0000000000..f08358e963
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch
@@ -0,0 +1,52 @@
+From c125c66ea71b9377ae2478c4f1b87b180cc5c6ef Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:11:18 +0100
+Subject: [PATCH 08/10] net: mtk_eth_soc: add advertisement programming
+
+Program the advertisement into the mtk PCS block.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -70,16 +70,27 @@ static int mtk_pcs_config(struct phylink
+ {
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ unsigned int rgc3;
++ int advertise;
++ bool changed;
+
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ rgc3 = RG_PHY_SPEED_3_125G;
+ else
+ rgc3 = 0;
+
++ advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
++ advertising);
++ if (advertise < 0)
++ return advertise;
++
+ /* Configure the underlying interface speed */
+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
+ RG_PHY_SPEED_3_125G, rgc3);
+
++ /* Update the advertisement, noting whether it has changed */
++ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
++ SGMII_ADVERTISE, advertise, &changed);
++
+ /* Setup SGMIISYS with the determined property */
+ if (interface != PHY_INTERFACE_MODE_SGMII)
+ mtk_pcs_setup_mode_force(mpcs, interface);
+@@ -90,7 +101,7 @@ static int mtk_pcs_config(struct phylink
+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+ SGMII_PHYA_PWD, 0);
+
+- return 0;
++ return changed;
+ }
+
+ static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
diff --git a/target/linux/generic/backport-6.1/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch b/target/linux/generic/backport-6.1/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch
new file mode 100644
index 0000000000..602d52c6f4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch
@@ -0,0 +1,63 @@
+From 3027d89f87707e7f3e5b683e0d37a32afb5bde96 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:11:23 +0100
+Subject: [PATCH 09/10] net: mtk_eth_soc: move and correct link timer
+ programming
+
+Program the link timer appropriately for the interface mode being
+used, using the newly introduced phylink helper that provides the
+nanosecond link timer interval.
+
+The intervals are 1.6ms for SGMII based protocols and 10ms for
+802.3z based protocols.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -36,10 +36,6 @@ static void mtk_pcs_get_state(struct phy
+ /* For SGMII interface mode */
+ static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+ {
+- /* Setup the link timer and QPHY power up inside SGMIISYS */
+- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+- SGMII_LINK_TIMER_DEFAULT);
+-
+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+ SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS);
+
+@@ -69,8 +65,8 @@ static int mtk_pcs_config(struct phylink
+ bool permit_pause_to_mac)
+ {
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
++ int advertise, link_timer;
+ unsigned int rgc3;
+- int advertise;
+ bool changed;
+
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+@@ -83,6 +79,10 @@ static int mtk_pcs_config(struct phylink
+ if (advertise < 0)
+ return advertise;
+
++ link_timer = phylink_get_link_timer_ns(interface);
++ if (link_timer < 0)
++ return link_timer;
++
+ /* Configure the underlying interface speed */
+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
+ RG_PHY_SPEED_3_125G, rgc3);
+@@ -91,6 +91,9 @@ static int mtk_pcs_config(struct phylink
+ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
+ SGMII_ADVERTISE, advertise, &changed);
+
++ /* Setup the link timer and QPHY power up inside SGMIISYS */
++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
++
+ /* Setup SGMIISYS with the determined property */
+ if (interface != PHY_INTERFACE_MODE_SGMII)
+ mtk_pcs_setup_mode_force(mpcs, interface);
diff --git a/target/linux/generic/backport-6.1/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch b/target/linux/generic/backport-6.1/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch
new file mode 100644
index 0000000000..0e9a0535a7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch
@@ -0,0 +1,132 @@
+From 81b0f12a2a8a1699a7d49c3995e5f71e4ec018e6 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 27 Oct 2022 14:11:28 +0100
+Subject: [PATCH 10/10] net: mtk_eth_soc: add support for in-band 802.3z
+ negotiation
+
+As a result of help from Frank Wunderlich to investigate and test, we
+now know how to program this PCS for in-band 802.3z negotiation. Add
+support for this by moving the contents of the two functions into the
+common mtk_pcs_config() function and adding the register settings for
+802.3z negotiation.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 77 ++++++++++++-----------
+ 1 file changed, 42 insertions(+), 35 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -33,41 +33,15 @@ static void mtk_pcs_get_state(struct phy
+ FIELD_GET(SGMII_LPA, adv));
+ }
+
+-/* For SGMII interface mode */
+-static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
+-{
+- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+- SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS);
+-
+- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+- SGMII_AN_RESTART, SGMII_AN_RESTART);
+-}
+-
+-/* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
+- * fixed speed.
+- */
+-static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
+- phy_interface_t interface)
+-{
+- /* Disable SGMII AN */
+- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+- SGMII_AN_ENABLE, 0);
+-
+- /* Set the speed etc but leave the duplex unchanged */
+- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+- SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL,
+- SGMII_SPEED_1000);
+-}
+-
+ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+ {
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
++ unsigned int rgc3, sgm_mode, bmcr;
+ int advertise, link_timer;
+- unsigned int rgc3;
+- bool changed;
++ bool changed, use_an;
+
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ rgc3 = RG_PHY_SPEED_3_125G;
+@@ -83,6 +57,37 @@ static int mtk_pcs_config(struct phylink
+ if (link_timer < 0)
+ return link_timer;
+
++ /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
++ * we assume that fixes it's speed at bitrate = line rate (in
++ * other words, 1000Mbps or 2500Mbps).
++ */
++ if (interface == PHY_INTERFACE_MODE_SGMII) {
++ sgm_mode = SGMII_IF_MODE_SGMII;
++ if (phylink_autoneg_inband(mode)) {
++ sgm_mode |= SGMII_REMOTE_FAULT_DIS |
++ SGMII_SPEED_DUPLEX_AN;
++ use_an = true;
++ } else {
++ use_an = false;
++ }
++ } else if (phylink_autoneg_inband(mode)) {
++ /* 1000base-X or 2500base-X autoneg */
++ sgm_mode = SGMII_REMOTE_FAULT_DIS;
++ use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
++ advertising);
++ } else {
++ /* 1000base-X or 2500base-X without autoneg */
++ sgm_mode = 0;
++ use_an = false;
++ }
++
++ if (use_an) {
++ /* FIXME: Do we need to set AN_RESTART here? */
++ bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE;
++ } else {
++ bmcr = 0;
++ }
++
+ /* Configure the underlying interface speed */
+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
+ RG_PHY_SPEED_3_125G, rgc3);
+@@ -94,11 +99,14 @@ static int mtk_pcs_config(struct phylink
+ /* Setup the link timer and QPHY power up inside SGMIISYS */
+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
+
+- /* Setup SGMIISYS with the determined property */
+- if (interface != PHY_INTERFACE_MODE_SGMII)
+- mtk_pcs_setup_mode_force(mpcs, interface);
+- else if (phylink_autoneg_inband(mode))
+- mtk_pcs_setup_mode_an(mpcs);
++ /* Update the sgmsys mode register */
++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
++ SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
++ SGMII_IF_MODE_SGMII, sgm_mode);
++
++ /* Update the BMCR */
++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
++ SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr);
+
+ /* Release PHYA power down state */
+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+@@ -121,8 +129,7 @@ static void mtk_pcs_link_up(struct phyli
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ unsigned int sgm_mode;
+
+- if (!phylink_autoneg_inband(mode) ||
+- phy_interface_mode_is_8023z(interface)) {
++ if (!phylink_autoneg_inband(mode)) {
+ /* Force the speed and duplex setting */
+ if (speed == SPEED_10)
+ sgm_mode = SGMII_SPEED_10;
diff --git a/target/linux/generic/backport-6.1/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch b/target/linux/generic/backport-6.1/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch
new file mode 100644
index 0000000000..8d2991f450
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch
@@ -0,0 +1,119 @@
+From 7ff82416de8295c61423ef6fd75f052d3837d2f7 Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Wed, 1 Feb 2023 19:23:29 +0100
+Subject: [PATCH 11/13] net: mediatek: sgmii: ensure the SGMII PHY is powered
+ down on configuration
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The code expect the PHY to be in power down which is only true after reset.
+Allow changes of the SGMII parameters more than once.
+
+Only power down when reconfiguring to avoid bouncing the link when there's
+no reason to - based on code from Russell King.
+
+There are cases when the SGMII_PHYA_PWD register contains 0x9 which
+prevents SGMII from working. The SGMII still shows link but no traffic
+can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
+taken from a good working state of the SGMII interface.
+
+Fixes: 42c03844e93d ("net-next: mediatek: add support for MediaTek MT7622 SoC")
+Suggested-by: Russell King (Oracle) <linux@armlinux.org.uk>
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+[ bmork: rebased and squashed into one patch ]
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 ++
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 39 +++++++++++++++------
+ 2 files changed, 30 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1070,11 +1070,13 @@ struct mtk_soc_data {
+ * @regmap: The register map pointing at the range used to setup
+ * SGMII modes
+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
++ * @interface: Currently configured interface mode
+ * @pcs: Phylink PCS structure
+ */
+ struct mtk_pcs {
+ struct regmap *regmap;
+ u32 ana_rgc3;
++ phy_interface_t interface;
+ struct phylink_pcs pcs;
+ };
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -43,11 +43,6 @@ static int mtk_pcs_config(struct phylink
+ int advertise, link_timer;
+ bool changed, use_an;
+
+- if (interface == PHY_INTERFACE_MODE_2500BASEX)
+- rgc3 = RG_PHY_SPEED_3_125G;
+- else
+- rgc3 = 0;
+-
+ advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
+ advertising);
+ if (advertise < 0)
+@@ -88,9 +83,22 @@ static int mtk_pcs_config(struct phylink
+ bmcr = 0;
+ }
+
+- /* Configure the underlying interface speed */
+- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
+- RG_PHY_SPEED_3_125G, rgc3);
++ if (mpcs->interface != interface) {
++ /* PHYA power down */
++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
++ SGMII_PHYA_PWD, SGMII_PHYA_PWD);
++
++ if (interface == PHY_INTERFACE_MODE_2500BASEX)
++ rgc3 = RG_PHY_SPEED_3_125G;
++ else
++ rgc3 = 0;
++
++ /* Configure the underlying interface speed */
++ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
++ RG_PHY_SPEED_3_125G, rgc3);
++
++ mpcs->interface = interface;
++ }
+
+ /* Update the advertisement, noting whether it has changed */
+ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
+@@ -108,9 +116,17 @@ static int mtk_pcs_config(struct phylink
+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+ SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr);
+
+- /* Release PHYA power down state */
+- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+- SGMII_PHYA_PWD, 0);
++ /* Release PHYA power down state
++ * Only removing bit SGMII_PHYA_PWD isn't enough.
++ * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
++ * prevents SGMII from working. The SGMII still shows link but no traffic
++ * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
++ * taken from a good working state of the SGMII interface.
++ * Unknown how much the QPHY needs but it is racy without a sleep.
++ * Tested on mt7622 & mt7986.
++ */
++ usleep_range(50, 100);
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+
+ return changed;
+ }
+@@ -171,6 +187,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ return PTR_ERR(ss->pcs[i].regmap);
+
+ ss->pcs[i].pcs.ops = &mtk_pcs_ops;
++ ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
+ }
+
+ return 0;
diff --git a/target/linux/generic/backport-6.1/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch b/target/linux/generic/backport-6.1/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch
new file mode 100644
index 0000000000..79e5ad147c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch
@@ -0,0 +1,52 @@
+From 9d32637122de88f1ef614c29703f0e050cad342e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
+Date: Wed, 1 Feb 2023 19:23:30 +0100
+Subject: [PATCH 12/13] net: mediatek: sgmii: fix duplex configuration
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The logic of the duplex bit is inverted. Setting it means half
+duplex, not full duplex.
+
+Fix and rename macro to avoid confusion.
+
+Fixes: 7e538372694b ("net: ethernet: mediatek: Re-add support SGMII")
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +-
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 6 +++---
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -531,7 +531,7 @@
+ #define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
+ #define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
+ #define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
+-#define SGMII_DUPLEX_FULL BIT(4)
++#define SGMII_DUPLEX_HALF BIT(4)
+ #define SGMII_IF_MODE_BIT5 BIT(5)
+ #define SGMII_REMOTE_FAULT_DIS BIT(8)
+ #define SGMII_CODE_SYNC_SET_VAL BIT(9)
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -154,11 +154,11 @@ static void mtk_pcs_link_up(struct phyli
+ else
+ sgm_mode = SGMII_SPEED_1000;
+
+- if (duplex == DUPLEX_FULL)
+- sgm_mode |= SGMII_DUPLEX_FULL;
++ if (duplex != DUPLEX_FULL)
++ sgm_mode |= SGMII_DUPLEX_HALF;
+
+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+- SGMII_DUPLEX_FULL | SGMII_SPEED_MASK,
++ SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
+ sgm_mode);
+ }
+ }
diff --git a/target/linux/generic/backport-6.1/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch b/target/linux/generic/backport-6.1/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch
new file mode 100644
index 0000000000..56d7a1348f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch
@@ -0,0 +1,33 @@
+From 3337a6e04ddf2923a1bdcf3d31b3b52412bf82dd Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Wed, 1 Feb 2023 19:23:31 +0100
+Subject: [PATCH 13/13] mtk_sgmii: enable PCS polling to allow SFP work
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently there is no IRQ handling (even the SGMII supports it).
+Enable polling to support SFP ports.
+
+Fixes: 14a44ab0330d ("net: mtk_eth_soc: partially convert to phylink_pcs")
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+[ bmork: changed "1" => "true" ]
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -187,6 +187,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ return PTR_ERR(ss->pcs[i].regmap);
+
+ ss->pcs[i].pcs.ops = &mtk_pcs_ops;
++ ss->pcs[i].pcs.poll = true;
+ ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
+ }
+
diff --git a/target/linux/generic/backport-6.1/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch b/target/linux/generic/backport-6.1/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch
new file mode 100644
index 0000000000..a63b110914
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch
@@ -0,0 +1,48 @@
+From 611e2dabb4b3243d176739fd6a5a34d007fa3f86 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 14 Mar 2023 00:34:26 +0000
+Subject: [PATCH 1/2] net: ethernet: mtk_eth_soc: reset PCS state
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reset the internal PCS state machine when changing interface mode.
+This prevents confusing the state machine when changing interface
+modes, e.g. from SGMII to 2500Base-X or vice-versa.
+
+Fixes: 7e538372694b ("net: ethernet: mediatek: Re-add support SGMII")
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Tested-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++++
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 4 ++++
+ 2 files changed, 8 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -539,6 +539,10 @@
+ #define SGMII_SEND_AN_ERROR_EN BIT(11)
+ #define SGMII_IF_MODE_MASK GENMASK(5, 1)
+
++/* Register to reset SGMII design */
++#define SGMII_RESERVED_0 0x34
++#define SGMII_SW_RESET BIT(0)
++
+ /* Register to set SGMII speed, ANA RG_ Control Signals III*/
+ #define SGMSYS_ANA_RG_CS3 0x2028
+ #define RG_PHY_SPEED_MASK (BIT(2) | BIT(3))
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -88,6 +88,10 @@ static int mtk_pcs_config(struct phylink
+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+ SGMII_PHYA_PWD, SGMII_PHYA_PWD);
+
++ /* Reset SGMII PCS state */
++ regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0,
++ SGMII_SW_RESET, SGMII_SW_RESET);
++
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ rgc3 = RG_PHY_SPEED_3_125G;
+ else
diff --git a/target/linux/generic/backport-6.1/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch b/target/linux/generic/backport-6.1/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch
new file mode 100644
index 0000000000..0fabeea20c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch
@@ -0,0 +1,103 @@
+From 6e933a804c7db8be64f367f33e63cd7dcc302ebb Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 14 Mar 2023 00:34:45 +0000
+Subject: [PATCH 2/2] net: ethernet: mtk_eth_soc: only write values if needed
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Only restart auto-negotiation and write link timer if actually
+necessary. This prevents losing the link in case of minor
+changes.
+
+Fixes: 7e538372694b ("net: ethernet: mediatek: Re-add support SGMII")
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Tested-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 24 +++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -38,20 +38,16 @@ static int mtk_pcs_config(struct phylink
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+ {
++ bool mode_changed = false, changed, use_an;
+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+ unsigned int rgc3, sgm_mode, bmcr;
+ int advertise, link_timer;
+- bool changed, use_an;
+
+ advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
+ advertising);
+ if (advertise < 0)
+ return advertise;
+
+- link_timer = phylink_get_link_timer_ns(interface);
+- if (link_timer < 0)
+- return link_timer;
+-
+ /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
+ * we assume that fixes it's speed at bitrate = line rate (in
+ * other words, 1000Mbps or 2500Mbps).
+@@ -77,13 +73,16 @@ static int mtk_pcs_config(struct phylink
+ }
+
+ if (use_an) {
+- /* FIXME: Do we need to set AN_RESTART here? */
+- bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE;
++ bmcr = SGMII_AN_ENABLE;
+ } else {
+ bmcr = 0;
+ }
+
+ if (mpcs->interface != interface) {
++ link_timer = phylink_get_link_timer_ns(interface);
++ if (link_timer < 0)
++ return link_timer;
++
+ /* PHYA power down */
+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+ SGMII_PHYA_PWD, SGMII_PHYA_PWD);
+@@ -101,16 +100,17 @@ static int mtk_pcs_config(struct phylink
+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
+ RG_PHY_SPEED_3_125G, rgc3);
+
++ /* Setup the link timer */
++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
++
+ mpcs->interface = interface;
++ mode_changed = true;
+ }
+
+ /* Update the advertisement, noting whether it has changed */
+ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
+ SGMII_ADVERTISE, advertise, &changed);
+
+- /* Setup the link timer and QPHY power up inside SGMIISYS */
+- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
+-
+ /* Update the sgmsys mode register */
+ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+ SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
+@@ -118,7 +118,7 @@ static int mtk_pcs_config(struct phylink
+
+ /* Update the BMCR */
+ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+- SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr);
++ SGMII_AN_ENABLE, bmcr);
+
+ /* Release PHYA power down state
+ * Only removing bit SGMII_PHYA_PWD isn't enough.
+@@ -132,7 +132,7 @@ static int mtk_pcs_config(struct phylink
+ usleep_range(50, 100);
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+
+- return changed;
++ return changed || mode_changed;
+ }
+
+ static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
diff --git a/target/linux/generic/backport-6.1/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch b/target/linux/generic/backport-6.1/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch
new file mode 100644
index 0000000000..c1cf323800
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch
@@ -0,0 +1,200 @@
+From f5d43ddd334b7c32fcaed9ba46afbd85cb467f1f Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 19 Mar 2023 12:56:28 +0000
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for MT7981 SoC
+
+The MediaTek MT7981 SoC comes with two 1G/2.5G SGMII ports, just like
+MT7986.
+
+In addition MT7981 is equipped with a built-in 1000Base-T PHY which can
+be used with GMAC1.
+
+As many MT7981 boards make use of inverting SGMII signal polarity, add
+new device-tree attribute 'mediatek,pn_swap' to support them.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_path.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c
+@@ -96,12 +96,20 @@ static int set_mux_gmac2_gmac0_to_gephy(
+
+ static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path)
+ {
+- unsigned int val = 0;
++ unsigned int val = 0, mask = 0, reg = 0;
+ bool updated = true;
+
+ switch (path) {
+ case MTK_ETH_PATH_GMAC2_SGMII:
+- val = CO_QPHY_SEL;
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_U3_COPHY_V2)) {
++ reg = USB_PHY_SWITCH_REG;
++ val = SGMII_QPHY_SEL;
++ mask = QPHY_SEL_MASK;
++ } else {
++ reg = INFRA_MISC2;
++ val = CO_QPHY_SEL;
++ mask = val;
++ }
+ break;
+ default:
+ updated = false;
+@@ -109,7 +117,7 @@ static int set_mux_u3_gmac2_to_qphy(stru
+ }
+
+ if (updated)
+- regmap_update_bits(eth->infra, INFRA_MISC2, CO_QPHY_SEL, val);
++ regmap_update_bits(eth->infra, reg, mask, val);
+
+ dev_dbg(eth->dev, "path %s in %s updated = %d\n",
+ mtk_eth_path_name(path), __func__, updated);
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4755,6 +4755,26 @@ static const struct mtk_soc_data mt7629_
+ },
+ };
+
++static const struct mtk_soc_data mt7981_data = {
++ .reg_map = &mt7986_reg_map,
++ .ana_rgc3 = 0x128,
++ .caps = MT7981_CAPS,
++ .hw_features = MTK_HW_FEATURES,
++ .required_clks = MT7981_CLKS_BITMAP,
++ .required_pctl = false,
++ .offload_version = 2,
++ .hash_offset = 4,
++ .foe_entry_size = sizeof(struct mtk_foe_entry),
++ .txrx = {
++ .txd_size = sizeof(struct mtk_tx_dma_v2),
++ .rxd_size = sizeof(struct mtk_rx_dma_v2),
++ .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
++ .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
++ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
++ .dma_len_offset = 8,
++ },
++};
++
+ static const struct mtk_soc_data mt7986_data = {
+ .reg_map = &mt7986_reg_map,
+ .ana_rgc3 = 0x128,
+@@ -4797,6 +4817,7 @@ const struct of_device_id of_mtk_match[]
+ { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data},
+ { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data},
+ { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data},
++ { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data},
+ { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data},
+ { .compatible = "ralink,rt5350-eth", .data = &rt5350_data},
+ {},
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -553,11 +553,22 @@
+ #define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
+ #define SGMII_PHYA_PWD BIT(4)
+
++/* Register to QPHY wrapper control */
++#define SGMSYS_QPHY_WRAP_CTRL 0xec
++#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
++#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
++#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
++
+ /* Infrasys subsystem config registers */
+ #define INFRA_MISC2 0x70c
+ #define CO_QPHY_SEL BIT(0)
+ #define GEPHY_MAC_SEL BIT(1)
+
++/* Top misc registers */
++#define USB_PHY_SWITCH_REG 0x218
++#define QPHY_SEL_MASK GENMASK(1, 0)
++#define SGMII_QPHY_SEL 0x2
++
+ /* MT7628/88 specific stuff */
+ #define MT7628_PDMA_OFFSET 0x0800
+ #define MT7628_SDM_OFFSET 0x0c00
+@@ -738,6 +749,17 @@ enum mtk_clks_map {
+ BIT(MTK_CLK_SGMII2_CDR_FB) | \
+ BIT(MTK_CLK_SGMII_CK) | \
+ BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP))
++#define MT7981_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \
++ BIT(MTK_CLK_WOCPU0) | \
++ BIT(MTK_CLK_SGMII_TX_250M) | \
++ BIT(MTK_CLK_SGMII_RX_250M) | \
++ BIT(MTK_CLK_SGMII_CDR_REF) | \
++ BIT(MTK_CLK_SGMII_CDR_FB) | \
++ BIT(MTK_CLK_SGMII2_TX_250M) | \
++ BIT(MTK_CLK_SGMII2_RX_250M) | \
++ BIT(MTK_CLK_SGMII2_CDR_REF) | \
++ BIT(MTK_CLK_SGMII2_CDR_FB) | \
++ BIT(MTK_CLK_SGMII_CK))
+ #define MT7986_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \
+ BIT(MTK_CLK_WOCPU1) | BIT(MTK_CLK_WOCPU0) | \
+ BIT(MTK_CLK_SGMII_TX_250M) | \
+@@ -851,6 +873,7 @@ enum mkt_eth_capabilities {
+ MTK_NETSYS_V2_BIT,
+ MTK_SOC_MT7628_BIT,
+ MTK_RSTCTRL_PPE1_BIT,
++ MTK_U3_COPHY_V2_BIT,
+
+ /* MUX BITS*/
+ MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
+@@ -885,6 +908,7 @@ enum mkt_eth_capabilities {
+ #define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT)
+ #define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT)
+ #define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT)
++#define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT)
+
+ #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \
+ BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT)
+@@ -963,6 +987,11 @@ enum mkt_eth_capabilities {
+ MTK_MUX_U3_GMAC2_TO_QPHY | \
+ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA)
+
++#define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \
++ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
++ MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \
++ MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1)
++
+ #define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \
+ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
+ MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1)
+@@ -1076,12 +1105,14 @@ struct mtk_soc_data {
+ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
+ * @interface: Currently configured interface mode
+ * @pcs: Phylink PCS structure
++ * @flags: Flags indicating hardware properties
+ */
+ struct mtk_pcs {
+ struct regmap *regmap;
+ u32 ana_rgc3;
+ phy_interface_t interface;
+ struct phylink_pcs pcs;
++ u32 flags;
+ };
+
+ /* struct mtk_sgmii - This is the structure holding sgmii regmap and its
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -87,6 +87,11 @@ static int mtk_pcs_config(struct phylink
+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+ SGMII_PHYA_PWD, SGMII_PHYA_PWD);
+
++ if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
++ SGMII_PN_SWAP_MASK,
++ SGMII_PN_SWAP_TX_RX);
++
+ /* Reset SGMII PCS state */
+ regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0,
+ SGMII_SW_RESET, SGMII_SW_RESET);
+@@ -186,6 +191,11 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+
+ ss->pcs[i].ana_rgc3 = ana_rgc3;
+ ss->pcs[i].regmap = syscon_node_to_regmap(np);
++
++ ss->pcs[i].flags = 0;
++ if (of_property_read_bool(np, "mediatek,pnswap"))
++ ss->pcs[i].flags |= MTK_SGMII_FLAG_PN_SWAP;
++
+ of_node_put(np);
+ if (IS_ERR(ss->pcs[i].regmap))
+ return PTR_ERR(ss->pcs[i].regmap);
diff --git a/target/linux/generic/backport-6.1/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch b/target/linux/generic/backport-6.1/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch
new file mode 100644
index 0000000000..9def19d67e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch
@@ -0,0 +1,76 @@
+From c0a440031d4314d1023c1b87f43a4233634eebdb Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 19 Mar 2023 12:57:15 +0000
+Subject: [PATCH] net: ethernet: mtk_eth_soc: set MDIO bus clock frequency
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Set MDIO bus clock frequency and allow setting a custom maximum
+frequency from device tree.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Tested-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 21 +++++++++++++++++++++
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 7 +++++++
+ 2 files changed, 28 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -702,8 +702,10 @@ static const struct phylink_mac_ops mtk_
+
+ static int mtk_mdio_init(struct mtk_eth *eth)
+ {
++ unsigned int max_clk = 2500000, divider;
+ struct device_node *mii_np;
+ int ret;
++ u32 val;
+
+ mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus");
+ if (!mii_np) {
+@@ -729,6 +731,25 @@ static int mtk_mdio_init(struct mtk_eth
+ eth->mii_bus->parent = eth->dev;
+
+ snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np);
++
++ if (!of_property_read_u32(mii_np, "clock-frequency", &val)) {
++ if (val > MDC_MAX_FREQ || val < MDC_MAX_FREQ / MDC_MAX_DIVIDER) {
++ dev_err(eth->dev, "MDIO clock frequency out of range");
++ ret = -EINVAL;
++ goto err_put_node;
++ }
++ max_clk = val;
++ }
++ divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63);
++
++ /* Configure MDC Divider */
++ val = mtk_r32(eth, MTK_PPSC);
++ val &= ~PPSC_MDC_CFG;
++ val |= FIELD_PREP(PPSC_MDC_CFG, divider) | PPSC_MDC_TURBO;
++ mtk_w32(eth, val, MTK_PPSC);
++
++ dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider);
++
+ ret = of_mdiobus_register(eth->mii_bus, mii_np);
+
+ err_put_node:
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -363,6 +363,13 @@
+ #define RX_DMA_VTAG_V2 BIT(0)
+ #define RX_DMA_L4_VALID_V2 BIT(2)
+
++/* PHY Polling and SMI Master Control registers */
++#define MTK_PPSC 0x10000
++#define PPSC_MDC_CFG GENMASK(29, 24)
++#define PPSC_MDC_TURBO BIT(20)
++#define MDC_MAX_FREQ 25000000
++#define MDC_MAX_DIVIDER 63
++
+ /* PHY Indirect Access Control registers */
+ #define MTK_PHY_IAC 0x10004
+ #define PHY_IAC_ACCESS BIT(31)
diff --git a/target/linux/generic/backport-6.1/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch b/target/linux/generic/backport-6.1/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch
new file mode 100644
index 0000000000..203c5dc48b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch
@@ -0,0 +1,512 @@
+From 2a3ec7ae313310c1092e4256208cc04d1958e469 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 19 Mar 2023 12:58:02 +0000
+Subject: [PATCH] net: ethernet: mtk_eth_soc: switch to external PCS driver
+
+Now that we got a PCS driver, use it and remove the now redundant
+PCS code and it's header macros from the Ethernet driver.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/Kconfig | 2 +
+ drivers/net/ethernet/mediatek/Makefile | 2 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 61 +++++-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 93 +--------
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 217 --------------------
+ 5 files changed, 56 insertions(+), 319 deletions(-)
+ delete mode 100644 drivers/net/ethernet/mediatek/mtk_sgmii.c
+
+--- a/drivers/net/ethernet/mediatek/Kconfig
++++ b/drivers/net/ethernet/mediatek/Kconfig
+@@ -18,6 +18,8 @@ config NET_MEDIATEK_SOC
+ select DIMLIB
+ select PAGE_POOL
+ select PAGE_POOL_STATS
++ select PCS_MTK_LYNXI
++ select REGMAP_MMIO
+ help
+ This driver supports the gigabit ethernet MACs in the
+ MediaTek SoC family.
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -4,7 +4,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-y := mtk_eth_soc.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_wed_wo.o
+ ifdef CONFIG_DEBUG_FS
+ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -20,6 +20,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/pinctrl/devinfo.h>
+ #include <linux/phylink.h>
++#include <linux/pcs/pcs-mtk-lynxi.h>
+ #include <linux/jhash.h>
+ #include <linux/bitfield.h>
+ #include <net/dsa.h>
+@@ -357,7 +358,7 @@ static struct phylink_pcs *mtk_mac_selec
+ sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+ 0 : mac->id;
+
+- return mtk_sgmii_select_pcs(eth->sgmii, sid);
++ return eth->sgmii_pcs[sid];
+ }
+
+ return NULL;
+@@ -3977,8 +3978,17 @@ static int mtk_unreg_dev(struct mtk_eth
+ return 0;
+ }
+
++static void mtk_sgmii_destroy(struct mtk_eth *eth)
++{
++ int i;
++
++ for (i = 0; i < MTK_MAX_DEVS; i++)
++ mtk_pcs_lynxi_destroy(eth->sgmii_pcs[i]);
++}
++
+ static int mtk_cleanup(struct mtk_eth *eth)
+ {
++ mtk_sgmii_destroy(eth);
+ mtk_unreg_dev(eth);
+ mtk_free_dev(eth);
+ cancel_work_sync(&eth->pending_work);
+@@ -4408,6 +4418,36 @@ void mtk_eth_set_dma_device(struct mtk_e
+ rtnl_unlock();
+ }
+
++static int mtk_sgmii_init(struct mtk_eth *eth)
++{
++ struct device_node *np;
++ struct regmap *regmap;
++ u32 flags;
++ int i;
++
++ for (i = 0; i < MTK_MAX_DEVS; i++) {
++ np = of_parse_phandle(eth->dev->of_node, "mediatek,sgmiisys", i);
++ if (!np)
++ break;
++
++ regmap = syscon_node_to_regmap(np);
++ flags = 0;
++ if (of_property_read_bool(np, "mediatek,pnswap"))
++ flags |= MTK_SGMII_FLAG_PN_SWAP;
++
++ of_node_put(np);
++
++ if (IS_ERR(regmap))
++ return PTR_ERR(regmap);
++
++ eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev, regmap,
++ eth->soc->ana_rgc3,
++ flags);
++ }
++
++ return 0;
++}
++
+ static int mtk_probe(struct platform_device *pdev)
+ {
+ struct resource *res = NULL;
+@@ -4471,13 +4511,7 @@ static int mtk_probe(struct platform_dev
+ }
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
+- eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
+- GFP_KERNEL);
+- if (!eth->sgmii)
+- return -ENOMEM;
+-
+- err = mtk_sgmii_init(eth->sgmii, pdev->dev.of_node,
+- eth->soc->ana_rgc3);
++ err = mtk_sgmii_init(eth);
+
+ if (err)
+ return err;
+@@ -4488,14 +4522,17 @@ static int mtk_probe(struct platform_dev
+ "mediatek,pctl");
+ if (IS_ERR(eth->pctl)) {
+ dev_err(&pdev->dev, "no pctl regmap found\n");
+- return PTR_ERR(eth->pctl);
++ err = PTR_ERR(eth->pctl);
++ goto err_destroy_sgmii;
+ }
+ }
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- if (!res)
+- return -EINVAL;
++ if (!res) {
++ err = -EINVAL;
++ goto err_destroy_sgmii;
++ }
+ }
+
+ if (eth->soc->offload_version) {
+@@ -4655,6 +4692,8 @@ err_deinit_hw:
+ mtk_hw_deinit(eth);
+ err_wed_exit:
+ mtk_wed_exit();
++err_destroy_sgmii:
++ mtk_sgmii_destroy(eth);
+
+ return err;
+ }
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -507,65 +507,6 @@
+ #define ETHSYS_DMA_AG_MAP_QDMA BIT(1)
+ #define ETHSYS_DMA_AG_MAP_PPE BIT(2)
+
+-/* SGMII subsystem config registers */
+-/* BMCR (low 16) BMSR (high 16) */
+-#define SGMSYS_PCS_CONTROL_1 0x0
+-#define SGMII_BMCR GENMASK(15, 0)
+-#define SGMII_BMSR GENMASK(31, 16)
+-#define SGMII_AN_RESTART BIT(9)
+-#define SGMII_ISOLATE BIT(10)
+-#define SGMII_AN_ENABLE BIT(12)
+-#define SGMII_LINK_STATYS BIT(18)
+-#define SGMII_AN_ABILITY BIT(19)
+-#define SGMII_AN_COMPLETE BIT(21)
+-#define SGMII_PCS_FAULT BIT(23)
+-#define SGMII_AN_EXPANSION_CLR BIT(30)
+-
+-#define SGMSYS_PCS_ADVERTISE 0x8
+-#define SGMII_ADVERTISE GENMASK(15, 0)
+-#define SGMII_LPA GENMASK(31, 16)
+-
+-/* Register to programmable link timer, the unit in 2 * 8ns */
+-#define SGMSYS_PCS_LINK_TIMER 0x18
+-#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
+-#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & SGMII_LINK_TIMER_MASK)
+-
+-/* Register to control remote fault */
+-#define SGMSYS_SGMII_MODE 0x20
+-#define SGMII_IF_MODE_SGMII BIT(0)
+-#define SGMII_SPEED_DUPLEX_AN BIT(1)
+-#define SGMII_SPEED_MASK GENMASK(3, 2)
+-#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
+-#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
+-#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
+-#define SGMII_DUPLEX_HALF BIT(4)
+-#define SGMII_IF_MODE_BIT5 BIT(5)
+-#define SGMII_REMOTE_FAULT_DIS BIT(8)
+-#define SGMII_CODE_SYNC_SET_VAL BIT(9)
+-#define SGMII_CODE_SYNC_SET_EN BIT(10)
+-#define SGMII_SEND_AN_ERROR_EN BIT(11)
+-#define SGMII_IF_MODE_MASK GENMASK(5, 1)
+-
+-/* Register to reset SGMII design */
+-#define SGMII_RESERVED_0 0x34
+-#define SGMII_SW_RESET BIT(0)
+-
+-/* Register to set SGMII speed, ANA RG_ Control Signals III*/
+-#define SGMSYS_ANA_RG_CS3 0x2028
+-#define RG_PHY_SPEED_MASK (BIT(2) | BIT(3))
+-#define RG_PHY_SPEED_1_25G 0x0
+-#define RG_PHY_SPEED_3_125G BIT(2)
+-
+-/* Register to power up QPHY */
+-#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
+-#define SGMII_PHYA_PWD BIT(4)
+-
+-/* Register to QPHY wrapper control */
+-#define SGMSYS_QPHY_WRAP_CTRL 0xec
+-#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
+-#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
+-#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
+-
+ /* Infrasys subsystem config registers */
+ #define INFRA_MISC2 0x70c
+ #define CO_QPHY_SEL BIT(0)
+@@ -1105,31 +1046,6 @@ struct mtk_soc_data {
+ /* currently no SoC has more than 2 macs */
+ #define MTK_MAX_DEVS 2
+
+-/* struct mtk_pcs - This structure holds each sgmii regmap and associated
+- * data
+- * @regmap: The register map pointing at the range used to setup
+- * SGMII modes
+- * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
+- * @interface: Currently configured interface mode
+- * @pcs: Phylink PCS structure
+- * @flags: Flags indicating hardware properties
+- */
+-struct mtk_pcs {
+- struct regmap *regmap;
+- u32 ana_rgc3;
+- phy_interface_t interface;
+- struct phylink_pcs pcs;
+- u32 flags;
+-};
+-
+-/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
+- * characteristics
+- * @pcs Array of individual PCS structures
+- */
+-struct mtk_sgmii {
+- struct mtk_pcs pcs[MTK_MAX_DEVS];
+-};
+-
+ /* struct mtk_eth - This is the main datasructure for holding the state
+ * of the driver
+ * @dev: The device pointer
+@@ -1149,6 +1065,7 @@ struct mtk_sgmii {
+ * MII modes
+ * @infra: The register map pointing at the range used to setup
+ * SGMII and GePHY path
++ * @sgmii_pcs: Pointers to mtk-pcs-lynxi phylink_pcs instances
+ * @pctl: The register map pointing at the range used to setup
+ * GMAC port drive/slew values
+ * @dma_refcnt: track how many netdevs are using the DMA engine
+@@ -1189,8 +1106,8 @@ struct mtk_eth {
+ u32 msg_enable;
+ unsigned long sysclk;
+ struct regmap *ethsys;
+- struct regmap *infra;
+- struct mtk_sgmii *sgmii;
++ struct regmap *infra;
++ struct phylink_pcs *sgmii_pcs[MTK_MAX_DEVS];
+ struct regmap *pctl;
+ bool hwlro;
+ refcount_t dma_refcnt;
+@@ -1352,10 +1269,6 @@ void mtk_stats_update_mac(struct mtk_mac
+ void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
+ u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+
+-struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id);
+-int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
+- u32 ana_rgc3);
+-
+ int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ /dev/null
+@@ -1,217 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-// Copyright (c) 2018-2019 MediaTek Inc.
+-
+-/* A library for MediaTek SGMII circuit
+- *
+- * Author: Sean Wang <sean.wang@mediatek.com>
+- *
+- */
+-
+-#include <linux/mfd/syscon.h>
+-#include <linux/of.h>
+-#include <linux/phylink.h>
+-#include <linux/regmap.h>
+-
+-#include "mtk_eth_soc.h"
+-
+-static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
+-{
+- return container_of(pcs, struct mtk_pcs, pcs);
+-}
+-
+-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 bm, adv;
+-
+- /* Read the BMSR and LPA */
+- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
+- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
+-
+- phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
+- FIELD_GET(SGMII_LPA, adv));
+-}
+-
+-static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+- phy_interface_t interface,
+- const unsigned long *advertising,
+- bool permit_pause_to_mac)
+-{
+- bool mode_changed = false, changed, use_an;
+- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+- unsigned int rgc3, sgm_mode, bmcr;
+- int advertise, link_timer;
+-
+- advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
+- advertising);
+- if (advertise < 0)
+- return advertise;
+-
+- /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
+- * we assume that fixes it's speed at bitrate = line rate (in
+- * other words, 1000Mbps or 2500Mbps).
+- */
+- if (interface == PHY_INTERFACE_MODE_SGMII) {
+- sgm_mode = SGMII_IF_MODE_SGMII;
+- if (phylink_autoneg_inband(mode)) {
+- sgm_mode |= SGMII_REMOTE_FAULT_DIS |
+- SGMII_SPEED_DUPLEX_AN;
+- use_an = true;
+- } else {
+- use_an = false;
+- }
+- } else if (phylink_autoneg_inband(mode)) {
+- /* 1000base-X or 2500base-X autoneg */
+- sgm_mode = SGMII_REMOTE_FAULT_DIS;
+- use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+- advertising);
+- } else {
+- /* 1000base-X or 2500base-X without autoneg */
+- sgm_mode = 0;
+- use_an = false;
+- }
+-
+- if (use_an) {
+- bmcr = SGMII_AN_ENABLE;
+- } else {
+- bmcr = 0;
+- }
+-
+- if (mpcs->interface != interface) {
+- link_timer = phylink_get_link_timer_ns(interface);
+- if (link_timer < 0)
+- return link_timer;
+-
+- /* PHYA power down */
+- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+- SGMII_PHYA_PWD, SGMII_PHYA_PWD);
+-
+- if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
+- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
+- SGMII_PN_SWAP_MASK,
+- SGMII_PN_SWAP_TX_RX);
+-
+- /* Reset SGMII PCS state */
+- regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0,
+- SGMII_SW_RESET, SGMII_SW_RESET);
+-
+- if (interface == PHY_INTERFACE_MODE_2500BASEX)
+- rgc3 = RG_PHY_SPEED_3_125G;
+- else
+- rgc3 = 0;
+-
+- /* Configure the underlying interface speed */
+- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
+- RG_PHY_SPEED_3_125G, rgc3);
+-
+- /* Setup the link timer */
+- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
+-
+- mpcs->interface = interface;
+- mode_changed = true;
+- }
+-
+- /* Update the advertisement, noting whether it has changed */
+- regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
+- SGMII_ADVERTISE, advertise, &changed);
+-
+- /* Update the sgmsys mode register */
+- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+- SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
+- SGMII_IF_MODE_SGMII, sgm_mode);
+-
+- /* Update the BMCR */
+- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+- SGMII_AN_ENABLE, bmcr);
+-
+- /* Release PHYA power down state
+- * Only removing bit SGMII_PHYA_PWD isn't enough.
+- * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
+- * prevents SGMII from working. The SGMII still shows link but no traffic
+- * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
+- * taken from a good working state of the SGMII interface.
+- * Unknown how much the QPHY needs but it is racy without a sleep.
+- * Tested on mt7622 & mt7986.
+- */
+- usleep_range(50, 100);
+- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+-
+- return changed || mode_changed;
+-}
+-
+-static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
+-{
+- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+-
+- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
+- SGMII_AN_RESTART, SGMII_AN_RESTART);
+-}
+-
+-static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+- phy_interface_t interface, int speed, int duplex)
+-{
+- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
+- unsigned int sgm_mode;
+-
+- if (!phylink_autoneg_inband(mode)) {
+- /* Force the speed and duplex setting */
+- if (speed == SPEED_10)
+- sgm_mode = SGMII_SPEED_10;
+- else if (speed == SPEED_100)
+- sgm_mode = SGMII_SPEED_100;
+- else
+- sgm_mode = SGMII_SPEED_1000;
+-
+- if (duplex != DUPLEX_FULL)
+- sgm_mode |= SGMII_DUPLEX_HALF;
+-
+- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
+- SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
+- sgm_mode);
+- }
+-}
+-
+-static const struct phylink_pcs_ops mtk_pcs_ops = {
+- .pcs_get_state = mtk_pcs_get_state,
+- .pcs_config = mtk_pcs_config,
+- .pcs_an_restart = mtk_pcs_restart_an,
+- .pcs_link_up = mtk_pcs_link_up,
+-};
+-
+-int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
+-{
+- struct device_node *np;
+- int i;
+-
+- for (i = 0; i < MTK_MAX_DEVS; i++) {
+- np = of_parse_phandle(r, "mediatek,sgmiisys", i);
+- if (!np)
+- break;
+-
+- ss->pcs[i].ana_rgc3 = ana_rgc3;
+- ss->pcs[i].regmap = syscon_node_to_regmap(np);
+-
+- ss->pcs[i].flags = 0;
+- if (of_property_read_bool(np, "mediatek,pnswap"))
+- ss->pcs[i].flags |= MTK_SGMII_FLAG_PN_SWAP;
+-
+- of_node_put(np);
+- if (IS_ERR(ss->pcs[i].regmap))
+- return PTR_ERR(ss->pcs[i].regmap);
+-
+- ss->pcs[i].pcs.ops = &mtk_pcs_ops;
+- ss->pcs[i].pcs.poll = true;
+- ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
+- }
+-
+- return 0;
+-}
+-
+-struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
+-{
+- if (!ss->pcs[id].regmap)
+- return NULL;
+-
+- return &ss->pcs[id].pcs;
+-}
diff --git a/target/linux/generic/backport-6.1/733-v6.4-21-net-mtk_eth_soc-use-WO-firmware-for-MT7981.patch b/target/linux/generic/backport-6.1/733-v6.4-21-net-mtk_eth_soc-use-WO-firmware-for-MT7981.patch
new file mode 100644
index 0000000000..9ce2735951
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.4-21-net-mtk_eth_soc-use-WO-firmware-for-MT7981.patch
@@ -0,0 +1,46 @@
+From f5af7931d2a2cae66d0f9dad4ba517b1b00620b3 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 19 Apr 2023 19:07:23 +0100
+Subject: [PATCH] net: mtk_eth_soc: use WO firmware for MT7981
+
+In order to support wireless offloading on MT7981 we need to load the
+appropriate firmware. Recognize MT7981 and load mt7981_wo.bin.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 7 ++++++-
+ drivers/net/ethernet/mediatek/mtk_wed_wo.h | 1 +
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -326,7 +326,11 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
+ wo->hw->index + 1);
+
+ /* load firmware */
+- fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0;
++ if (of_device_is_compatible(wo->hw->node, "mediatek,mt7981-wed"))
++ fw_name = MT7981_FIRMWARE_WO;
++ else
++ fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0;
++
+ ret = request_firmware(&fw, fw_name, wo->hw->dev);
+ if (ret)
+ return ret;
+@@ -386,5 +390,6 @@ int mtk_wed_mcu_init(struct mtk_wed_wo *
+ 100, MTK_FW_DL_TIMEOUT);
+ }
+
++MODULE_FIRMWARE(MT7981_FIRMWARE_WO);
+ MODULE_FIRMWARE(MT7986_FIRMWARE_WO0);
+ MODULE_FIRMWARE(MT7986_FIRMWARE_WO1);
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
+@@ -88,6 +88,7 @@ enum mtk_wed_dummy_cr_idx {
+ MTK_WED_DUMMY_CR_WO_STATUS,
+ };
+
++#define MT7981_FIRMWARE_WO "mediatek/mt7981_wo.bin"
+ #define MT7986_FIRMWARE_WO0 "mediatek/mt7986_wo_0.bin"
+ #define MT7986_FIRMWARE_WO1 "mediatek/mt7986_wo_1.bin"
+
diff --git a/target/linux/generic/backport-6.1/733-v6.4-22-net-ethernet-mtk_eth_soc-fix-NULL-pointer-dereferenc.patch b/target/linux/generic/backport-6.1/733-v6.4-22-net-ethernet-mtk_eth_soc-fix-NULL-pointer-dereferenc.patch
new file mode 100644
index 0000000000..d715c4aa68
--- /dev/null
+++ b/target/linux/generic/backport-6.1/733-v6.4-22-net-ethernet-mtk_eth_soc-fix-NULL-pointer-dereferenc.patch
@@ -0,0 +1,28 @@
+From 7c83e28f10830aa5105c25eaabe890e3adac36aa Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 9 May 2023 03:20:06 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix NULL pointer dereference
+
+Check for NULL pointer to avoid kernel crashing in case of missing WO
+firmware in case only a single WEDv2 device has been initialized, e.g. on
+MT7981 which can connect just one wireless frontend.
+
+Fixes: 86ce0d09e424 ("net: ethernet: mtk_eth_soc: use WO firmware for MT7981")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_wed.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -647,7 +647,7 @@ __mtk_wed_detach(struct mtk_wed_device *
+ BIT(hw->index), BIT(hw->index));
+ }
+
+- if (!hw_list[!hw->index]->wed_dev &&
++ if ((!hw_list[!hw->index] || !hw_list[!hw->index]->wed_dev) &&
+ hw->eth->dma_dev != hw->eth->dev)
+ mtk_eth_set_dma_device(hw->eth, hw->eth->dev);
+
diff --git a/target/linux/generic/backport-6.1/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch b/target/linux/generic/backport-6.1/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch
new file mode 100644
index 0000000000..6788a2ec35
--- /dev/null
+++ b/target/linux/generic/backport-6.1/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch
@@ -0,0 +1,84 @@
+From b5375509184dc23d2b7fa0c5ed8763899ccc9674 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sat, 2 Oct 2021 19:58:11 +0200
+Subject: [PATCH] net: bgmac: improve handling PHY
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+1. Use info from DT if available
+
+It allows describing for example a fixed link. It's more accurate than
+just guessing there may be one (depending on a chipset).
+
+2. Verify PHY ID before trying to connect PHY
+
+PHY addr 0x1e (30) is special in Broadcom routers and means a switch
+connected as MDIO devices instead of a real PHY. Don't try connecting to
+it.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma.c | 33 ++++++++++++++--------
+ 1 file changed, 21 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -11,6 +11,7 @@
+ #include <linux/bcma/bcma.h>
+ #include <linux/brcmphy.h>
+ #include <linux/etherdevice.h>
++#include <linux/of_mdio.h>
+ #include <linux/of_net.h>
+ #include "bgmac.h"
+
+@@ -86,17 +87,28 @@ static int bcma_phy_connect(struct bgmac
+ struct phy_device *phy_dev;
+ char bus_id[MII_BUS_ID_SIZE + 3];
+
++ /* DT info should be the most accurate */
++ phy_dev = of_phy_get_and_connect(bgmac->net_dev, bgmac->dev->of_node,
++ bgmac_adjust_link);
++ if (phy_dev)
++ return 0;
++
+ /* Connect to the PHY */
+- snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id,
+- bgmac->phyaddr);
+- phy_dev = phy_connect(bgmac->net_dev, bus_id, bgmac_adjust_link,
+- PHY_INTERFACE_MODE_MII);
+- if (IS_ERR(phy_dev)) {
+- dev_err(bgmac->dev, "PHY connection failed\n");
+- return PTR_ERR(phy_dev);
++ if (bgmac->mii_bus && bgmac->phyaddr != BGMAC_PHY_NOREGS) {
++ snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id,
++ bgmac->phyaddr);
++ phy_dev = phy_connect(bgmac->net_dev, bus_id, bgmac_adjust_link,
++ PHY_INTERFACE_MODE_MII);
++ if (IS_ERR(phy_dev)) {
++ dev_err(bgmac->dev, "PHY connection failed\n");
++ return PTR_ERR(phy_dev);
++ }
++
++ return 0;
+ }
+
+- return 0;
++ /* Assume a fixed link to the switch port */
++ return bgmac_phy_connect_direct(bgmac);
+ }
+
+ static const struct bcma_device_id bgmac_bcma_tbl[] = {
+@@ -297,10 +309,7 @@ static int bgmac_probe(struct bcma_devic
+ bgmac->cco_ctl_maskset = bcma_bgmac_cco_ctl_maskset;
+ bgmac->get_bus_clock = bcma_bgmac_get_bus_clock;
+ bgmac->cmn_maskset32 = bcma_bgmac_cmn_maskset32;
+- if (bgmac->mii_bus)
+- bgmac->phy_connect = bcma_phy_connect;
+- else
+- bgmac->phy_connect = bgmac_phy_connect_direct;
++ bgmac->phy_connect = bcma_phy_connect;
+
+ err = bgmac_enet_probe(bgmac);
+ if (err)
diff --git a/target/linux/generic/backport-6.1/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch b/target/linux/generic/backport-6.1/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch
new file mode 100644
index 0000000000..f134828273
--- /dev/null
+++ b/target/linux/generic/backport-6.1/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch
@@ -0,0 +1,54 @@
+From 45c9d966688e7fad7f24bfc450547d91e4304d0b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sat, 2 Oct 2021 19:58:12 +0200
+Subject: [PATCH] net: bgmac: support MDIO described in DT
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Check ethernet controller DT node for "mdio" subnode and use it with
+of_mdiobus_register() when present. That allows specifying MDIO and its
+PHY devices in a standard DT based way.
+
+This is required for BCM53573 SoC support. That family is sometimes
+called Northstar (by marketing?) but is quite different from it. It uses
+different CPU(s) and many different hw blocks.
+
+One of shared blocks in BCM53573 is Ethernet controller. Switch however
+is not SRAB accessible (as it Northstar) but is MDIO attached.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c
+@@ -10,6 +10,7 @@
+
+ #include <linux/bcma/bcma.h>
+ #include <linux/brcmphy.h>
++#include <linux/of_mdio.h>
+ #include "bgmac.h"
+
+ static bool bcma_mdio_wait_value(struct bcma_device *core, u16 reg, u32 mask,
+@@ -211,6 +212,7 @@ struct mii_bus *bcma_mdio_mii_register(s
+ {
+ struct bcma_device *core = bgmac->bcma.core;
+ struct mii_bus *mii_bus;
++ struct device_node *np;
+ int err;
+
+ mii_bus = mdiobus_alloc();
+@@ -229,7 +231,9 @@ struct mii_bus *bcma_mdio_mii_register(s
+ mii_bus->parent = &core->dev;
+ mii_bus->phy_mask = ~(1 << bgmac->phyaddr);
+
+- err = mdiobus_register(mii_bus);
++ np = of_get_child_by_name(core->dev.of_node, "mdio");
++
++ err = of_mdiobus_register(mii_bus, np);
+ if (err) {
+ dev_err(&core->dev, "Registration of mii bus failed\n");
+ goto err_free_bus;
diff --git a/target/linux/generic/backport-6.1/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch b/target/linux/generic/backport-6.1/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch
new file mode 100644
index 0000000000..8f000ba918
--- /dev/null
+++ b/target/linux/generic/backport-6.1/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch
@@ -0,0 +1,48 @@
+From 0ccf8511182436183c031e8a2f740ae91a02c625 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 14 Sep 2021 14:33:45 +0200
+Subject: net: phy: at803x: add support for qca 8327 internal phy
+
+Add support for qca8327 internal phy needed for correct init of the
+switch port. It does use the same qca8337 function and reg just with a
+different id.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Tested-by: Rosen Penev <rosenp@gmail.com>
+Tested-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1412,6 +1412,19 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
++}, {
++ /* QCA8327 */
++ .phy_id = QCA8327_PHY_ID,
++ .phy_id_mask = QCA8K_PHY_ID_MASK,
++ .name = "QCA PHY 8327",
++ /* PHY_GBIT_FEATURES */
++ .probe = at803x_probe,
++ .flags = PHY_IS_INTERNAL,
++ .config_init = qca83xx_config_init,
++ .soft_reset = genphy_soft_reset,
++ .get_sset_count = at803x_get_sset_count,
++ .get_strings = at803x_get_strings,
++ .get_stats = at803x_get_stats,
+ }, };
+
+ module_phy_driver(at803x_driver);
+@@ -1422,6 +1435,8 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
+ { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
+ { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
++ { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
++ { PHY_ID_MATCH_EXACT(QCA8327_PHY_ID) },
+ { }
+ };
+
diff --git a/target/linux/generic/backport-6.1/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch b/target/linux/generic/backport-6.1/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch
new file mode 100644
index 0000000000..dc149a742b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch
@@ -0,0 +1,131 @@
+From 983d96a9116a328668601555d96736261d33170c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 16 Sep 2021 14:03:51 +0200
+Subject: [PATCH] net: dsa: b53: Include all ports in "enabled_ports"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Make "enabled_ports" bitfield contain all available switch ports
+including a CPU port. This way there is no need for fixup during
+initialization.
+
+For BCM53010, BCM53018 and BCM53019 include also other available ports.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Tested-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/b53/b53_common.c | 23 +++++++++++------------
+ 1 file changed, 11 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -2300,7 +2300,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5325_DEVICE_ID,
+ .dev_name = "BCM5325",
+ .vlans = 16,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x3f,
+ .arl_bins = 2,
+ .arl_buckets = 1024,
+ .imp_port = 5,
+@@ -2311,7 +2311,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5365_DEVICE_ID,
+ .dev_name = "BCM5365",
+ .vlans = 256,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x3f,
+ .arl_bins = 2,
+ .arl_buckets = 1024,
+ .imp_port = 5,
+@@ -2322,7 +2322,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5389_DEVICE_ID,
+ .dev_name = "BCM5389",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x11f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2336,7 +2336,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5395_DEVICE_ID,
+ .dev_name = "BCM5395",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x11f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2350,7 +2350,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5397_DEVICE_ID,
+ .dev_name = "BCM5397",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x11f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2364,7 +2364,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM5398_DEVICE_ID,
+ .dev_name = "BCM5398",
+ .vlans = 4096,
+- .enabled_ports = 0x7f,
++ .enabled_ports = 0x17f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2378,7 +2378,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53115_DEVICE_ID,
+ .dev_name = "BCM53115",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x11f,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .vta_regs = B53_VTA_REGS,
+@@ -2392,7 +2392,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53125_DEVICE_ID,
+ .dev_name = "BCM53125",
+ .vlans = 4096,
+- .enabled_ports = 0xff,
++ .enabled_ports = 0x1ff,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2434,7 +2434,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53010_DEVICE_ID,
+ .dev_name = "BCM53010",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x1bf,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2476,7 +2476,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53018_DEVICE_ID,
+ .dev_name = "BCM53018",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x1bf,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2490,7 +2490,7 @@ static const struct b53_chip_data b53_sw
+ .chip_id = BCM53019_DEVICE_ID,
+ .dev_name = "BCM53019",
+ .vlans = 4096,
+- .enabled_ports = 0x1f,
++ .enabled_ports = 0x1bf,
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+@@ -2632,7 +2632,6 @@ static int b53_switch_init(struct b53_de
+ dev->cpu_port = 5;
+ }
+
+- dev->enabled_ports |= BIT(dev->cpu_port);
+ dev->num_ports = fls(dev->enabled_ports);
+
+ dev->ds->num_ports = min_t(unsigned int, dev->num_ports, DSA_MAX_PORTS);
diff --git a/target/linux/generic/backport-6.1/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch b/target/linux/generic/backport-6.1/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch
new file mode 100644
index 0000000000..23805a9027
--- /dev/null
+++ b/target/linux/generic/backport-6.1/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch
@@ -0,0 +1,42 @@
+From b290c6384afabbca5ae6e2af72fb1b2bc37922be Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 16 Sep 2021 14:03:52 +0200
+Subject: [PATCH] net: dsa: b53: Drop BCM5301x workaround for a wrong CPU/IMP
+ port
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+On BCM5301x port 8 requires a fixed link when used.
+
+Years ago when b53 was an OpenWrt downstream driver (with configuration
+based on sometimes bugged NVRAM) there was a need for a fixup. In case
+of forcing fixed link for (incorrectly specified) port 5 the code had to
+actually setup port 8 link.
+
+For upstream b53 driver with setup based on DT there is no need for that
+workaround. In DT we have and require correct ports setup.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Tested-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/b53/b53_common.c | 6 ------
+ 1 file changed, 6 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -1291,12 +1291,6 @@ static void b53_adjust_link(struct dsa_s
+ return;
+ }
+ }
+- } else if (is5301x(dev)) {
+- if (port != dev->cpu_port) {
+- b53_force_port_config(dev, dev->cpu_port, 2000,
+- DUPLEX_FULL, true, true);
+- b53_force_link(dev, dev->cpu_port, 1);
+- }
+ }
+
+ /* Re-negotiate EEE if it was enabled already */
diff --git a/target/linux/generic/backport-6.1/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch b/target/linux/generic/backport-6.1/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch
new file mode 100644
index 0000000000..941fa23eb4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch
@@ -0,0 +1,32 @@
+From 3ff26b29230c54fea2353b63124c589b61953e14 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 16 Sep 2021 14:03:53 +0200
+Subject: [PATCH] net: dsa: b53: Improve flow control setup on BCM5301x
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+According to the Broadcom's reference driver flow control needs to be
+enabled for any CPU switch port (5, 7 or 8 - depending on which one is
+used). Current code makes it work only for the port 5. Use
+dsa_is_cpu_port() which solved that problem.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Tested-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/b53/b53_common.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -1222,7 +1222,7 @@ static void b53_adjust_link(struct dsa_s
+ return;
+
+ /* Enable flow control on BCM5301x's CPU port */
+- if (is5301x(dev) && port == dev->cpu_port)
++ if (is5301x(dev) && dsa_is_cpu_port(ds, port))
+ tx_pause = rx_pause = true;
+
+ if (phydev->pause) {
diff --git a/target/linux/generic/backport-6.1/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch b/target/linux/generic/backport-6.1/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch
new file mode 100644
index 0000000000..07d0ec03cf
--- /dev/null
+++ b/target/linux/generic/backport-6.1/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch
@@ -0,0 +1,205 @@
+From 7d5af56418d7d01e43247a33b6fe6492ea871923 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 16 Sep 2021 14:03:54 +0200
+Subject: [PATCH] net: dsa: b53: Drop unused "cpu_port" field
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It's set but never used anymore.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Tested-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/b53/b53_common.c | 28 ----------------------------
+ drivers/net/dsa/b53/b53_priv.h | 1 -
+ 2 files changed, 29 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -2298,7 +2298,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 2,
+ .arl_buckets = 1024,
+ .imp_port = 5,
+- .cpu_port = B53_CPU_PORT_25,
+ .duplex_reg = B53_DUPLEX_STAT_FE,
+ },
+ {
+@@ -2309,7 +2308,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 2,
+ .arl_buckets = 1024,
+ .imp_port = 5,
+- .cpu_port = B53_CPU_PORT_25,
+ .duplex_reg = B53_DUPLEX_STAT_FE,
+ },
+ {
+@@ -2320,7 +2318,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2334,7 +2331,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2348,7 +2344,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS_9798,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2362,7 +2357,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS_9798,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2377,7 +2371,6 @@ static const struct b53_chip_data b53_sw
+ .arl_buckets = 1024,
+ .vta_regs = B53_VTA_REGS,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+@@ -2390,7 +2383,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2404,7 +2396,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2418,7 +2409,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS_63XX,
+ .duplex_reg = B53_DUPLEX_STAT_63XX,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
+@@ -2432,7 +2422,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2446,7 +2435,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2460,7 +2448,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2474,7 +2461,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2488,7 +2474,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2502,7 +2487,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2516,7 +2500,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2545,7 +2528,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 1024,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2559,7 +2541,6 @@ static const struct b53_chip_data b53_sw
+ .arl_bins = 4,
+ .arl_buckets = 256,
+ .imp_port = 8,
+- .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+@@ -2585,7 +2566,6 @@ static int b53_switch_init(struct b53_de
+ dev->vta_regs[2] = chip->vta_regs[2];
+ dev->jumbo_pm_reg = chip->jumbo_pm_reg;
+ dev->imp_port = chip->imp_port;
+- dev->cpu_port = chip->cpu_port;
+ dev->num_vlans = chip->vlans;
+ dev->num_arl_bins = chip->arl_bins;
+ dev->num_arl_buckets = chip->arl_buckets;
+@@ -2617,13 +2597,6 @@ static int b53_switch_init(struct b53_de
+ break;
+ #endif
+ }
+- } else if (dev->chip_id == BCM53115_DEVICE_ID) {
+- u64 strap_value;
+-
+- b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value);
+- /* use second IMP port if GMII is enabled */
+- if (strap_value & SV_GMII_CTRL_115)
+- dev->cpu_port = 5;
+ }
+
+ dev->num_ports = fls(dev->enabled_ports);
+--- a/drivers/net/dsa/b53/b53_priv.h
++++ b/drivers/net/dsa/b53/b53_priv.h
+@@ -124,7 +124,6 @@ struct b53_device {
+ /* used ports mask */
+ u16 enabled_ports;
+ unsigned int imp_port;
+- unsigned int cpu_port;
+
+ /* connect specific data */
+ u8 current_page;
diff --git a/target/linux/generic/backport-6.1/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch b/target/linux/generic/backport-6.1/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch
new file mode 100644
index 0000000000..99d91dfa76
--- /dev/null
+++ b/target/linux/generic/backport-6.1/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch
@@ -0,0 +1,65 @@
+From b4df02b562f4aa14ff6811f30e1b4d2159585c59 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 19 Sep 2021 18:28:15 +0200
+Subject: net: phy: at803x: add support for qca 8327 A variant internal phy
+
+For qca8327 internal phy there are 2 different switch variant with 2
+different phy id. Add this missing variant so the internal phy can be
+correctly identified and fixed.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 25 ++++++++++++++++++++-----
+ 1 file changed, 20 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -150,7 +150,8 @@
+ #define ATH8035_PHY_ID 0x004dd072
+ #define AT8030_PHY_ID_MASK 0xffffffef
+
+-#define QCA8327_PHY_ID 0x004dd034
++#define QCA8327_A_PHY_ID 0x004dd033
++#define QCA8327_B_PHY_ID 0x004dd034
+ #define QCA8337_PHY_ID 0x004dd036
+ #define QCA8K_PHY_ID_MASK 0xffffffff
+
+@@ -1413,10 +1414,23 @@ static struct phy_driver at803x_driver[]
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
+ }, {
+- /* QCA8327 */
+- .phy_id = QCA8327_PHY_ID,
++ /* QCA8327-A from switch QCA8327-AL1A */
++ .phy_id = QCA8327_A_PHY_ID,
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
+- .name = "QCA PHY 8327",
++ .name = "QCA PHY 8327-A",
++ /* PHY_GBIT_FEATURES */
++ .probe = at803x_probe,
++ .flags = PHY_IS_INTERNAL,
++ .config_init = qca83xx_config_init,
++ .soft_reset = genphy_soft_reset,
++ .get_sset_count = at803x_get_sset_count,
++ .get_strings = at803x_get_strings,
++ .get_stats = at803x_get_stats,
++}, {
++ /* QCA8327-B from switch QCA8327-BL1A */
++ .phy_id = QCA8327_B_PHY_ID,
++ .phy_id_mask = QCA8K_PHY_ID_MASK,
++ .name = "QCA PHY 8327-B",
+ /* PHY_GBIT_FEATURES */
+ .probe = at803x_probe,
+ .flags = PHY_IS_INTERNAL,
+@@ -1436,7 +1450,8 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) },
+ { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
+ { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
+- { PHY_ID_MATCH_EXACT(QCA8327_PHY_ID) },
++ { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
++ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
+ { }
+ };
+
diff --git a/target/linux/generic/backport-6.1/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch b/target/linux/generic/backport-6.1/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch
new file mode 100644
index 0000000000..cd83fac83c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch
@@ -0,0 +1,45 @@
+From 15b9df4ece17d084f14eb0ca1cf05f2ad497e425 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 19 Sep 2021 18:28:16 +0200
+Subject: net: phy: at803x: add resume/suspend function to qca83xx phy
+
+Add resume/suspend function to qca83xx internal phy.
+We can't use the at803x generic function as the documentation lacks of
+any support for WoL regs.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1413,6 +1413,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
+ }, {
+ /* QCA8327-A from switch QCA8327-AL1A */
+ .phy_id = QCA8327_A_PHY_ID,
+@@ -1426,6 +1428,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
+ }, {
+ /* QCA8327-B from switch QCA8327-BL1A */
+ .phy_id = QCA8327_B_PHY_ID,
+@@ -1439,6 +1443,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
+ }, };
+
+ module_phy_driver(at803x_driver);
diff --git a/target/linux/generic/backport-6.1/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch b/target/linux/generic/backport-6.1/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch
new file mode 100644
index 0000000000..586d8953b2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch
@@ -0,0 +1,95 @@
+From d44fd8604a4ab92119adb35f05fd87612af722b5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 19 Sep 2021 18:28:17 +0200
+Subject: net: phy: at803x: fix spacing and improve name for 83xx phy
+
+Fix spacing and improve name for 83xx phy following other phy in the
+same driver.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 60 ++++++++++++++++++++++++------------------------
+ 1 file changed, 30 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1402,47 +1402,47 @@ static struct phy_driver at803x_driver[]
+ .config_aneg = at803x_config_aneg,
+ }, {
+ /* QCA8337 */
+- .phy_id = QCA8337_PHY_ID,
+- .phy_id_mask = QCA8K_PHY_ID_MASK,
+- .name = "QCA PHY 8337",
++ .phy_id = QCA8337_PHY_ID,
++ .phy_id_mask = QCA8K_PHY_ID_MASK,
++ .name = "Qualcomm Atheros 8337 internal PHY",
+ /* PHY_GBIT_FEATURES */
+- .probe = at803x_probe,
+- .flags = PHY_IS_INTERNAL,
+- .config_init = qca83xx_config_init,
+- .soft_reset = genphy_soft_reset,
+- .get_sset_count = at803x_get_sset_count,
+- .get_strings = at803x_get_strings,
+- .get_stats = at803x_get_stats,
++ .probe = at803x_probe,
++ .flags = PHY_IS_INTERNAL,
++ .config_init = qca83xx_config_init,
++ .soft_reset = genphy_soft_reset,
++ .get_sset_count = at803x_get_sset_count,
++ .get_strings = at803x_get_strings,
++ .get_stats = at803x_get_stats,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ /* QCA8327-A from switch QCA8327-AL1A */
+- .phy_id = QCA8327_A_PHY_ID,
+- .phy_id_mask = QCA8K_PHY_ID_MASK,
+- .name = "QCA PHY 8327-A",
++ .phy_id = QCA8327_A_PHY_ID,
++ .phy_id_mask = QCA8K_PHY_ID_MASK,
++ .name = "Qualcomm Atheros 8327-A internal PHY",
+ /* PHY_GBIT_FEATURES */
+- .probe = at803x_probe,
+- .flags = PHY_IS_INTERNAL,
+- .config_init = qca83xx_config_init,
+- .soft_reset = genphy_soft_reset,
+- .get_sset_count = at803x_get_sset_count,
+- .get_strings = at803x_get_strings,
+- .get_stats = at803x_get_stats,
++ .probe = at803x_probe,
++ .flags = PHY_IS_INTERNAL,
++ .config_init = qca83xx_config_init,
++ .soft_reset = genphy_soft_reset,
++ .get_sset_count = at803x_get_sset_count,
++ .get_strings = at803x_get_strings,
++ .get_stats = at803x_get_stats,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ /* QCA8327-B from switch QCA8327-BL1A */
+- .phy_id = QCA8327_B_PHY_ID,
+- .phy_id_mask = QCA8K_PHY_ID_MASK,
+- .name = "QCA PHY 8327-B",
++ .phy_id = QCA8327_B_PHY_ID,
++ .phy_id_mask = QCA8K_PHY_ID_MASK,
++ .name = "Qualcomm Atheros 8327-B internal PHY",
+ /* PHY_GBIT_FEATURES */
+- .probe = at803x_probe,
+- .flags = PHY_IS_INTERNAL,
+- .config_init = qca83xx_config_init,
+- .soft_reset = genphy_soft_reset,
+- .get_sset_count = at803x_get_sset_count,
+- .get_strings = at803x_get_strings,
+- .get_stats = at803x_get_stats,
++ .probe = at803x_probe,
++ .flags = PHY_IS_INTERNAL,
++ .config_init = qca83xx_config_init,
++ .soft_reset = genphy_soft_reset,
++ .get_sset_count = at803x_get_sset_count,
++ .get_strings = at803x_get_strings,
++ .get_stats = at803x_get_stats,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, };
diff --git a/target/linux/generic/backport-6.1/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch b/target/linux/generic/backport-6.1/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch
new file mode 100644
index 0000000000..09797ae83b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch
@@ -0,0 +1,131 @@
+From ba3c01ee02ed0d821c9f241f179bbc9457542b8f Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 10 Oct 2021 00:46:15 +0200
+Subject: net: phy: at803x: fix resume for QCA8327 phy
+
+From Documentation phy resume triggers phy reset and restart
+auto-negotiation. Add a dedicated function to wait reset to finish as
+it was notice a regression where port sometime are not reliable after a
+suspend/resume session. The reset wait logic is copied from phy_poll_reset.
+Add dedicated suspend function to use genphy_suspend only with QCA8337
+phy and set only additional debug settings for QCA8327. With more test
+it was reported that QCA8327 doesn't proprely support this mode and
+using this cause the unreliability of the switch ports, especially the
+malfunction of the port0.
+
+Fixes: 15b9df4ece17 ("net: phy: at803x: add resume/suspend function to qca83xx phy")
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 69 +++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 63 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -92,9 +92,14 @@
+ #define AT803X_DEBUG_REG_5 0x05
+ #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
+
++#define AT803X_DEBUG_REG_HIB_CTRL 0x0b
++#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10)
++#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13)
++
+ #define AT803X_DEBUG_REG_3C 0x3C
+
+ #define AT803X_DEBUG_REG_3D 0x3D
++#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
+
+ #define AT803X_DEBUG_REG_1F 0x1F
+ #define AT803X_DEBUG_PLL_ON BIT(2)
+@@ -1304,6 +1309,58 @@ static int qca83xx_config_init(struct ph
+ return 0;
+ }
+
++static int qca83xx_resume(struct phy_device *phydev)
++{
++ int ret, val;
++
++ /* Skip reset if not suspended */
++ if (!phydev->suspended)
++ return 0;
++
++ /* Reinit the port, reset values set by suspend */
++ qca83xx_config_init(phydev);
++
++ /* Reset the port on port resume */
++ phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
++
++ /* On resume from suspend the switch execute a reset and
++ * restart auto-negotiation. Wait for reset to complete.
++ */
++ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
++ 50000, 600000, true);
++ if (ret)
++ return ret;
++
++ msleep(1);
++
++ return 0;
++}
++
++static int qca83xx_suspend(struct phy_device *phydev)
++{
++ u16 mask = 0;
++
++ /* Only QCA8337 support actual suspend.
++ * QCA8327 cause port unreliability when phy suspend
++ * is set.
++ */
++ if (phydev->drv->phy_id == QCA8337_PHY_ID) {
++ genphy_suspend(phydev);
++ } else {
++ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
++ phy_modify(phydev, MII_BMCR, mask, 0);
++ }
++
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_3D,
++ AT803X_DEBUG_GATE_CLK_IN1000, 0);
++
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
++ AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
++ AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
++
++ return 0;
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+ /* Qualcomm Atheros AR8035 */
+@@ -1413,8 +1470,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
+- .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .suspend = qca83xx_suspend,
++ .resume = qca83xx_resume,
+ }, {
+ /* QCA8327-A from switch QCA8327-AL1A */
+ .phy_id = QCA8327_A_PHY_ID,
+@@ -1428,8 +1485,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
+- .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .suspend = qca83xx_suspend,
++ .resume = qca83xx_resume,
+ }, {
+ /* QCA8327-B from switch QCA8327-BL1A */
+ .phy_id = QCA8327_B_PHY_ID,
+@@ -1443,8 +1500,8 @@ static struct phy_driver at803x_driver[]
+ .get_sset_count = at803x_get_sset_count,
+ .get_strings = at803x_get_strings,
+ .get_stats = at803x_get_stats,
+- .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .suspend = qca83xx_suspend,
++ .resume = qca83xx_resume,
+ }, };
+
+ module_phy_driver(at803x_driver);
diff --git a/target/linux/generic/backport-6.1/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch b/target/linux/generic/backport-6.1/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch
new file mode 100644
index 0000000000..c504c37c84
--- /dev/null
+++ b/target/linux/generic/backport-6.1/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch
@@ -0,0 +1,91 @@
+From 1ca8311949aec5c9447645731ef1c6bc5bd71350 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 10 Oct 2021 00:46:16 +0200
+Subject: net: phy: at803x: add DAC amplitude fix for 8327 phy
+
+QCA8327 internal phy require DAC amplitude adjustement set to +6% with
+100m speed. Also add additional define to report a change of the same
+reg in QCA8337. (different scope it does set 1000m voltage)
+Add link_change_notify function to set the proper amplitude adjustement
+on PHY_RUNNING state and disable on any other state.
+
+Fixes: b4df02b562f4 ("net: phy: at803x: add support for qca 8327 A variant internal phy")
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 33 +++++++++++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -87,6 +87,8 @@
+ #define AT803X_PSSR_MR_AN_COMPLETE 0x0200
+
+ #define AT803X_DEBUG_REG_0 0x00
++#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2)
++#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2)
+ #define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
+
+ #define AT803X_DEBUG_REG_5 0x05
+@@ -1306,9 +1308,37 @@ static int qca83xx_config_init(struct ph
+ break;
+ }
+
++ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
++ * Disable on init and enable only with 100m speed following
++ * qca original source code.
++ */
++ if (phydev->drv->phy_id == QCA8327_A_PHY_ID ||
++ phydev->drv->phy_id == QCA8327_B_PHY_ID)
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
++ QCA8327_DEBUG_MANU_CTRL_EN, 0);
++
+ return 0;
+ }
+
++static void qca83xx_link_change_notify(struct phy_device *phydev)
++{
++ /* QCA8337 doesn't require DAC Amplitude adjustement */
++ if (phydev->drv->phy_id == QCA8337_PHY_ID)
++ return;
++
++ /* Set DAC Amplitude adjustment to +6% for 100m on link running */
++ if (phydev->state == PHY_RUNNING) {
++ if (phydev->speed == SPEED_100)
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
++ QCA8327_DEBUG_MANU_CTRL_EN,
++ QCA8327_DEBUG_MANU_CTRL_EN);
++ } else {
++ /* Reset DAC Amplitude adjustment */
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
++ QCA8327_DEBUG_MANU_CTRL_EN, 0);
++ }
++}
++
+ static int qca83xx_resume(struct phy_device *phydev)
+ {
+ int ret, val;
+@@ -1463,6 +1493,7 @@ static struct phy_driver at803x_driver[]
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
+ .name = "Qualcomm Atheros 8337 internal PHY",
+ /* PHY_GBIT_FEATURES */
++ .link_change_notify = qca83xx_link_change_notify,
+ .probe = at803x_probe,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = qca83xx_config_init,
+@@ -1478,6 +1509,7 @@ static struct phy_driver at803x_driver[]
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
+ .name = "Qualcomm Atheros 8327-A internal PHY",
+ /* PHY_GBIT_FEATURES */
++ .link_change_notify = qca83xx_link_change_notify,
+ .probe = at803x_probe,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = qca83xx_config_init,
+@@ -1493,6 +1525,7 @@ static struct phy_driver at803x_driver[]
+ .phy_id_mask = QCA8K_PHY_ID_MASK,
+ .name = "Qualcomm Atheros 8327-B internal PHY",
+ /* PHY_GBIT_FEATURES */
++ .link_change_notify = qca83xx_link_change_notify,
+ .probe = at803x_probe,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = qca83xx_config_init,
diff --git a/target/linux/generic/backport-6.1/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch b/target/linux/generic/backport-6.1/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch
new file mode 100644
index 0000000000..9f880593f1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch
@@ -0,0 +1,27 @@
+From 9d1c29b4028557a496be9c5eb2b4b86063700636 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 10 Oct 2021 00:46:17 +0200
+Subject: net: phy: at803x: enable prefer master for 83xx internal phy
+
+From original QCA source code the port was set to prefer master as port
+type in 1000BASE-T mode. Apply the same settings also here.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1317,6 +1317,9 @@ static int qca83xx_config_init(struct ph
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
+
++ /* Following original QCA sourcecode set port to prefer master */
++ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
++
+ return 0;
+ }
+
diff --git a/target/linux/generic/backport-6.1/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch b/target/linux/generic/backport-6.1/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch
new file mode 100644
index 0000000000..89e9b3f662
--- /dev/null
+++ b/target/linux/generic/backport-6.1/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch
@@ -0,0 +1,127 @@
+From 67999555ff42e91de7654488d9a7735bd9e84555 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 10 Oct 2021 00:46:18 +0200
+Subject: net: phy: at803x: better describe debug regs
+
+Give a name to known debug regs from Documentation instead of using
+unknown hex values.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 30 +++++++++++++++---------------
+ 1 file changed, 15 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -86,12 +86,12 @@
+ #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
+ #define AT803X_PSSR_MR_AN_COMPLETE 0x0200
+
+-#define AT803X_DEBUG_REG_0 0x00
++#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00
+ #define QCA8327_DEBUG_MANU_CTRL_EN BIT(2)
+ #define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2)
+ #define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
+
+-#define AT803X_DEBUG_REG_5 0x05
++#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05
+ #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
+
+ #define AT803X_DEBUG_REG_HIB_CTRL 0x0b
+@@ -100,7 +100,7 @@
+
+ #define AT803X_DEBUG_REG_3C 0x3C
+
+-#define AT803X_DEBUG_REG_3D 0x3D
++#define AT803X_DEBUG_REG_GREEN 0x3D
+ #define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6)
+
+ #define AT803X_DEBUG_REG_1F 0x1F
+@@ -284,25 +284,25 @@ static int at803x_read_page(struct phy_d
+
+ static int at803x_enable_rx_delay(struct phy_device *phydev)
+ {
+- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0,
++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0,
+ AT803X_DEBUG_RX_CLK_DLY_EN);
+ }
+
+ static int at803x_enable_tx_delay(struct phy_device *phydev)
+ {
+- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0,
++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0,
+ AT803X_DEBUG_TX_CLK_DLY_EN);
+ }
+
+ static int at803x_disable_rx_delay(struct phy_device *phydev)
+ {
+- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
+ AT803X_DEBUG_RX_CLK_DLY_EN, 0);
+ }
+
+ static int at803x_disable_tx_delay(struct phy_device *phydev)
+ {
+- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5,
++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE,
+ AT803X_DEBUG_TX_CLK_DLY_EN, 0);
+ }
+
+@@ -1292,9 +1292,9 @@ static int qca83xx_config_init(struct ph
+ switch (switch_revision) {
+ case 1:
+ /* For 100M waveform */
+- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_0, 0x02ea);
++ at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
+ /* Turn on Gigabit clock */
+- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x68a0);
++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
+ break;
+
+ case 2:
+@@ -1302,8 +1302,8 @@ static int qca83xx_config_init(struct ph
+ fallthrough;
+ case 4:
+ phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
+- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x6860);
+- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_5, 0x2c46);
++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860);
++ at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46);
+ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
+ break;
+ }
+@@ -1314,7 +1314,7 @@ static int qca83xx_config_init(struct ph
+ */
+ if (phydev->drv->phy_id == QCA8327_A_PHY_ID ||
+ phydev->drv->phy_id == QCA8327_B_PHY_ID)
+- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
+
+ /* Following original QCA sourcecode set port to prefer master */
+@@ -1332,12 +1332,12 @@ static void qca83xx_link_change_notify(s
+ /* Set DAC Amplitude adjustment to +6% for 100m on link running */
+ if (phydev->state == PHY_RUNNING) {
+ if (phydev->speed == SPEED_100)
+- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
+ QCA8327_DEBUG_MANU_CTRL_EN,
+ QCA8327_DEBUG_MANU_CTRL_EN);
+ } else {
+ /* Reset DAC Amplitude adjustment */
+- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
+ }
+ }
+@@ -1384,7 +1384,7 @@ static int qca83xx_suspend(struct phy_de
+ phy_modify(phydev, MII_BMCR, mask, 0);
+ }
+
+- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_3D,
++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
+ AT803X_DEBUG_GATE_CLK_IN1000, 0);
+
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
diff --git a/target/linux/generic/backport-6.1/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch b/target/linux/generic/backport-6.1/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch
new file mode 100644
index 0000000000..c8d424de38
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch
@@ -0,0 +1,80 @@
+From d8b6f5bae6d3b648a67b6958cb98e4e97256d652 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:06 +0200
+Subject: dsa: qca8k: add mac_power_sel support
+
+Add missing mac power sel support needed for ipq8064/5 SoC that require
+1.8v for the internal regulator port instead of the default 1.5v.
+If other device needs this, consider adding a dedicated binding to
+support this.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 31 +++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 5 +++++
+ 2 files changed, 36 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -951,6 +951,33 @@ qca8k_setup_of_rgmii_delay(struct qca8k_
+ }
+
+ static int
++qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
++{
++ u32 mask = 0;
++ int ret = 0;
++
++ /* SoC specific settings for ipq8064.
++ * If more device require this consider adding
++ * a dedicated binding.
++ */
++ if (of_machine_is_compatible("qcom,ipq8064"))
++ mask |= QCA8K_MAC_PWR_RGMII0_1_8V;
++
++ /* SoC specific settings for ipq8065 */
++ if (of_machine_is_compatible("qcom,ipq8065"))
++ mask |= QCA8K_MAC_PWR_RGMII1_1_8V;
++
++ if (mask) {
++ ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL,
++ QCA8K_MAC_PWR_RGMII0_1_8V |
++ QCA8K_MAC_PWR_RGMII1_1_8V,
++ mask);
++ }
++
++ return ret;
++}
++
++static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+@@ -979,6 +1006,10 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
++ ret = qca8k_setup_mac_pwr_sel(priv);
++ if (ret)
++ return ret;
++
+ /* Enable CPU Port */
+ ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -100,6 +100,11 @@
+ #define QCA8K_SGMII_MODE_CTRL_PHY (1 << 22)
+ #define QCA8K_SGMII_MODE_CTRL_MAC (2 << 22)
+
++/* MAC_PWR_SEL registers */
++#define QCA8K_REG_MAC_PWR_SEL 0x0e4
++#define QCA8K_MAC_PWR_RGMII1_1_8V BIT(18)
++#define QCA8K_MAC_PWR_RGMII0_1_8V BIT(19)
++
+ /* EEE control registers */
+ #define QCA8K_REG_EEE_CTRL 0x100
+ #define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2)
diff --git a/target/linux/generic/backport-6.1/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch b/target/linux/generic/backport-6.1/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch
new file mode 100644
index 0000000000..bd768ec27d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch
@@ -0,0 +1,30 @@
+From fdbf35df9c091db9c46e57e9938e3f7a4f603a7c Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:07 +0200
+Subject: dt-bindings: net: dsa: qca8k: Add SGMII clock phase properties
+
+Add names and descriptions of additional PORT0_PAD_CTRL properties.
+qca,sgmii-(rx|tx)clk-falling-edge are for setting the respective clock
+phase to failling edge.
+
+Co-developed-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -37,6 +37,10 @@ A CPU port node has the following option
+ managed entity. See
+ Documentation/devicetree/bindings/net/fixed-link.txt
+ for details.
++- qca,sgmii-rxclk-falling-edge: Set the receive clock phase to falling edge.
++ Mostly used in qca8327 with CPU port 0 set to
++ sgmii.
++- qca,sgmii-txclk-falling-edge: Set the transmit clock phase to falling edge.
+
+ For QCA8K the 'fixed-link' sub-node supports only the following properties:
+
diff --git a/target/linux/generic/backport-6.1/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch b/target/linux/generic/backport-6.1/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch
new file mode 100644
index 0000000000..e464452d82
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch
@@ -0,0 +1,127 @@
+From 6c43809bf1bee76c434e365a26546a92a5fbec14 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:08 +0200
+Subject: net: dsa: qca8k: add support for sgmii falling edge
+
+Add support for this in the qca8k driver. Also add support for SGMII
+rx/tx clock falling edge. This is only present for pad0, pad5 and
+pad6 have these bit reserved from Documentation. Add a comment that this
+is hardcoded to PAD0 as qca8327/28/34/37 have an unique sgmii line and
+setting falling in port0 applies to both configuration with sgmii used
+for port0 or port6.
+
+Co-developed-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 4 ++++
+ 2 files changed, 67 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -978,6 +978,42 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
+ }
+
+ static int
++qca8k_parse_port_config(struct qca8k_priv *priv)
++{
++ struct device_node *port_dn;
++ phy_interface_t mode;
++ struct dsa_port *dp;
++ int port, ret;
++
++ /* We have 2 CPU port. Check them */
++ for (port = 0; port < QCA8K_NUM_PORTS; port++) {
++ /* Skip every other port */
++ if (port != 0 && port != 6)
++ continue;
++
++ dp = dsa_to_port(priv->ds, port);
++ port_dn = dp->dn;
++
++ if (!of_device_is_available(port_dn))
++ continue;
++
++ ret = of_get_phy_mode(port_dn, &mode);
++ if (ret)
++ continue;
++
++ if (mode == PHY_INTERFACE_MODE_SGMII) {
++ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
++ priv->sgmii_tx_clk_falling_edge = true;
++
++ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
++ priv->sgmii_rx_clk_falling_edge = true;
++ }
++ }
++
++ return 0;
++}
++
++static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+@@ -990,6 +1026,11 @@ qca8k_setup(struct dsa_switch *ds)
+ return -EINVAL;
+ }
+
++ /* Parse CPU port config to be later used in phy_link mac_config */
++ ret = qca8k_parse_port_config(priv);
++ if (ret)
++ return ret;
++
+ mutex_init(&priv->reg_mutex);
+
+ /* Start by setting up the register mapping */
+@@ -1274,6 +1315,28 @@ qca8k_phylink_mac_config(struct dsa_swit
+ }
+
+ qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
++
++ /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
++ * falling edge is set writing in the PORT0 PAD reg
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8327 ||
++ priv->switch_id == QCA8K_ID_QCA8337)
++ reg = QCA8K_REG_PORT0_PAD_CTRL;
++
++ val = 0;
++
++ /* SGMII Clock phase configuration */
++ if (priv->sgmii_rx_clk_falling_edge)
++ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE;
++
++ if (priv->sgmii_tx_clk_falling_edge)
++ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
++
++ if (val)
++ ret = qca8k_rmw(priv, reg,
++ QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
++ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
++ val);
+ break;
+ default:
+ dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -35,6 +35,8 @@
+ #define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8)
+ #define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8)
+ #define QCA8K_REG_PORT0_PAD_CTRL 0x004
++#define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19)
++#define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18)
+ #define QCA8K_REG_PORT5_PAD_CTRL 0x008
+ #define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+ #define QCA8K_PORT_PAD_RGMII_EN BIT(26)
+@@ -260,6 +262,8 @@ struct qca8k_priv {
+ u8 switch_revision;
+ u8 rgmii_tx_delay;
+ u8 rgmii_rx_delay;
++ bool sgmii_rx_clk_falling_edge;
++ bool sgmii_tx_clk_falling_edge;
+ bool legacy_phy_port_mapping;
+ struct regmap *regmap;
+ struct mii_bus *bus;
diff --git a/target/linux/generic/backport-6.1/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch b/target/linux/generic/backport-6.1/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch
new file mode 100644
index 0000000000..606ac0af3d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch
@@ -0,0 +1,29 @@
+From 731d613338ec6de482053ffa3f71be2325b0f8eb Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:09 +0200
+Subject: dt-bindings: net: dsa: qca8k: Document support for CPU port 6
+
+The switch now support CPU port to be set 6 instead of be hardcoded to
+0. Document support for it and describe logic selection.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -29,7 +29,11 @@ the mdio MASTER is used as communication
+ Don't use mixed external and internal mdio-bus configurations, as this is
+ not supported by the hardware.
+
+-The CPU port of this switch is always port 0.
++This switch support 2 CPU port. Normally and advised configuration is with
++CPU port set to port 0. It is also possible to set the CPU port to port 6
++if the device requires it. The driver will configure the switch to the defined
++port. With both CPU port declared the first CPU port is selected as primary
++and the secondary CPU ignored.
+
+ A CPU port node has the following optional node:
+
diff --git a/target/linux/generic/backport-6.1/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch b/target/linux/generic/backport-6.1/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch
new file mode 100644
index 0000000000..320db8fa9f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch
@@ -0,0 +1,153 @@
+From 3fcf734aa482487df83cf8f18608438fcf59127f Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:10 +0200
+Subject: net: dsa: qca8k: add support for cpu port 6
+
+Currently CPU port is always hardcoded to port 0. This switch have 2 CPU
+ports. The original intention of this driver seems to be use the
+mac06_exchange bit to swap MAC0 with MAC6 in the strange configuration
+where device have connected only the CPU port 6. To skip the
+introduction of a new binding, rework the driver to address the
+secondary CPU port as primary and drop any reference of hardcoded port.
+With configuration of mac06 exchange, just skip the definition of port0
+and define the CPU port as a secondary. The driver will autoconfigure
+the switch to use that as the primary CPU port.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 51 ++++++++++++++++++++++++++++++++++---------------
+ drivers/net/dsa/qca8k.h | 2 --
+ 2 files changed, 36 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -977,6 +977,22 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
+ return ret;
+ }
+
++static int qca8k_find_cpu_port(struct dsa_switch *ds)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Find the connected cpu port. Valid port are 0 or 6 */
++ if (dsa_is_cpu_port(ds, 0))
++ return 0;
++
++ dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
++
++ if (dsa_is_cpu_port(ds, 6))
++ return 6;
++
++ return -EINVAL;
++}
++
+ static int
+ qca8k_parse_port_config(struct qca8k_priv *priv)
+ {
+@@ -1017,13 +1033,13 @@ static int
+ qca8k_setup(struct dsa_switch *ds)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int ret, i;
++ int cpu_port, ret, i;
+ u32 mask;
+
+- /* Make sure that port 0 is the cpu port */
+- if (!dsa_is_cpu_port(ds, 0)) {
+- dev_err(priv->dev, "port 0 is not the CPU port");
+- return -EINVAL;
++ cpu_port = qca8k_find_cpu_port(ds);
++ if (cpu_port < 0) {
++ dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
++ return cpu_port;
+ }
+
+ /* Parse CPU port config to be later used in phy_link mac_config */
+@@ -1065,7 +1081,7 @@ qca8k_setup(struct dsa_switch *ds)
+ dev_warn(priv->dev, "mib init failed");
+
+ /* Enable QCA header mode on the cpu port */
+- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT),
++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(cpu_port),
+ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
+ if (ret) {
+@@ -1087,10 +1103,10 @@ qca8k_setup(struct dsa_switch *ds)
+
+ /* Forward all unknown frames to CPU port for Linux processing */
+ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
+- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
+ if (ret)
+ return ret;
+
+@@ -1098,7 +1114,7 @@ qca8k_setup(struct dsa_switch *ds)
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+ /* CPU port gets connected to all user ports of the switch */
+ if (dsa_is_cpu_port(ds, i)) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT),
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
+ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
+ if (ret)
+ return ret;
+@@ -1110,7 +1126,7 @@ qca8k_setup(struct dsa_switch *ds)
+
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+ QCA8K_PORT_LOOKUP_MEMBER,
+- BIT(QCA8K_CPU_PORT));
++ BIT(cpu_port));
+ if (ret)
+ return ret;
+
+@@ -1616,9 +1632,12 @@ static int
+ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int port_mask = BIT(QCA8K_CPU_PORT);
++ int port_mask, cpu_port;
+ int i, ret;
+
++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
++ port_mask = BIT(cpu_port);
++
+ for (i = 1; i < QCA8K_NUM_PORTS; i++) {
+ if (dsa_to_port(ds, i)->bridge_dev != br)
+ continue;
+@@ -1645,7 +1664,9 @@ static void
+ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int i;
++ int cpu_port, i;
++
++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+
+ for (i = 1; i < QCA8K_NUM_PORTS; i++) {
+ if (dsa_to_port(ds, i)->bridge_dev != br)
+@@ -1662,7 +1683,7 @@ qca8k_port_bridge_leave(struct dsa_switc
+ * this port
+ */
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_MEMBER, BIT(QCA8K_CPU_PORT));
++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+ }
+
+ static int
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -24,8 +24,6 @@
+
+ #define QCA8K_NUM_FDB_RECORDS 2048
+
+-#define QCA8K_CPU_PORT 0
+-
+ #define QCA8K_PORT_VID_DEF 1
+
+ /* Global control registers */
diff --git a/target/linux/generic/backport-6.1/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch b/target/linux/generic/backport-6.1/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch
new file mode 100644
index 0000000000..de201764f9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch
@@ -0,0 +1,295 @@
+From 5654ec78dd7e64b1e04777b24007344329e6a63b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:11 +0200
+Subject: net: dsa: qca8k: rework rgmii delay logic and scan for cpu port 6
+
+Future proof commit. This switch have 2 CPU ports and one valid
+configuration is first CPU port set to sgmii and second CPU port set to
+rgmii-id. The current implementation detects delay only for CPU port
+zero set to rgmii and doesn't count any delay set in a secondary CPU
+port. Drop the current delay scan function and move it to the sgmii
+parser function to generalize and implicitly add support for secondary
+CPU port set to rgmii-id. Introduce new logic where delay is enabled
+also with internal delay binding declared and rgmii set as PHY mode.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 165 ++++++++++++++++++++++++------------------------
+ drivers/net/dsa/qca8k.h | 10 ++-
+ 2 files changed, 89 insertions(+), 86 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -889,68 +889,6 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ }
+
+ static int
+-qca8k_setup_of_rgmii_delay(struct qca8k_priv *priv)
+-{
+- struct device_node *port_dn;
+- phy_interface_t mode;
+- struct dsa_port *dp;
+- u32 val;
+-
+- /* CPU port is already checked */
+- dp = dsa_to_port(priv->ds, 0);
+-
+- port_dn = dp->dn;
+-
+- /* Check if port 0 is set to the correct type */
+- of_get_phy_mode(port_dn, &mode);
+- if (mode != PHY_INTERFACE_MODE_RGMII_ID &&
+- mode != PHY_INTERFACE_MODE_RGMII_RXID &&
+- mode != PHY_INTERFACE_MODE_RGMII_TXID) {
+- return 0;
+- }
+-
+- switch (mode) {
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- if (of_property_read_u32(port_dn, "rx-internal-delay-ps", &val))
+- val = 2;
+- else
+- /* Switch regs accept value in ns, convert ps to ns */
+- val = val / 1000;
+-
+- if (val > QCA8K_MAX_DELAY) {
+- dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
+- val = 3;
+- }
+-
+- priv->rgmii_rx_delay = val;
+- /* Stop here if we need to check only for rx delay */
+- if (mode != PHY_INTERFACE_MODE_RGMII_ID)
+- break;
+-
+- fallthrough;
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- if (of_property_read_u32(port_dn, "tx-internal-delay-ps", &val))
+- val = 1;
+- else
+- /* Switch regs accept value in ns, convert ps to ns */
+- val = val / 1000;
+-
+- if (val > QCA8K_MAX_DELAY) {
+- dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
+- val = 3;
+- }
+-
+- priv->rgmii_tx_delay = val;
+- break;
+- default:
+- return 0;
+- }
+-
+- return 0;
+-}
+-
+-static int
+ qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
+ {
+ u32 mask = 0;
+@@ -996,19 +934,21 @@ static int qca8k_find_cpu_port(struct ds
+ static int
+ qca8k_parse_port_config(struct qca8k_priv *priv)
+ {
++ int port, cpu_port_index = 0, ret;
+ struct device_node *port_dn;
+ phy_interface_t mode;
+ struct dsa_port *dp;
+- int port, ret;
++ u32 delay;
+
+ /* We have 2 CPU port. Check them */
+- for (port = 0; port < QCA8K_NUM_PORTS; port++) {
++ for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) {
+ /* Skip every other port */
+ if (port != 0 && port != 6)
+ continue;
+
+ dp = dsa_to_port(priv->ds, port);
+ port_dn = dp->dn;
++ cpu_port_index++;
+
+ if (!of_device_is_available(port_dn))
+ continue;
+@@ -1017,12 +957,54 @@ qca8k_parse_port_config(struct qca8k_pri
+ if (ret)
+ continue;
+
+- if (mode == PHY_INTERFACE_MODE_SGMII) {
++ switch (mode) {
++ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ delay = 0;
++
++ if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay))
++ /* Switch regs accept value in ns, convert ps to ns */
++ delay = delay / 1000;
++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_TXID)
++ delay = 1;
++
++ if (delay > QCA8K_MAX_DELAY) {
++ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
++ delay = 3;
++ }
++
++ priv->rgmii_tx_delay[cpu_port_index] = delay;
++
++ delay = 0;
++
++ if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay))
++ /* Switch regs accept value in ns, convert ps to ns */
++ delay = delay / 1000;
++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_RXID)
++ delay = 2;
++
++ if (delay > QCA8K_MAX_DELAY) {
++ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
++ delay = 3;
++ }
++
++ priv->rgmii_rx_delay[cpu_port_index] = delay;
++
++ break;
++ case PHY_INTERFACE_MODE_SGMII:
+ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
+ priv->sgmii_tx_clk_falling_edge = true;
+
+ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
+ priv->sgmii_rx_clk_falling_edge = true;
++
++ break;
++ default:
++ continue;
+ }
+ }
+
+@@ -1059,10 +1041,6 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
+- ret = qca8k_setup_of_rgmii_delay(priv);
+- if (ret)
+- return ret;
+-
+ ret = qca8k_setup_mac_pwr_sel(priv);
+ if (ret)
+ return ret;
+@@ -1229,8 +1207,8 @@ qca8k_phylink_mac_config(struct dsa_swit
+ const struct phylink_link_state *state)
+ {
+ struct qca8k_priv *priv = ds->priv;
+- u32 reg, val;
+- int ret;
++ int cpu_port_index, ret;
++ u32 reg, val, delay;
+
+ switch (port) {
+ case 0: /* 1st CPU port */
+@@ -1242,6 +1220,7 @@ qca8k_phylink_mac_config(struct dsa_swit
+ return;
+
+ reg = QCA8K_REG_PORT0_PAD_CTRL;
++ cpu_port_index = QCA8K_CPU_PORT0;
+ break;
+ case 1:
+ case 2:
+@@ -1260,6 +1239,7 @@ qca8k_phylink_mac_config(struct dsa_swit
+ return;
+
+ reg = QCA8K_REG_PORT6_PAD_CTRL;
++ cpu_port_index = QCA8K_CPU_PORT6;
+ break;
+ default:
+ dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
+@@ -1274,23 +1254,40 @@ qca8k_phylink_mac_config(struct dsa_swit
+
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+- /* RGMII mode means no delay so don't enable the delay */
+- qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
+- break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+- /* RGMII_ID needs internal delay. This is enabled through
+- * PORT5_PAD_CTRL for all ports, rather than individual port
+- * registers
++ val = QCA8K_PORT_PAD_RGMII_EN;
++
++ /* Delay can be declared in 3 different way.
++ * Mode to rgmii and internal-delay standard binding defined
++ * rgmii-id or rgmii-tx/rx phy mode set.
++ * The parse logic set a delay different than 0 only when one
++ * of the 3 different way is used. In all other case delay is
++ * not enabled. With ID or TX/RXID delay is enabled and set
++ * to the default and recommended value.
++ */
++ if (priv->rgmii_tx_delay[cpu_port_index]) {
++ delay = priv->rgmii_tx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
++ }
++
++ if (priv->rgmii_rx_delay[cpu_port_index]) {
++ delay = priv->rgmii_rx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
++ }
++
++ /* Set RGMII delay based on the selected values */
++ qca8k_write(priv, reg, val);
++
++ /* QCA8337 requires to set rgmii rx delay for all ports.
++ * This is enabled through PORT5_PAD_CTRL for all ports,
++ * rather than individual port registers.
+ */
+- qca8k_write(priv, reg,
+- QCA8K_PORT_PAD_RGMII_EN |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY(priv->rgmii_tx_delay) |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY(priv->rgmii_rx_delay) |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+- /* QCA8337 requires to set rgmii rx delay */
+ if (priv->switch_id == QCA8K_ID_QCA8337)
+ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -13,6 +13,7 @@
+ #include <linux/gpio.h>
+
+ #define QCA8K_NUM_PORTS 7
++#define QCA8K_NUM_CPU_PORTS 2
+ #define QCA8K_MAX_MTU 9000
+
+ #define PHY_ID_QCA8327 0x004dd034
+@@ -255,13 +256,18 @@ struct qca8k_match_data {
+ u8 id;
+ };
+
++enum {
++ QCA8K_CPU_PORT0,
++ QCA8K_CPU_PORT6,
++};
++
+ struct qca8k_priv {
+ u8 switch_id;
+ u8 switch_revision;
+- u8 rgmii_tx_delay;
+- u8 rgmii_rx_delay;
+ bool sgmii_rx_clk_falling_edge;
+ bool sgmii_tx_clk_falling_edge;
++ u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
++ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+ bool legacy_phy_port_mapping;
+ struct regmap *regmap;
+ struct mii_bus *bus;
diff --git a/target/linux/generic/backport-6.1/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch b/target/linux/generic/backport-6.1/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch
new file mode 100644
index 0000000000..8abd264e79
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch
@@ -0,0 +1,33 @@
+From 13ad5ccc093ff448b99ac7e138e91e78796adb48 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:12 +0200
+Subject: dt-bindings: net: dsa: qca8k: Document qca,sgmii-enable-pll
+
+Document qca,sgmii-enable-pll binding used in the CPU nodes to
+enable SGMII PLL on MAC config.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -45,6 +45,16 @@ A CPU port node has the following option
+ Mostly used in qca8327 with CPU port 0 set to
+ sgmii.
+ - qca,sgmii-txclk-falling-edge: Set the transmit clock phase to falling edge.
++- qca,sgmii-enable-pll : For SGMII CPU port, explicitly enable PLL, TX and RX
++ chain along with Signal Detection.
++ This should NOT be enabled for qca8327. If enabled with
++ qca8327 the sgmii port won't correctly init and an err
++ is printed.
++ This can be required for qca8337 switch with revision 2.
++ A warning is displayed when used with revision greater
++ 2.
++ With CPU port set to sgmii and qca8337 it is advised
++ to set this unless a communication problem is observed.
+
+ For QCA8K the 'fixed-link' sub-node supports only the following properties:
+
diff --git a/target/linux/generic/backport-6.1/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch b/target/linux/generic/backport-6.1/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch
new file mode 100644
index 0000000000..2b5a84a1b0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch
@@ -0,0 +1,65 @@
+From bbc4799e8bb6c397e3b3fec13de68e179f5db9ff Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:13 +0200
+Subject: net: dsa: qca8k: add explicit SGMII PLL enable
+
+Support enabling PLL on the SGMII CPU port. Some device require this
+special configuration or no traffic is transmitted and the switch
+doesn't work at all. A dedicated binding is added to the CPU node
+port to apply the correct reg on mac config.
+Fail to correctly configure sgmii with qca8327 switch and warn if pll is
+used on qca8337 with a revision greater than 1.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 19 +++++++++++++++++--
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 18 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1002,6 +1002,18 @@ qca8k_parse_port_config(struct qca8k_pri
+ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
+ priv->sgmii_rx_clk_falling_edge = true;
+
++ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) {
++ priv->sgmii_enable_pll = true;
++
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling");
++ priv->sgmii_enable_pll = false;
++ }
++
++ if (priv->switch_revision < 2)
++ dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more.");
++ }
++
+ break;
+ default:
+ continue;
+@@ -1312,8 +1324,11 @@ qca8k_phylink_mac_config(struct dsa_swit
+ if (ret)
+ return;
+
+- val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
+- QCA8K_SGMII_EN_TX | QCA8K_SGMII_EN_SD;
++ val |= QCA8K_SGMII_EN_SD;
++
++ if (priv->sgmii_enable_pll)
++ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
++ QCA8K_SGMII_EN_TX;
+
+ if (dsa_is_cpu_port(ds, port)) {
+ /* CPU port, we're talking to the CPU MAC, be a PHY */
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -266,6 +266,7 @@ struct qca8k_priv {
+ u8 switch_revision;
+ bool sgmii_rx_clk_falling_edge;
+ bool sgmii_tx_clk_falling_edge;
++ bool sgmii_enable_pll;
+ u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+ bool legacy_phy_port_mapping;
diff --git a/target/linux/generic/backport-6.1/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch b/target/linux/generic/backport-6.1/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch
new file mode 100644
index 0000000000..38dc954e8c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch
@@ -0,0 +1,37 @@
+From 924087c5c3d41553700b0eb83ca2a53b91643dca Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:14 +0200
+Subject: dt-bindings: net: dsa: qca8k: Document qca,led-open-drain binding
+
+Document new binding qca,ignore-power-on-sel used to ignore
+power on strapping and use sw regs instead.
+Document qca,led-open.drain to set led to open drain mode, the
+qca,ignore-power-on-sel is mandatory with this enabled or an error will
+be reported.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -13,6 +13,17 @@ Required properties:
+ Optional properties:
+
+ - reset-gpios: GPIO to be used to reset the whole device
++- qca,ignore-power-on-sel: Ignore power on pin strapping to configure led open
++ drain or eeprom presence. This is needed for broken
++ devices that have wrong configuration or when the oem
++ decided to not use pin strapping and fallback to sw
++ regs.
++- qca,led-open-drain: Set leds to open-drain mode. This requires the
++ qca,ignore-power-on-sel to be set or the driver will fail
++ to probe. This is needed if the oem doesn't use pin
++ strapping to set this mode and prefers to set it using sw
++ regs. The pin strapping related to led open drain mode is
++ the pin B68 for QCA832x and B49 for QCA833x
+
+ Subnodes:
+
diff --git a/target/linux/generic/backport-6.1/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch b/target/linux/generic/backport-6.1/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch
new file mode 100644
index 0000000000..aa5d92a4fd
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch
@@ -0,0 +1,92 @@
+From 362bb238d8bf1470424214a8a5968d9c6cce68fa Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:15 +0200
+Subject: net: dsa: qca8k: add support for pws config reg
+
+Some qca8327 switch require to force the ignore of power on sel
+strapping. Some switch require to set the led open drain mode in regs
+instead of using strapping. While most of the device implements this
+using the correct way using pin strapping, there are still some broken
+device that require to be set using sw regs.
+Introduce a new binding and support these special configuration.
+As led open drain require to ignore pin strapping to work, the probe
+fails with EINVAL error with incorrect configuration.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 39 +++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 6 ++++++
+ 2 files changed, 45 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -932,6 +932,41 @@ static int qca8k_find_cpu_port(struct ds
+ }
+
+ static int
++qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
++{
++ struct device_node *node = priv->dev->of_node;
++ u32 val = 0;
++ int ret;
++
++ /* QCA8327 require to set to the correct mode.
++ * His bigger brother QCA8328 have the 172 pin layout.
++ * Should be applied by default but we set this just to make sure.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
++ QCA8327_PWS_PACKAGE148_EN);
++ if (ret)
++ return ret;
++ }
++
++ if (of_property_read_bool(node, "qca,ignore-power-on-sel"))
++ val |= QCA8K_PWS_POWER_ON_SEL;
++
++ if (of_property_read_bool(node, "qca,led-open-drain")) {
++ if (!(val & QCA8K_PWS_POWER_ON_SEL)) {
++ dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set.");
++ return -EINVAL;
++ }
++
++ val |= QCA8K_PWS_LED_OPEN_EN_CSR;
++ }
++
++ return qca8k_rmw(priv, QCA8K_REG_PWS,
++ QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL,
++ val);
++}
++
++static int
+ qca8k_parse_port_config(struct qca8k_priv *priv)
+ {
+ int port, cpu_port_index = 0, ret;
+@@ -1053,6 +1088,10 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
++ ret = qca8k_setup_of_pws_reg(priv);
++ if (ret)
++ return ret;
++
+ ret = qca8k_setup_mac_pwr_sel(priv);
+ if (ret)
+ return ret;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -46,6 +46,12 @@
+ #define QCA8K_MAX_DELAY 3
+ #define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+ #define QCA8K_REG_PWS 0x010
++#define QCA8K_PWS_POWER_ON_SEL BIT(31)
++/* This reg is only valid for QCA832x and toggle the package
++ * type from 176 pin (by default) to 148 pin used on QCA8327
++ */
++#define QCA8327_PWS_PACKAGE148_EN BIT(30)
++#define QCA8K_PWS_LED_OPEN_EN_CSR BIT(24)
+ #define QCA8K_PWS_SERDES_AEN_DIS BIT(7)
+ #define QCA8K_REG_MODULE_EN 0x030
+ #define QCA8K_MODULE_EN_MIB BIT(0)
diff --git a/target/linux/generic/backport-6.1/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch b/target/linux/generic/backport-6.1/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch
new file mode 100644
index 0000000000..1bfb00c5b2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch
@@ -0,0 +1,32 @@
+From ed7988d77fbfb79366b68f9e7fa60a6080da23d4 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:16 +0200
+Subject: dt-bindings: net: dsa: qca8k: document support for qca8328
+
+QCA8328 is the bigger brother of qca8327. Document the new compatible
+binding and add some information to understand the various switch
+compatible.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/dsa/qca8k.txt | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt
+@@ -3,9 +3,10 @@
+ Required properties:
+
+ - compatible: should be one of:
+- "qca,qca8327"
+- "qca,qca8334"
+- "qca,qca8337"
++ "qca,qca8328": referenced as AR8328(N)-AK1(A/B) QFN 176 pin package
++ "qca,qca8327": referenced as AR8327(N)-AL1A DR-QFN 148 pin package
++ "qca,qca8334": referenced as QCA8334-AL3C QFN 88 pin package
++ "qca,qca8337": referenced as QCA8337N-AL3(B/C) DR-QFN 148 pin package
+
+ - #size-cells: must be 0
+ - #address-cells: must be 1
diff --git a/target/linux/generic/backport-6.1/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch b/target/linux/generic/backport-6.1/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch
new file mode 100644
index 0000000000..b300621e63
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch
@@ -0,0 +1,78 @@
+From f477d1c8bdbef4f400718238e350f16f521d2a3e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:17 +0200
+Subject: net: dsa: qca8k: add support for QCA8328
+
+QCA8328 switch is the bigger brother of the qca8327. Same regs different
+chip. Change the function to set the correct pin layout and introduce a
+new match_data to differentiate the 2 switch as they have the same ID
+and their internal PHY have the same ID.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 19 ++++++++++++++++---
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 17 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -935,6 +935,7 @@ static int
+ qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
+ {
+ struct device_node *node = priv->dev->of_node;
++ const struct qca8k_match_data *data;
+ u32 val = 0;
+ int ret;
+
+@@ -943,8 +944,14 @@ qca8k_setup_of_pws_reg(struct qca8k_priv
+ * Should be applied by default but we set this just to make sure.
+ */
+ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ data = of_device_get_match_data(priv->dev);
++
++ /* Set the correct package of 148 pin for QCA8327 */
++ if (data->reduced_package)
++ val |= QCA8327_PWS_PACKAGE148_EN;
++
+ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
+- QCA8327_PWS_PACKAGE148_EN);
++ val);
+ if (ret)
+ return ret;
+ }
+@@ -2124,7 +2131,12 @@ static int qca8k_resume(struct device *d
+ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ qca8k_suspend, qca8k_resume);
+
+-static const struct qca8k_match_data qca832x = {
++static const struct qca8k_match_data qca8327 = {
++ .id = QCA8K_ID_QCA8327,
++ .reduced_package = true,
++};
++
++static const struct qca8k_match_data qca8328 = {
+ .id = QCA8K_ID_QCA8327,
+ };
+
+@@ -2133,7 +2145,8 @@ static const struct qca8k_match_data qca
+ };
+
+ static const struct of_device_id qca8k_of_match[] = {
+- { .compatible = "qca,qca8327", .data = &qca832x },
++ { .compatible = "qca,qca8327", .data = &qca8327 },
++ { .compatible = "qca,qca8328", .data = &qca8328 },
+ { .compatible = "qca,qca8334", .data = &qca833x },
+ { .compatible = "qca,qca8337", .data = &qca833x },
+ { /* sentinel */ },
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -260,6 +260,7 @@ struct ar8xxx_port_status {
+
+ struct qca8k_match_data {
+ u8 id;
++ bool reduced_package;
+ };
+
+ enum {
diff --git a/target/linux/generic/backport-6.1/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch b/target/linux/generic/backport-6.1/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch
new file mode 100644
index 0000000000..27f94dca02
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch
@@ -0,0 +1,159 @@
+From cef08115846e581f80ff99abf7bf218da1840616 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:18 +0200
+Subject: net: dsa: qca8k: set internal delay also for sgmii
+
+QCA original code report port instability and sa that SGMII also require
+to set internal delay. Generalize the rgmii delay function and apply the
+advised value if they are not defined in DT.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 88 +++++++++++++++++++++++++++++++++----------------
+ drivers/net/dsa/qca8k.h | 2 ++
+ 2 files changed, 62 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1004,6 +1004,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
++ case PHY_INTERFACE_MODE_SGMII:
+ delay = 0;
+
+ if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay))
+@@ -1036,8 +1037,13 @@ qca8k_parse_port_config(struct qca8k_pri
+
+ priv->rgmii_rx_delay[cpu_port_index] = delay;
+
+- break;
+- case PHY_INTERFACE_MODE_SGMII:
++ /* Skip sgmii parsing for rgmii* mode */
++ if (mode == PHY_INTERFACE_MODE_RGMII ||
++ mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_TXID ||
++ mode == PHY_INTERFACE_MODE_RGMII_RXID)
++ break;
++
+ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
+ priv->sgmii_tx_clk_falling_edge = true;
+
+@@ -1261,12 +1267,53 @@ qca8k_setup(struct dsa_switch *ds)
+ }
+
+ static void
++qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index,
++ u32 reg)
++{
++ u32 delay, val = 0;
++ int ret;
++
++ /* Delay can be declared in 3 different way.
++ * Mode to rgmii and internal-delay standard binding defined
++ * rgmii-id or rgmii-tx/rx phy mode set.
++ * The parse logic set a delay different than 0 only when one
++ * of the 3 different way is used. In all other case delay is
++ * not enabled. With ID or TX/RXID delay is enabled and set
++ * to the default and recommended value.
++ */
++ if (priv->rgmii_tx_delay[cpu_port_index]) {
++ delay = priv->rgmii_tx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
++ }
++
++ if (priv->rgmii_rx_delay[cpu_port_index]) {
++ delay = priv->rgmii_rx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
++ }
++
++ /* Set RGMII delay based on the selected values */
++ ret = qca8k_rmw(priv, reg,
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN,
++ val);
++ if (ret)
++ dev_err(priv->dev, "Failed to set internal delay for CPU port%d",
++ cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6);
++}
++
++static void
+ qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+ const struct phylink_link_state *state)
+ {
+ struct qca8k_priv *priv = ds->priv;
+ int cpu_port_index, ret;
+- u32 reg, val, delay;
++ u32 reg, val;
+
+ switch (port) {
+ case 0: /* 1st CPU port */
+@@ -1315,32 +1362,10 @@ qca8k_phylink_mac_config(struct dsa_swit
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+- val = QCA8K_PORT_PAD_RGMII_EN;
+-
+- /* Delay can be declared in 3 different way.
+- * Mode to rgmii and internal-delay standard binding defined
+- * rgmii-id or rgmii-tx/rx phy mode set.
+- * The parse logic set a delay different than 0 only when one
+- * of the 3 different way is used. In all other case delay is
+- * not enabled. With ID or TX/RXID delay is enabled and set
+- * to the default and recommended value.
+- */
+- if (priv->rgmii_tx_delay[cpu_port_index]) {
+- delay = priv->rgmii_tx_delay[cpu_port_index];
+-
+- val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
+- }
+-
+- if (priv->rgmii_rx_delay[cpu_port_index]) {
+- delay = priv->rgmii_rx_delay[cpu_port_index];
+-
+- val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
+- }
++ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
+
+- /* Set RGMII delay based on the selected values */
+- qca8k_write(priv, reg, val);
++ /* Configure rgmii delay */
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
+
+ /* QCA8337 requires to set rgmii rx delay for all ports.
+ * This is enabled through PORT5_PAD_CTRL for all ports,
+@@ -1411,6 +1436,13 @@ qca8k_phylink_mac_config(struct dsa_swit
+ QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
+ val);
++
++ /* From original code is reported port instability as SGMII also
++ * require delay set. Apply advised values here or take them from DT.
++ */
++ if (state->interface == PHY_INTERFACE_MODE_SGMII)
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
++
+ break;
+ default:
+ dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -39,7 +39,9 @@
+ #define QCA8K_REG_PORT5_PAD_CTRL 0x008
+ #define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+ #define QCA8K_PORT_PAD_RGMII_EN BIT(26)
++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22)
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) ((x) << 22)
++#define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) ((x) << 20)
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
diff --git a/target/linux/generic/backport-6.1/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch b/target/linux/generic/backport-6.1/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch
new file mode 100644
index 0000000000..b991798c87
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch
@@ -0,0 +1,124 @@
+From fd0bb28c547f7c8affb1691128cece38f5b626a1 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:19 +0200
+Subject: net: dsa: qca8k: move port config to dedicated struct
+
+Move ports related config to dedicated struct to keep things organized.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 26 +++++++++++++-------------
+ drivers/net/dsa/qca8k.h | 10 +++++++---
+ 2 files changed, 20 insertions(+), 16 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1019,7 +1019,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ delay = 3;
+ }
+
+- priv->rgmii_tx_delay[cpu_port_index] = delay;
++ priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay;
+
+ delay = 0;
+
+@@ -1035,7 +1035,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ delay = 3;
+ }
+
+- priv->rgmii_rx_delay[cpu_port_index] = delay;
++ priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay;
+
+ /* Skip sgmii parsing for rgmii* mode */
+ if (mode == PHY_INTERFACE_MODE_RGMII ||
+@@ -1045,17 +1045,17 @@ qca8k_parse_port_config(struct qca8k_pri
+ break;
+
+ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
+- priv->sgmii_tx_clk_falling_edge = true;
++ priv->ports_config.sgmii_tx_clk_falling_edge = true;
+
+ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
+- priv->sgmii_rx_clk_falling_edge = true;
++ priv->ports_config.sgmii_rx_clk_falling_edge = true;
+
+ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) {
+- priv->sgmii_enable_pll = true;
++ priv->ports_config.sgmii_enable_pll = true;
+
+ if (priv->switch_id == QCA8K_ID_QCA8327) {
+ dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling");
+- priv->sgmii_enable_pll = false;
++ priv->ports_config.sgmii_enable_pll = false;
+ }
+
+ if (priv->switch_revision < 2)
+@@ -1281,15 +1281,15 @@ qca8k_mac_config_setup_internal_delay(st
+ * not enabled. With ID or TX/RXID delay is enabled and set
+ * to the default and recommended value.
+ */
+- if (priv->rgmii_tx_delay[cpu_port_index]) {
+- delay = priv->rgmii_tx_delay[cpu_port_index];
++ if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) {
++ delay = priv->ports_config.rgmii_tx_delay[cpu_port_index];
+
+ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
+ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
+ }
+
+- if (priv->rgmii_rx_delay[cpu_port_index]) {
+- delay = priv->rgmii_rx_delay[cpu_port_index];
++ if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) {
++ delay = priv->ports_config.rgmii_rx_delay[cpu_port_index];
+
+ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
+ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
+@@ -1397,7 +1397,7 @@ qca8k_phylink_mac_config(struct dsa_swit
+
+ val |= QCA8K_SGMII_EN_SD;
+
+- if (priv->sgmii_enable_pll)
++ if (priv->ports_config.sgmii_enable_pll)
+ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
+ QCA8K_SGMII_EN_TX;
+
+@@ -1425,10 +1425,10 @@ qca8k_phylink_mac_config(struct dsa_swit
+ val = 0;
+
+ /* SGMII Clock phase configuration */
+- if (priv->sgmii_rx_clk_falling_edge)
++ if (priv->ports_config.sgmii_rx_clk_falling_edge)
+ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE;
+
+- if (priv->sgmii_tx_clk_falling_edge)
++ if (priv->ports_config.sgmii_tx_clk_falling_edge)
+ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
+
+ if (val)
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -270,15 +270,19 @@ enum {
+ QCA8K_CPU_PORT6,
+ };
+
+-struct qca8k_priv {
+- u8 switch_id;
+- u8 switch_revision;
++struct qca8k_ports_config {
+ bool sgmii_rx_clk_falling_edge;
+ bool sgmii_tx_clk_falling_edge;
+ bool sgmii_enable_pll;
+ u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
++};
++
++struct qca8k_priv {
++ u8 switch_id;
++ u8 switch_revision;
+ bool legacy_phy_port_mapping;
++ struct qca8k_ports_config ports_config;
+ struct regmap *regmap;
+ struct mii_bus *bus;
+ struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
diff --git a/target/linux/generic/backport-6.1/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch b/target/linux/generic/backport-6.1/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch
new file mode 100644
index 0000000000..f7cb514176
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch
@@ -0,0 +1,26 @@
+From e52073a8e3086046a098b8a7cbeb282ff0cdb424 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:20 +0200
+Subject: dt-bindings: net: ipq8064-mdio: fix warning with new qca8k switch
+
+Fix warning now that we have qca8k switch Documentation using yaml.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml
++++ b/Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml
+@@ -51,6 +51,9 @@ examples:
+ switch@10 {
+ compatible = "qca,qca8337";
+ reg = <0x10>;
+- /* ... */
++
++ ports {
++ /* ... */
++ };
+ };
+ };
diff --git a/target/linux/generic/backport-6.1/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch b/target/linux/generic/backport-6.1/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch
new file mode 100644
index 0000000000..b9bce97dd3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch
@@ -0,0 +1,631 @@
+From d291fbb8245d5ba04979fed85575860a5cea7196 Mon Sep 17 00:00:00 2001
+From: Matthew Hagan <mnhagan88@gmail.com>
+Date: Thu, 14 Oct 2021 00:39:21 +0200
+Subject: dt-bindings: net: dsa: qca8k: convert to YAML schema
+
+Convert the qca8k bindings to YAML format.
+
+Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
+Co-developed-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ .../devicetree/bindings/net/dsa/qca8k.txt | 245 --------------
+ .../devicetree/bindings/net/dsa/qca8k.yaml | 362 +++++++++++++++++++++
+ 2 files changed, 362 insertions(+), 245 deletions(-)
+ delete mode 100644 Documentation/devicetree/bindings/net/dsa/qca8k.txt
+ create mode 100644 Documentation/devicetree/bindings/net/dsa/qca8k.yaml
+
+--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt
++++ /dev/null
+@@ -1,245 +0,0 @@
+-* Qualcomm Atheros QCA8xxx switch family
+-
+-Required properties:
+-
+-- compatible: should be one of:
+- "qca,qca8328": referenced as AR8328(N)-AK1(A/B) QFN 176 pin package
+- "qca,qca8327": referenced as AR8327(N)-AL1A DR-QFN 148 pin package
+- "qca,qca8334": referenced as QCA8334-AL3C QFN 88 pin package
+- "qca,qca8337": referenced as QCA8337N-AL3(B/C) DR-QFN 148 pin package
+-
+-- #size-cells: must be 0
+-- #address-cells: must be 1
+-
+-Optional properties:
+-
+-- reset-gpios: GPIO to be used to reset the whole device
+-- qca,ignore-power-on-sel: Ignore power on pin strapping to configure led open
+- drain or eeprom presence. This is needed for broken
+- devices that have wrong configuration or when the oem
+- decided to not use pin strapping and fallback to sw
+- regs.
+-- qca,led-open-drain: Set leds to open-drain mode. This requires the
+- qca,ignore-power-on-sel to be set or the driver will fail
+- to probe. This is needed if the oem doesn't use pin
+- strapping to set this mode and prefers to set it using sw
+- regs. The pin strapping related to led open drain mode is
+- the pin B68 for QCA832x and B49 for QCA833x
+-
+-Subnodes:
+-
+-The integrated switch subnode should be specified according to the binding
+-described in dsa/dsa.txt. If the QCA8K switch is connect to a SoC's external
+-mdio-bus each subnode describing a port needs to have a valid phandle
+-referencing the internal PHY it is connected to. This is because there's no
+-N:N mapping of port and PHY id.
+-To declare the internal mdio-bus configuration, declare a mdio node in the
+-switch node and declare the phandle for the port referencing the internal
+-PHY is connected to. In this config a internal mdio-bus is registered and
+-the mdio MASTER is used as communication.
+-
+-Don't use mixed external and internal mdio-bus configurations, as this is
+-not supported by the hardware.
+-
+-This switch support 2 CPU port. Normally and advised configuration is with
+-CPU port set to port 0. It is also possible to set the CPU port to port 6
+-if the device requires it. The driver will configure the switch to the defined
+-port. With both CPU port declared the first CPU port is selected as primary
+-and the secondary CPU ignored.
+-
+-A CPU port node has the following optional node:
+-
+-- fixed-link : Fixed-link subnode describing a link to a non-MDIO
+- managed entity. See
+- Documentation/devicetree/bindings/net/fixed-link.txt
+- for details.
+-- qca,sgmii-rxclk-falling-edge: Set the receive clock phase to falling edge.
+- Mostly used in qca8327 with CPU port 0 set to
+- sgmii.
+-- qca,sgmii-txclk-falling-edge: Set the transmit clock phase to falling edge.
+-- qca,sgmii-enable-pll : For SGMII CPU port, explicitly enable PLL, TX and RX
+- chain along with Signal Detection.
+- This should NOT be enabled for qca8327. If enabled with
+- qca8327 the sgmii port won't correctly init and an err
+- is printed.
+- This can be required for qca8337 switch with revision 2.
+- A warning is displayed when used with revision greater
+- 2.
+- With CPU port set to sgmii and qca8337 it is advised
+- to set this unless a communication problem is observed.
+-
+-For QCA8K the 'fixed-link' sub-node supports only the following properties:
+-
+-- 'speed' (integer, mandatory), to indicate the link speed. Accepted
+- values are 10, 100 and 1000
+-- 'full-duplex' (boolean, optional), to indicate that full duplex is
+- used. When absent, half duplex is assumed.
+-
+-Examples:
+-
+-for the external mdio-bus configuration:
+-
+- &mdio0 {
+- phy_port1: phy@0 {
+- reg = <0>;
+- };
+-
+- phy_port2: phy@1 {
+- reg = <1>;
+- };
+-
+- phy_port3: phy@2 {
+- reg = <2>;
+- };
+-
+- phy_port4: phy@3 {
+- reg = <3>;
+- };
+-
+- phy_port5: phy@4 {
+- reg = <4>;
+- };
+-
+- switch@10 {
+- compatible = "qca,qca8337";
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>;
+- reg = <0x10>;
+-
+- ports {
+- #address-cells = <1>;
+- #size-cells = <0>;
+- port@0 {
+- reg = <0>;
+- label = "cpu";
+- ethernet = <&gmac1>;
+- phy-mode = "rgmii";
+- fixed-link {
+- speed = 1000;
+- full-duplex;
+- };
+- };
+-
+- port@1 {
+- reg = <1>;
+- label = "lan1";
+- phy-handle = <&phy_port1>;
+- };
+-
+- port@2 {
+- reg = <2>;
+- label = "lan2";
+- phy-handle = <&phy_port2>;
+- };
+-
+- port@3 {
+- reg = <3>;
+- label = "lan3";
+- phy-handle = <&phy_port3>;
+- };
+-
+- port@4 {
+- reg = <4>;
+- label = "lan4";
+- phy-handle = <&phy_port4>;
+- };
+-
+- port@5 {
+- reg = <5>;
+- label = "wan";
+- phy-handle = <&phy_port5>;
+- };
+- };
+- };
+- };
+-
+-for the internal master mdio-bus configuration:
+-
+- &mdio0 {
+- switch@10 {
+- compatible = "qca,qca8337";
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>;
+- reg = <0x10>;
+-
+- ports {
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- port@0 {
+- reg = <0>;
+- label = "cpu";
+- ethernet = <&gmac1>;
+- phy-mode = "rgmii";
+- fixed-link {
+- speed = 1000;
+- full-duplex;
+- };
+- };
+-
+- port@1 {
+- reg = <1>;
+- label = "lan1";
+- phy-mode = "internal";
+- phy-handle = <&phy_port1>;
+- };
+-
+- port@2 {
+- reg = <2>;
+- label = "lan2";
+- phy-mode = "internal";
+- phy-handle = <&phy_port2>;
+- };
+-
+- port@3 {
+- reg = <3>;
+- label = "lan3";
+- phy-mode = "internal";
+- phy-handle = <&phy_port3>;
+- };
+-
+- port@4 {
+- reg = <4>;
+- label = "lan4";
+- phy-mode = "internal";
+- phy-handle = <&phy_port4>;
+- };
+-
+- port@5 {
+- reg = <5>;
+- label = "wan";
+- phy-mode = "internal";
+- phy-handle = <&phy_port5>;
+- };
+- };
+-
+- mdio {
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- phy_port1: phy@0 {
+- reg = <0>;
+- };
+-
+- phy_port2: phy@1 {
+- reg = <1>;
+- };
+-
+- phy_port3: phy@2 {
+- reg = <2>;
+- };
+-
+- phy_port4: phy@3 {
+- reg = <3>;
+- };
+-
+- phy_port5: phy@4 {
+- reg = <4>;
+- };
+- };
+- };
+- };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.yaml
+@@ -0,0 +1,362 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/dsa/qca8k.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm Atheros QCA83xx switch family
++
++maintainers:
++ - John Crispin <john@phrozen.org>
++
++description:
++ If the QCA8K switch is connect to an SoC's external mdio-bus, each subnode
++ describing a port needs to have a valid phandle referencing the internal PHY
++ it is connected to. This is because there is no N:N mapping of port and PHY
++ ID. To declare the internal mdio-bus configuration, declare an MDIO node in
++ the switch node and declare the phandle for the port, referencing the internal
++ PHY it is connected to. In this config, an internal mdio-bus is registered and
++ the MDIO master is used for communication. Mixed external and internal
++ mdio-bus configurations are not supported by the hardware.
++
++properties:
++ compatible:
++ oneOf:
++ - enum:
++ - qca,qca8327
++ - qca,qca8328
++ - qca,qca8334
++ - qca,qca8337
++ description: |
++ qca,qca8328: referenced as AR8328(N)-AK1(A/B) QFN 176 pin package
++ qca,qca8327: referenced as AR8327(N)-AL1A DR-QFN 148 pin package
++ qca,qca8334: referenced as QCA8334-AL3C QFN 88 pin package
++ qca,qca8337: referenced as QCA8337N-AL3(B/C) DR-QFN 148 pin package
++
++ reg:
++ maxItems: 1
++
++ reset-gpios:
++ description:
++ GPIO to be used to reset the whole device
++ maxItems: 1
++
++ qca,ignore-power-on-sel:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ Ignore power-on pin strapping to configure LED open-drain or EEPROM
++ presence. This is needed for devices with incorrect configuration or when
++ the OEM has decided not to use pin strapping and falls back to SW regs.
++
++ qca,led-open-drain:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ Set LEDs to open-drain mode. This requires the qca,ignore-power-on-sel to
++ be set, otherwise the driver will fail at probe. This is required if the
++ OEM does not use pin strapping to set this mode and prefers to set it
++ using SW regs. The pin strappings related to LED open-drain mode are
++ B68 on the QCA832x and B49 on the QCA833x.
++
++ mdio:
++ type: object
++ description: Qca8k switch have an internal mdio to access switch port.
++ If this is not present, the legacy mapping is used and the
++ internal mdio access is used.
++ With the legacy mapping the reg corresponding to the internal
++ mdio is the switch reg with an offset of -1.
++
++ properties:
++ '#address-cells':
++ const: 1
++ '#size-cells':
++ const: 0
++
++ patternProperties:
++ "^(ethernet-)?phy@[0-4]$":
++ type: object
++
++ allOf:
++ - $ref: "http://devicetree.org/schemas/net/mdio.yaml#"
++
++ properties:
++ reg:
++ maxItems: 1
++
++ required:
++ - reg
++
++patternProperties:
++ "^(ethernet-)?ports$":
++ type: object
++ properties:
++ '#address-cells':
++ const: 1
++ '#size-cells':
++ const: 0
++
++ patternProperties:
++ "^(ethernet-)?port@[0-6]$":
++ type: object
++ description: Ethernet switch ports
++
++ properties:
++ reg:
++ description: Port number
++
++ label:
++ description:
++ Describes the label associated with this port, which will become
++ the netdev name
++ $ref: /schemas/types.yaml#/definitions/string
++
++ link:
++ description:
++ Should be a list of phandles to other switch's DSA port. This
++ port is used as the outgoing port towards the phandle ports. The
++ full routing information must be given, not just the one hop
++ routes to neighbouring switches
++ $ref: /schemas/types.yaml#/definitions/phandle-array
++
++ ethernet:
++ description:
++ Should be a phandle to a valid Ethernet device node. This host
++ device is what the switch port is connected to
++ $ref: /schemas/types.yaml#/definitions/phandle
++
++ phy-handle: true
++
++ phy-mode: true
++
++ fixed-link: true
++
++ mac-address: true
++
++ sfp: true
++
++ qca,sgmii-rxclk-falling-edge:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ Set the receive clock phase to falling edge. Mostly commonly used on
++ the QCA8327 with CPU port 0 set to SGMII.
++
++ qca,sgmii-txclk-falling-edge:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ Set the transmit clock phase to falling edge.
++
++ qca,sgmii-enable-pll:
++ $ref: /schemas/types.yaml#/definitions/flag
++ description:
++ For SGMII CPU port, explicitly enable PLL, TX and RX chain along with
++ Signal Detection. On the QCA8327 this should not be enabled, otherwise
++ the SGMII port will not initialize. When used on the QCA8337, revision 3
++ or greater, a warning will be displayed. When the CPU port is set to
++ SGMII on the QCA8337, it is advised to set this unless a communication
++ issue is observed.
++
++ required:
++ - reg
++
++ additionalProperties: false
++
++oneOf:
++ - required:
++ - ports
++ - required:
++ - ethernet-ports
++
++required:
++ - compatible
++ - reg
++
++additionalProperties: true
++
++examples:
++ - |
++ #include <dt-bindings/gpio/gpio.h>
++
++ mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ external_phy_port1: ethernet-phy@0 {
++ reg = <0>;
++ };
++
++ external_phy_port2: ethernet-phy@1 {
++ reg = <1>;
++ };
++
++ external_phy_port3: ethernet-phy@2 {
++ reg = <2>;
++ };
++
++ external_phy_port4: ethernet-phy@3 {
++ reg = <3>;
++ };
++
++ external_phy_port5: ethernet-phy@4 {
++ reg = <4>;
++ };
++
++ switch@10 {
++ compatible = "qca,qca8337";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>;
++ reg = <0x10>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ reg = <0>;
++ label = "cpu";
++ ethernet = <&gmac1>;
++ phy-mode = "rgmii";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++ label = "lan1";
++ phy-handle = <&external_phy_port1>;
++ };
++
++ port@2 {
++ reg = <2>;
++ label = "lan2";
++ phy-handle = <&external_phy_port2>;
++ };
++
++ port@3 {
++ reg = <3>;
++ label = "lan3";
++ phy-handle = <&external_phy_port3>;
++ };
++
++ port@4 {
++ reg = <4>;
++ label = "lan4";
++ phy-handle = <&external_phy_port4>;
++ };
++
++ port@5 {
++ reg = <5>;
++ label = "wan";
++ phy-handle = <&external_phy_port5>;
++ };
++ };
++ };
++ };
++ - |
++ #include <dt-bindings/gpio/gpio.h>
++
++ mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ switch@10 {
++ compatible = "qca,qca8337";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>;
++ reg = <0x10>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ reg = <0>;
++ label = "cpu";
++ ethernet = <&gmac1>;
++ phy-mode = "rgmii";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++ label = "lan1";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port1>;
++ };
++
++ port@2 {
++ reg = <2>;
++ label = "lan2";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port2>;
++ };
++
++ port@3 {
++ reg = <3>;
++ label = "lan3";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port3>;
++ };
++
++ port@4 {
++ reg = <4>;
++ label = "lan4";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port4>;
++ };
++
++ port@5 {
++ reg = <5>;
++ label = "wan";
++ phy-mode = "internal";
++ phy-handle = <&internal_phy_port5>;
++ };
++
++ port@6 {
++ reg = <0>;
++ label = "cpu";
++ ethernet = <&gmac1>;
++ phy-mode = "sgmii";
++
++ qca,sgmii-rxclk-falling-edge;
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++ };
++
++ mdio {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ internal_phy_port1: ethernet-phy@0 {
++ reg = <0>;
++ };
++
++ internal_phy_port2: ethernet-phy@1 {
++ reg = <1>;
++ };
++
++ internal_phy_port3: ethernet-phy@2 {
++ reg = <2>;
++ };
++
++ internal_phy_port4: ethernet-phy@3 {
++ reg = <3>;
++ };
++
++ internal_phy_port5: ethernet-phy@4 {
++ reg = <4>;
++ };
++ };
++ };
++ };
diff --git a/target/linux/generic/backport-6.1/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch b/target/linux/generic/backport-6.1/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch
new file mode 100644
index 0000000000..a510cfdc18
--- /dev/null
+++ b/target/linux/generic/backport-6.1/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch
@@ -0,0 +1,28 @@
+From 06dd34a628ae5b6a839b757e746de165d6789ca8 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 17 Oct 2021 16:56:46 +0200
+Subject: net: dsa: qca8k: fix delay applied to wrong cpu in parse_port_config
+
+Fix delay settings applied to wrong cpu in parse_port_config. The delay
+values is set to the wrong index as the cpu_port_index is incremented
+too early. Start the cpu_port_index to -1 so the correct value is
+applied to address also the case with invalid phy mode and not available
+port.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -976,7 +976,7 @@ qca8k_setup_of_pws_reg(struct qca8k_priv
+ static int
+ qca8k_parse_port_config(struct qca8k_priv *priv)
+ {
+- int port, cpu_port_index = 0, ret;
++ int port, cpu_port_index = -1, ret;
+ struct device_node *port_dn;
+ phy_interface_t mode;
+ struct dsa_port *dp;
diff --git a/target/linux/generic/backport-6.1/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch b/target/linux/generic/backport-6.1/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch
new file mode 100644
index 0000000000..71fa3022d5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch
@@ -0,0 +1,151 @@
+From 040e926f5813a5f4cc18dbff7c942d1e52f368f2 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 19 Oct 2021 02:08:50 +0200
+Subject: net: dsa: qca8k: tidy for loop in setup and add cpu port check
+
+Tidy and organize qca8k setup function from multiple for loop.
+Change for loop in bridge leave/join to scan all port and skip cpu port.
+No functional change intended.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 74 +++++++++++++++++++++++++++++--------------------
+ 1 file changed, 44 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1122,28 +1122,34 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ dev_warn(priv->dev, "mib init failed");
+
+- /* Enable QCA header mode on the cpu port */
+- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(cpu_port),
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
+- if (ret) {
+- dev_err(priv->dev, "failed enabling QCA header mode");
+- return ret;
+- }
+-
+- /* Disable forwarding by default on all ports */
++ /* Initial setup of all ports */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ /* Disable forwarding by default on all ports */
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+ QCA8K_PORT_LOOKUP_MEMBER, 0);
+ if (ret)
+ return ret;
+- }
+
+- /* Disable MAC by default on all ports */
+- for (i = 1; i < QCA8K_NUM_PORTS; i++)
+- qca8k_port_set_status(priv, i, 0);
++ /* Enable QCA header mode on all cpu ports */
++ if (dsa_is_cpu_port(ds, i)) {
++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
++ if (ret) {
++ dev_err(priv->dev, "failed enabling QCA header mode");
++ return ret;
++ }
++ }
++
++ /* Disable MAC by default on all user ports */
++ if (dsa_is_user_port(ds, i))
++ qca8k_port_set_status(priv, i, 0);
++ }
+
+- /* Forward all unknown frames to CPU port for Linux processing */
++ /* Forward all unknown frames to CPU port for Linux processing
++ * Notice that in multi-cpu config only one port should be set
++ * for igmp, unknown, multicast and broadcast packet
++ */
+ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+@@ -1152,11 +1158,13 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
+- /* Setup connection between CPU port & user ports */
++ /* Setup connection between CPU port & user ports
++ * Configure specific switch configuration for ports
++ */
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+ /* CPU port gets connected to all user ports of the switch */
+ if (dsa_is_cpu_port(ds, i)) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
+ if (ret)
+ return ret;
+@@ -1193,16 +1201,14 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+ }
+- }
+
+- /* The port 5 of the qca8337 have some problem in flood condition. The
+- * original legacy driver had some specific buffer and priority settings
+- * for the different port suggested by the QCA switch team. Add this
+- * missing settings to improve switch stability under load condition.
+- * This problem is limited to qca8337 and other qca8k switch are not affected.
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8337) {
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ /* The port 5 of the qca8337 have some problem in flood condition. The
++ * original legacy driver had some specific buffer and priority settings
++ * for the different port suggested by the QCA switch team. Add this
++ * missing settings to improve switch stability under load condition.
++ * This problem is limited to qca8337 and other qca8k switch are not affected.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8337) {
+ switch (i) {
+ /* The 2 CPU port and port 5 requires some different
+ * priority than any other ports.
+@@ -1238,6 +1244,12 @@ qca8k_setup(struct dsa_switch *ds)
+ QCA8K_PORT_HOL_CTRL1_WRED_EN,
+ mask);
+ }
++
++ /* Set initial MTU for every port.
++ * We have only have a general MTU setting. So track
++ * every port and set the max across all port.
++ */
++ priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+ }
+
+ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
+@@ -1251,8 +1263,6 @@ qca8k_setup(struct dsa_switch *ds)
+ }
+
+ /* Setup our port MTUs to match power on defaults */
+- for (i = 0; i < QCA8K_NUM_PORTS; i++)
+- priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
+ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
+ if (ret)
+ dev_warn(priv->dev, "failed setting MTU settings");
+@@ -1728,7 +1738,9 @@ qca8k_port_bridge_join(struct dsa_switch
+ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+ port_mask = BIT(cpu_port);
+
+- for (i = 1; i < QCA8K_NUM_PORTS; i++) {
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
+ if (dsa_to_port(ds, i)->bridge_dev != br)
+ continue;
+ /* Add this port to the portvlan mask of the other ports
+@@ -1758,7 +1770,9 @@ qca8k_port_bridge_leave(struct dsa_switc
+
+ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+
+- for (i = 1; i < QCA8K_NUM_PORTS; i++) {
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
+ if (dsa_to_port(ds, i)->bridge_dev != br)
+ continue;
+ /* Remove this port to the portvlan mask of the other ports
diff --git a/target/linux/generic/backport-6.1/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch b/target/linux/generic/backport-6.1/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch
new file mode 100644
index 0000000000..4a61703c52
--- /dev/null
+++ b/target/linux/generic/backport-6.1/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch
@@ -0,0 +1,47 @@
+From 5f15d392dcb4aa250a63d6f2c5adfc26c0aedc78 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 2 Nov 2021 19:30:41 +0100
+Subject: net: dsa: qca8k: make sure PAD0 MAC06 exchange is disabled
+
+Some device set MAC06 exchange in the bootloader. This cause some
+problem as we don't support this strange mode and we just set the port6
+as the primary CPU port. With MAC06 exchange, PAD0 reg configure port6
+instead of port0. Add an extra check and explicitly disable MAC06 exchange
+to correctly configure the port PAD config.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Fixes: 3fcf734aa482 ("net: dsa: qca8k: add support for cpu port 6")
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 8 ++++++++
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1109,6 +1109,14 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
++ /* Make sure MAC06 is disabled */
++ ret = qca8k_reg_clear(priv, QCA8K_REG_PORT0_PAD_CTRL,
++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
++ if (ret) {
++ dev_err(priv->dev, "failed disabling MAC06 exchange");
++ return ret;
++ }
++
+ /* Enable CPU Port */
+ ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -34,6 +34,7 @@
+ #define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8)
+ #define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8)
+ #define QCA8K_REG_PORT0_PAD_CTRL 0x004
++#define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31)
+ #define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19)
+ #define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18)
+ #define QCA8K_REG_PORT5_PAD_CTRL 0x008
diff --git a/target/linux/generic/backport-6.1/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch b/target/linux/generic/backport-6.1/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch
new file mode 100644
index 0000000000..df9518d86c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch
@@ -0,0 +1,48 @@
+From 3b00a07c2443745d62babfe08dbb2ad8e649526e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Fri, 19 Nov 2021 03:03:49 +0100
+Subject: [PATCH] net: dsa: qca8k: fix internal delay applied to the wrong PAD
+ config
+
+With SGMII phy the internal delay is always applied to the PAD0 config.
+This is caused by the falling edge configuration that hardcode the reg
+to PAD0 (as the falling edge bits are present only in PAD0 reg)
+Move the delay configuration before the reg overwrite to correctly apply
+the delay.
+
+Fixes: cef08115846e ("net: dsa: qca8k: set internal delay also for sgmii")
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1433,6 +1433,12 @@ qca8k_phylink_mac_config(struct dsa_swit
+
+ qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
+
++ /* From original code is reported port instability as SGMII also
++ * require delay set. Apply advised values here or take them from DT.
++ */
++ if (state->interface == PHY_INTERFACE_MODE_SGMII)
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
++
+ /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
+ * falling edge is set writing in the PORT0 PAD reg
+ */
+@@ -1455,12 +1461,6 @@ qca8k_phylink_mac_config(struct dsa_swit
+ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
+ val);
+
+- /* From original code is reported port instability as SGMII also
+- * require delay set. Apply advised values here or take them from DT.
+- */
+- if (state->interface == PHY_INTERFACE_MODE_SGMII)
+- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
+-
+ break;
+ default:
+ dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
diff --git a/target/linux/generic/backport-6.1/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch b/target/linux/generic/backport-6.1/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch
new file mode 100644
index 0000000000..7348d93ec4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch
@@ -0,0 +1,46 @@
+From 65258b9d8cde45689bdc86ca39b50f01f983733b Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Fri, 19 Nov 2021 03:03:50 +0100
+Subject: [PATCH] net: dsa: qca8k: fix MTU calculation
+
+qca8k has a global MTU, so its tracking the MTU per port to make sure
+that the largest MTU gets applied.
+Since it uses the frame size instead of MTU the driver MTU change function
+will then add the size of Ethernet header and checksum on top of MTU.
+
+The driver currently populates the per port MTU size as Ethernet frame
+length + checksum which equals 1518.
+
+The issue is that then MTU change function will go through all of the
+ports, find the largest MTU and apply the Ethernet header + checksum on
+top of it again, so for a desired MTU of 1500 you will end up with 1536.
+
+This is obviously incorrect, so to correct it populate the per port struct
+MTU with just the MTU and not include the Ethernet header + checksum size
+as those will be added by the MTU change function.
+
+Fixes: f58d2598cf70 ("net: dsa: qca8k: implement the port MTU callbacks")
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1256,8 +1256,12 @@ qca8k_setup(struct dsa_switch *ds)
+ /* Set initial MTU for every port.
+ * We have only have a general MTU setting. So track
+ * every port and set the max across all port.
++ * Set per port MTU to 1500 as the MTU change function
++ * will add the overhead and if its set to 1518 then it
++ * will apply the overhead again and we will end up with
++ * MTU of 1536 instead of 1518
+ */
+- priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN;
++ priv->port_mtu[i] = ETH_DATA_LEN;
+ }
+
+ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
diff --git a/target/linux/generic/backport-6.1/753-v5.17-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch b/target/linux/generic/backport-6.1/753-v5.17-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch
new file mode 100644
index 0000000000..f477b1b929
--- /dev/null
+++ b/target/linux/generic/backport-6.1/753-v5.17-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch
@@ -0,0 +1,29 @@
+From b9133f3ef5a2659730cf47a74bd0a9259f1cf8ff Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:40 +0100
+Subject: net: dsa: qca8k: remove redundant check in parse_port_config
+
+The very next check for port 0 and 6 already makes sure we don't go out
+of bounds with the ports_config delay table.
+Remove the redundant check.
+
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -983,7 +983,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ u32 delay;
+
+ /* We have 2 CPU port. Check them */
+- for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) {
++ for (port = 0; port < QCA8K_NUM_PORTS; port++) {
+ /* Skip every other port */
+ if (port != 0 && port != 6)
+ continue;
diff --git a/target/linux/generic/backport-6.1/754-v5.17-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch b/target/linux/generic/backport-6.1/754-v5.17-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch
new file mode 100644
index 0000000000..2cea88089d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/754-v5.17-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch
@@ -0,0 +1,507 @@
+From 90ae68bfc2ffcb54a4ba4f64edbeb84a80cbb57c Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:41 +0100
+Subject: net: dsa: qca8k: convert to GENMASK/FIELD_PREP/FIELD_GET
+
+Convert and try to standardize bit fields using
+GENMASK/FIELD_PREP/FIELD_GET macros. Rework some logic to support the
+standard macro and tidy things up. No functional change intended.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 98 +++++++++++++++----------------
+ drivers/net/dsa/qca8k.h | 153 ++++++++++++++++++++++++++----------------------
+ 2 files changed, 130 insertions(+), 121 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -9,6 +9,7 @@
+ #include <linux/module.h>
+ #include <linux/phy.h>
+ #include <linux/netdevice.h>
++#include <linux/bitfield.h>
+ #include <net/dsa.h>
+ #include <linux/of_net.h>
+ #include <linux/of_mdio.h>
+@@ -319,18 +320,18 @@ qca8k_fdb_read(struct qca8k_priv *priv,
+ }
+
+ /* vid - 83:72 */
+- fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M;
++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
+ /* aging - 67:64 */
+- fdb->aging = reg[2] & QCA8K_ATU_STATUS_M;
++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
+ /* portmask - 54:48 */
+- fdb->port_mask = (reg[1] >> QCA8K_ATU_PORT_S) & QCA8K_ATU_PORT_M;
++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
+ /* mac - 47:0 */
+- fdb->mac[0] = (reg[1] >> QCA8K_ATU_ADDR0_S) & 0xff;
+- fdb->mac[1] = reg[1] & 0xff;
+- fdb->mac[2] = (reg[0] >> QCA8K_ATU_ADDR2_S) & 0xff;
+- fdb->mac[3] = (reg[0] >> QCA8K_ATU_ADDR3_S) & 0xff;
+- fdb->mac[4] = (reg[0] >> QCA8K_ATU_ADDR4_S) & 0xff;
+- fdb->mac[5] = reg[0] & 0xff;
++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
+
+ return 0;
+ }
+@@ -343,18 +344,18 @@ qca8k_fdb_write(struct qca8k_priv *priv,
+ int i;
+
+ /* vid - 83:72 */
+- reg[2] = (vid & QCA8K_ATU_VID_M) << QCA8K_ATU_VID_S;
++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
+ /* aging - 67:64 */
+- reg[2] |= aging & QCA8K_ATU_STATUS_M;
++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
+ /* portmask - 54:48 */
+- reg[1] = (port_mask & QCA8K_ATU_PORT_M) << QCA8K_ATU_PORT_S;
++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
+ /* mac - 47:0 */
+- reg[1] |= mac[0] << QCA8K_ATU_ADDR0_S;
+- reg[1] |= mac[1];
+- reg[0] |= mac[2] << QCA8K_ATU_ADDR2_S;
+- reg[0] |= mac[3] << QCA8K_ATU_ADDR3_S;
+- reg[0] |= mac[4] << QCA8K_ATU_ADDR4_S;
+- reg[0] |= mac[5];
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+
+ /* load the array into the ARL table */
+ for (i = 0; i < 3; i++)
+@@ -372,7 +373,7 @@ qca8k_fdb_access(struct qca8k_priv *priv
+ reg |= cmd;
+ if (port >= 0) {
+ reg |= QCA8K_ATU_FUNC_PORT_EN;
+- reg |= (port & QCA8K_ATU_FUNC_PORT_M) << QCA8K_ATU_FUNC_PORT_S;
++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
+ }
+
+ /* Write the function register triggering the table access */
+@@ -454,7 +455,7 @@ qca8k_vlan_access(struct qca8k_priv *pri
+ /* Set the command and VLAN index */
+ reg = QCA8K_VTU_FUNC1_BUSY;
+ reg |= cmd;
+- reg |= vid << QCA8K_VTU_FUNC1_VID_S;
++ reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
+
+ /* Write the function register triggering the table access */
+ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
+@@ -500,13 +501,11 @@ qca8k_vlan_add(struct qca8k_priv *priv,
+ if (ret < 0)
+ goto out;
+ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+- reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port));
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+ if (untagged)
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_UNTAG <<
+- QCA8K_VTU_FUNC0_EG_MODE_S(port);
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
+ else
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG <<
+- QCA8K_VTU_FUNC0_EG_MODE_S(port);
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
+
+ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+ if (ret)
+@@ -534,15 +533,13 @@ qca8k_vlan_del(struct qca8k_priv *priv,
+ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+ if (ret < 0)
+ goto out;
+- reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port));
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT <<
+- QCA8K_VTU_FUNC0_EG_MODE_S(port);
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
+
+ /* Check if we're the last member to be removed */
+ del = true;
+ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- mask = QCA8K_VTU_FUNC0_EG_MODE_NOT;
+- mask <<= QCA8K_VTU_FUNC0_EG_MODE_S(i);
++ mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
+
+ if ((reg & mask) != mask) {
+ del = false;
+@@ -1014,7 +1011,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ mode == PHY_INTERFACE_MODE_RGMII_TXID)
+ delay = 1;
+
+- if (delay > QCA8K_MAX_DELAY) {
++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) {
+ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
+ delay = 3;
+ }
+@@ -1030,7 +1027,7 @@ qca8k_parse_port_config(struct qca8k_pri
+ mode == PHY_INTERFACE_MODE_RGMII_RXID)
+ delay = 2;
+
+- if (delay > QCA8K_MAX_DELAY) {
++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) {
+ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
+ delay = 3;
+ }
+@@ -1141,8 +1138,8 @@ qca8k_setup(struct dsa_switch *ds)
+ /* Enable QCA header mode on all cpu ports */
+ if (dsa_is_cpu_port(ds, i)) {
+ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S |
+- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S);
++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
+ if (ret) {
+ dev_err(priv->dev, "failed enabling QCA header mode");
+ return ret;
+@@ -1159,10 +1156,10 @@ qca8k_setup(struct dsa_switch *ds)
+ * for igmp, unknown, multicast and broadcast packet
+ */
+ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S |
+- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S |
+- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S |
+- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S);
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));
+ if (ret)
+ return ret;
+
+@@ -1180,8 +1177,6 @@ qca8k_setup(struct dsa_switch *ds)
+
+ /* Individual user ports get connected to CPU port only */
+ if (dsa_is_user_port(ds, i)) {
+- int shift = 16 * (i % 2);
+-
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+ QCA8K_PORT_LOOKUP_MEMBER,
+ BIT(cpu_port));
+@@ -1198,8 +1193,8 @@ qca8k_setup(struct dsa_switch *ds)
+ * default egress vid
+ */
+ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
+- 0xfff << shift,
+- QCA8K_PORT_VID_DEF << shift);
++ QCA8K_EGREES_VLAN_PORT_MASK(i),
++ QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));
+ if (ret)
+ return ret;
+
+@@ -1246,7 +1241,7 @@ qca8k_setup(struct dsa_switch *ds)
+ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+ QCA8K_PORT_HOL_CTRL1_WRED_EN;
+ qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
+- QCA8K_PORT_HOL_CTRL1_ING_BUF |
++ QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
+ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
+ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+ QCA8K_PORT_HOL_CTRL1_WRED_EN,
+@@ -1269,8 +1264,8 @@ qca8k_setup(struct dsa_switch *ds)
+ mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
+ QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
+ qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
+- QCA8K_GLOBAL_FC_GOL_XON_THRES_S |
+- QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S,
++ QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK |
++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,
+ mask);
+ }
+
+@@ -1935,11 +1930,11 @@ qca8k_port_vlan_filtering(struct dsa_swi
+
+ if (vlan_filtering) {
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+ QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
+ } else {
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
+ }
+
+@@ -1963,10 +1958,9 @@ qca8k_port_vlan_add(struct dsa_switch *d
+ }
+
+ if (pvid) {
+- int shift = 16 * (port % 2);
+-
+ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
+- 0xfff << shift, vlan->vid << shift);
++ QCA8K_EGREES_VLAN_PORT_MASK(port),
++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
+ if (ret)
+ return ret;
+
+@@ -2060,7 +2054,7 @@ static int qca8k_read_switch_id(struct q
+ if (ret < 0)
+ return -ENODEV;
+
+- id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK);
++ id = QCA8K_MASK_CTRL_DEVICE_ID(val);
+ if (id != data->id) {
+ dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
+ return -ENODEV;
+@@ -2069,7 +2063,7 @@ static int qca8k_read_switch_id(struct q
+ priv->switch_id = id;
+
+ /* Save revision to communicate to the internal PHY driver */
+- priv->switch_revision = (val & QCA8K_MASK_CTRL_REV_ID_MASK);
++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
+
+ return 0;
+ }
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -30,9 +30,9 @@
+ /* Global control registers */
+ #define QCA8K_REG_MASK_CTRL 0x000
+ #define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0)
+-#define QCA8K_MASK_CTRL_REV_ID(x) ((x) >> 0)
++#define QCA8K_MASK_CTRL_REV_ID(x) FIELD_GET(QCA8K_MASK_CTRL_REV_ID_MASK, x)
+ #define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8)
+-#define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8)
++#define QCA8K_MASK_CTRL_DEVICE_ID(x) FIELD_GET(QCA8K_MASK_CTRL_DEVICE_ID_MASK, x)
+ #define QCA8K_REG_PORT0_PAD_CTRL 0x004
+ #define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31)
+ #define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19)
+@@ -41,12 +41,11 @@
+ #define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+ #define QCA8K_PORT_PAD_RGMII_EN BIT(26)
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22)
+-#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) ((x) << 22)
++#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, x)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20)
+-#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) ((x) << 20)
++#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, x)
+ #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
+ #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
+-#define QCA8K_MAX_DELAY 3
+ #define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+ #define QCA8K_REG_PWS 0x010
+ #define QCA8K_PWS_POWER_ON_SEL BIT(31)
+@@ -68,10 +67,12 @@
+ #define QCA8K_MDIO_MASTER_READ BIT(27)
+ #define QCA8K_MDIO_MASTER_WRITE 0
+ #define QCA8K_MDIO_MASTER_SUP_PRE BIT(26)
+-#define QCA8K_MDIO_MASTER_PHY_ADDR(x) ((x) << 21)
+-#define QCA8K_MDIO_MASTER_REG_ADDR(x) ((x) << 16)
+-#define QCA8K_MDIO_MASTER_DATA(x) (x)
++#define QCA8K_MDIO_MASTER_PHY_ADDR_MASK GENMASK(25, 21)
++#define QCA8K_MDIO_MASTER_PHY_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_PHY_ADDR_MASK, x)
++#define QCA8K_MDIO_MASTER_REG_ADDR_MASK GENMASK(20, 16)
++#define QCA8K_MDIO_MASTER_REG_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_REG_ADDR_MASK, x)
+ #define QCA8K_MDIO_MASTER_DATA_MASK GENMASK(15, 0)
++#define QCA8K_MDIO_MASTER_DATA(x) FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)
+ #define QCA8K_MDIO_MASTER_MAX_PORTS 5
+ #define QCA8K_MDIO_MASTER_MAX_REG 32
+ #define QCA8K_GOL_MAC_ADDR0 0x60
+@@ -93,9 +94,7 @@
+ #define QCA8K_PORT_STATUS_FLOW_AUTO BIT(12)
+ #define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4))
+ #define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2)
+-#define QCA8K_PORT_HDR_CTRL_RX_S 2
+ #define QCA8K_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0)
+-#define QCA8K_PORT_HDR_CTRL_TX_S 0
+ #define QCA8K_PORT_HDR_CTRL_ALL 2
+ #define QCA8K_PORT_HDR_CTRL_MGMT 1
+ #define QCA8K_PORT_HDR_CTRL_NONE 0
+@@ -105,10 +104,11 @@
+ #define QCA8K_SGMII_EN_TX BIT(3)
+ #define QCA8K_SGMII_EN_SD BIT(4)
+ #define QCA8K_SGMII_CLK125M_DELAY BIT(7)
+-#define QCA8K_SGMII_MODE_CTRL_MASK (BIT(22) | BIT(23))
+-#define QCA8K_SGMII_MODE_CTRL_BASEX (0 << 22)
+-#define QCA8K_SGMII_MODE_CTRL_PHY (1 << 22)
+-#define QCA8K_SGMII_MODE_CTRL_MAC (2 << 22)
++#define QCA8K_SGMII_MODE_CTRL_MASK GENMASK(23, 22)
++#define QCA8K_SGMII_MODE_CTRL(x) FIELD_PREP(QCA8K_SGMII_MODE_CTRL_MASK, x)
++#define QCA8K_SGMII_MODE_CTRL_BASEX QCA8K_SGMII_MODE_CTRL(0x0)
++#define QCA8K_SGMII_MODE_CTRL_PHY QCA8K_SGMII_MODE_CTRL(0x1)
++#define QCA8K_SGMII_MODE_CTRL_MAC QCA8K_SGMII_MODE_CTRL(0x2)
+
+ /* MAC_PWR_SEL registers */
+ #define QCA8K_REG_MAC_PWR_SEL 0x0e4
+@@ -121,100 +121,115 @@
+
+ /* ACL registers */
+ #define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8))
+-#define QCA8K_PORT_VLAN_CVID(x) (x << 16)
+-#define QCA8K_PORT_VLAN_SVID(x) x
++#define QCA8K_PORT_VLAN_CVID_MASK GENMASK(27, 16)
++#define QCA8K_PORT_VLAN_CVID(x) FIELD_PREP(QCA8K_PORT_VLAN_CVID_MASK, x)
++#define QCA8K_PORT_VLAN_SVID_MASK GENMASK(11, 0)
++#define QCA8K_PORT_VLAN_SVID(x) FIELD_PREP(QCA8K_PORT_VLAN_SVID_MASK, x)
+ #define QCA8K_REG_PORT_VLAN_CTRL1(_i) (0x424 + (_i * 8))
+ #define QCA8K_REG_IPV4_PRI_BASE_ADDR 0x470
+ #define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474
+
+ /* Lookup registers */
+ #define QCA8K_REG_ATU_DATA0 0x600
+-#define QCA8K_ATU_ADDR2_S 24
+-#define QCA8K_ATU_ADDR3_S 16
+-#define QCA8K_ATU_ADDR4_S 8
++#define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24)
++#define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16)
++#define QCA8K_ATU_ADDR4_MASK GENMASK(15, 8)
++#define QCA8K_ATU_ADDR5_MASK GENMASK(7, 0)
+ #define QCA8K_REG_ATU_DATA1 0x604
+-#define QCA8K_ATU_PORT_M 0x7f
+-#define QCA8K_ATU_PORT_S 16
+-#define QCA8K_ATU_ADDR0_S 8
++#define QCA8K_ATU_PORT_MASK GENMASK(22, 16)
++#define QCA8K_ATU_ADDR0_MASK GENMASK(15, 8)
++#define QCA8K_ATU_ADDR1_MASK GENMASK(7, 0)
+ #define QCA8K_REG_ATU_DATA2 0x608
+-#define QCA8K_ATU_VID_M 0xfff
+-#define QCA8K_ATU_VID_S 8
+-#define QCA8K_ATU_STATUS_M 0xf
++#define QCA8K_ATU_VID_MASK GENMASK(19, 8)
++#define QCA8K_ATU_STATUS_MASK GENMASK(3, 0)
+ #define QCA8K_ATU_STATUS_STATIC 0xf
+ #define QCA8K_REG_ATU_FUNC 0x60c
+ #define QCA8K_ATU_FUNC_BUSY BIT(31)
+ #define QCA8K_ATU_FUNC_PORT_EN BIT(14)
+ #define QCA8K_ATU_FUNC_MULTI_EN BIT(13)
+ #define QCA8K_ATU_FUNC_FULL BIT(12)
+-#define QCA8K_ATU_FUNC_PORT_M 0xf
+-#define QCA8K_ATU_FUNC_PORT_S 8
++#define QCA8K_ATU_FUNC_PORT_MASK GENMASK(11, 8)
+ #define QCA8K_REG_VTU_FUNC0 0x610
+ #define QCA8K_VTU_FUNC0_VALID BIT(20)
+ #define QCA8K_VTU_FUNC0_IVL_EN BIT(19)
+-#define QCA8K_VTU_FUNC0_EG_MODE_S(_i) (4 + (_i) * 2)
+-#define QCA8K_VTU_FUNC0_EG_MODE_MASK 3
+-#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD 0
+-#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG 1
+-#define QCA8K_VTU_FUNC0_EG_MODE_TAG 2
+-#define QCA8K_VTU_FUNC0_EG_MODE_NOT 3
++/* QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(17, 4)
++ * It does contain VLAN_MODE for each port [5:4] for port0,
++ * [7:6] for port1 ... [17:16] for port6. Use virtual port
++ * define to handle this.
++ */
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i) (4 + (_i) * 2)
++#define QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(1, 0)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(_i) (GENMASK(1, 0) << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x0)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNMOD(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNMOD << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x1)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNTAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_TAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x2)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_TAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_NOT FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x3)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(_i) (QCA8K_VTU_FUNC0_EG_MODE_NOT << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+ #define QCA8K_REG_VTU_FUNC1 0x614
+ #define QCA8K_VTU_FUNC1_BUSY BIT(31)
+-#define QCA8K_VTU_FUNC1_VID_S 16
++#define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16)
+ #define QCA8K_VTU_FUNC1_FULL BIT(4)
+ #define QCA8K_REG_GLOBAL_FW_CTRL0 0x620
+ #define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10)
+ #define QCA8K_REG_GLOBAL_FW_CTRL1 0x624
+-#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S 24
+-#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_S 16
+-#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_S 8
+-#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_S 0
++#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24)
++#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16)
++#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK GENMASK(14, 8)
++#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK GENMASK(6, 0)
+ #define QCA8K_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc)
+ #define QCA8K_PORT_LOOKUP_MEMBER GENMASK(6, 0)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE GENMASK(9, 8)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE (0 << 8)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK (1 << 8)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK (2 << 8)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE (3 << 8)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_MASK GENMASK(9, 8)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE QCA8K_PORT_LOOKUP_VLAN_MODE(0x0)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK QCA8K_PORT_LOOKUP_VLAN_MODE(0x1)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK QCA8K_PORT_LOOKUP_VLAN_MODE(0x2)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE QCA8K_PORT_LOOKUP_VLAN_MODE(0x3)
+ #define QCA8K_PORT_LOOKUP_STATE_MASK GENMASK(18, 16)
+-#define QCA8K_PORT_LOOKUP_STATE_DISABLED (0 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE_BLOCKING (1 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE_LISTENING (2 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE_LEARNING (3 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE_FORWARD (4 << 16)
+-#define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16)
++#define QCA8K_PORT_LOOKUP_STATE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_STATE_MASK, x)
++#define QCA8K_PORT_LOOKUP_STATE_DISABLED QCA8K_PORT_LOOKUP_STATE(0x0)
++#define QCA8K_PORT_LOOKUP_STATE_BLOCKING QCA8K_PORT_LOOKUP_STATE(0x1)
++#define QCA8K_PORT_LOOKUP_STATE_LISTENING QCA8K_PORT_LOOKUP_STATE(0x2)
++#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
++#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
+ #define QCA8K_PORT_LOOKUP_LEARN BIT(20)
+
+ #define QCA8K_REG_GLOBAL_FC_THRESH 0x800
+-#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) ((x) << 16)
+-#define QCA8K_GLOBAL_FC_GOL_XON_THRES_S GENMASK(24, 16)
+-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) ((x) << 0)
+-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S GENMASK(8, 0)
++#define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16)
++#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x)
++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK GENMASK(8, 0)
++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, x)
+
+ #define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF GENMASK(3, 0)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) ((x) << 0)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF GENMASK(7, 4)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) ((x) << 4)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF GENMASK(11, 8)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) ((x) << 8)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF GENMASK(15, 12)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) ((x) << 12)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF GENMASK(19, 16)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) ((x) << 16)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF GENMASK(23, 20)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) ((x) << 20)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF GENMASK(29, 24)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) ((x) << 24)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK GENMASK(3, 0)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK GENMASK(7, 4)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK GENMASK(11, 8)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK GENMASK(15, 12)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK GENMASK(19, 16)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK GENMASK(23, 20)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK GENMASK(29, 24)
++#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK, x)
+
+ #define QCA8K_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8)
+-#define QCA8K_PORT_HOL_CTRL1_ING_BUF GENMASK(3, 0)
+-#define QCA8K_PORT_HOL_CTRL1_ING(x) ((x) << 0)
++#define QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK GENMASK(3, 0)
++#define QCA8K_PORT_HOL_CTRL1_ING(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK, x)
+ #define QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN BIT(6)
+ #define QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN BIT(7)
+ #define QCA8K_PORT_HOL_CTRL1_WRED_EN BIT(8)
+ #define QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16)
+
+ /* Pkt edit registers */
++#define QCA8K_EGREES_VLAN_PORT_SHIFT(_i) (16 * ((_i) % 2))
++#define QCA8K_EGREES_VLAN_PORT_MASK(_i) (GENMASK(11, 0) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
++#define QCA8K_EGREES_VLAN_PORT(_i, x) ((x) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
+ #define QCA8K_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2)))
+
+ /* L3 registers */
diff --git a/target/linux/generic/backport-6.1/755-v5.17-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch b/target/linux/generic/backport-6.1/755-v5.17-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch
new file mode 100644
index 0000000000..8c39b8ea29
--- /dev/null
+++ b/target/linux/generic/backport-6.1/755-v5.17-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch
@@ -0,0 +1,25 @@
+From 994c28b6f971fa5db8ae977daea37eee87d93d51 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:42 +0100
+Subject: net: dsa: qca8k: remove extra mutex_init in qca8k_setup
+
+Mutex is already init in sw_probe. Remove the extra init in qca8k_setup.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1086,8 +1086,6 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
+- mutex_init(&priv->reg_mutex);
+-
+ /* Start by setting up the register mapping */
+ priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
+ &qca8k_regmap_config);
diff --git a/target/linux/generic/backport-6.1/756-v5.17-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch b/target/linux/generic/backport-6.1/756-v5.17-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch
new file mode 100644
index 0000000000..44d938c53e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/756-v5.17-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch
@@ -0,0 +1,46 @@
+From 36b8af12f424e7a7f60a935c60a0fd4aa0822378 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:43 +0100
+Subject: net: dsa: qca8k: move regmap init in probe and set it mandatory
+
+In preparation for regmap conversion, move regmap init in the probe
+function and make it mandatory as any read/write/rmw operation will be
+converted to regmap API.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1086,12 +1086,6 @@ qca8k_setup(struct dsa_switch *ds)
+ if (ret)
+ return ret;
+
+- /* Start by setting up the register mapping */
+- priv->regmap = devm_regmap_init(ds->dev, NULL, priv,
+- &qca8k_regmap_config);
+- if (IS_ERR(priv->regmap))
+- dev_warn(priv->dev, "regmap initialization failed");
+-
+ ret = qca8k_setup_mdio_bus(priv);
+ if (ret)
+ return ret;
+@@ -2096,6 +2090,14 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ }
+
++ /* Start by setting up the register mapping */
++ priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv,
++ &qca8k_regmap_config);
++ if (IS_ERR(priv->regmap)) {
++ dev_err(priv->dev, "regmap initialization failed");
++ return PTR_ERR(priv->regmap);
++ }
++
+ /* Check the detected switch id */
+ ret = qca8k_read_switch_id(priv);
+ if (ret)
diff --git a/target/linux/generic/backport-6.1/757-v5.17-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch b/target/linux/generic/backport-6.1/757-v5.17-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch
new file mode 100644
index 0000000000..4ca9c8ba41
--- /dev/null
+++ b/target/linux/generic/backport-6.1/757-v5.17-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch
@@ -0,0 +1,249 @@
+From 8b5f3f29a81a71934d004e21a1292c1148b05926 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:44 +0100
+Subject: net: dsa: qca8k: initial conversion to regmap helper
+
+Convert any qca8k set/clear/pool to regmap helper and add
+missing config to regmap_config struct.
+Read/write/rmw operation are reworked to use the regmap helper
+internally to keep the delta of this patch low. These additional
+function will then be dropped when the code split will be proposed.
+
+Ipq40xx SoC have the internal switch based on the qca8k regmap but use
+mmio for read/write/rmw operation instead of mdio.
+In preparation for the support of this internal switch, convert the
+driver to regmap API to later split the driver to common and specific
+code. The overhead introduced by the use of regamp API is marginal as the
+internal mdio will bypass it by using its direct access and regmap will be
+used only by configuration functions or fdb access.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 107 +++++++++++++++++++++---------------------------
+ 1 file changed, 47 insertions(+), 60 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -10,6 +10,7 @@
+ #include <linux/phy.h>
+ #include <linux/netdevice.h>
+ #include <linux/bitfield.h>
++#include <linux/regmap.h>
+ #include <net/dsa.h>
+ #include <linux/of_net.h>
+ #include <linux/of_mdio.h>
+@@ -152,6 +153,25 @@ qca8k_set_page(struct mii_bus *bus, u16
+ static int
+ qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
+ {
++ return regmap_read(priv->regmap, reg, val);
++}
++
++static int
++qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
++{
++ return regmap_write(priv->regmap, reg, val);
++}
++
++static int
++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++{
++ return regmap_update_bits(priv->regmap, reg, mask, write_val);
++}
++
++static int
++qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ int ret;
+@@ -172,8 +192,9 @@ exit:
+ }
+
+ static int
+-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
++qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
+ {
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ int ret;
+@@ -194,8 +215,9 @@ exit:
+ }
+
+ static int
+-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
+ {
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 val;
+@@ -223,34 +245,6 @@ exit:
+ return ret;
+ }
+
+-static int
+-qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val)
+-{
+- return qca8k_rmw(priv, reg, 0, val);
+-}
+-
+-static int
+-qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val)
+-{
+- return qca8k_rmw(priv, reg, val, 0);
+-}
+-
+-static int
+-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+-
+- return qca8k_read(priv, reg, val);
+-}
+-
+-static int
+-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+-
+- return qca8k_write(priv, reg, val);
+-}
+-
+ static const struct regmap_range qca8k_readable_ranges[] = {
+ regmap_reg_range(0x0000, 0x00e4), /* Global control */
+ regmap_reg_range(0x0100, 0x0168), /* EEE control */
+@@ -282,26 +276,19 @@ static struct regmap_config qca8k_regmap
+ .max_register = 0x16ac, /* end MIB - Port6 range */
+ .reg_read = qca8k_regmap_read,
+ .reg_write = qca8k_regmap_write,
++ .reg_update_bits = qca8k_regmap_update_bits,
+ .rd_table = &qca8k_readable_table,
++ .disable_locking = true, /* Locking is handled by qca8k read/write */
++ .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
+ };
+
+ static int
+ qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+ {
+- int ret, ret1;
+ u32 val;
+
+- ret = read_poll_timeout(qca8k_read, ret1, !(val & mask),
+- 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- priv, reg, &val);
+-
+- /* Check if qca8k_read has failed for a different reason
+- * before returning -ETIMEDOUT
+- */
+- if (ret < 0 && ret1 < 0)
+- return ret1;
+-
+- return ret;
++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
+ }
+
+ static int
+@@ -568,7 +555,7 @@ qca8k_mib_init(struct qca8k_priv *priv)
+ int ret;
+
+ mutex_lock(&priv->reg_mutex);
+- ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
+ if (ret)
+ goto exit;
+
+@@ -576,7 +563,7 @@ qca8k_mib_init(struct qca8k_priv *priv)
+ if (ret)
+ goto exit;
+
+- ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+ if (ret)
+ goto exit;
+
+@@ -597,9 +584,9 @@ qca8k_port_set_status(struct qca8k_priv
+ mask |= QCA8K_PORT_STATUS_LINK_AUTO;
+
+ if (enable)
+- qca8k_reg_set(priv, QCA8K_REG_PORT_STATUS(port), mask);
++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+ else
+- qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port), mask);
++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+ }
+
+ static u32
+@@ -861,8 +848,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ * a dt-overlay and driver reload changed the configuration
+ */
+
+- return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_EN);
++ return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_EN);
+ }
+
+ /* Check if the devicetree declare the port:phy mapping */
+@@ -1099,16 +1086,16 @@ qca8k_setup(struct dsa_switch *ds)
+ return ret;
+
+ /* Make sure MAC06 is disabled */
+- ret = qca8k_reg_clear(priv, QCA8K_REG_PORT0_PAD_CTRL,
+- QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
++ ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL,
++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
+ if (ret) {
+ dev_err(priv->dev, "failed disabling MAC06 exchange");
+ return ret;
+ }
+
+ /* Enable CPU Port */
+- ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+ if (ret) {
+ dev_err(priv->dev, "failed enabling CPU port");
+ return ret;
+@@ -1176,8 +1163,8 @@ qca8k_setup(struct dsa_switch *ds)
+ return ret;
+
+ /* Enable ARP Auto-learning by default */
+- ret = qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_LEARN);
++ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_LEARN);
+ if (ret)
+ return ret;
+
+@@ -1745,9 +1732,9 @@ qca8k_port_bridge_join(struct dsa_switch
+ /* Add this port to the portvlan mask of the other ports
+ * in the bridge
+ */
+- ret = qca8k_reg_set(priv,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
++ ret = regmap_set_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
+ if (ret)
+ return ret;
+ if (i != port)
+@@ -1777,9 +1764,9 @@ qca8k_port_bridge_leave(struct dsa_switc
+ /* Remove this port to the portvlan mask of the other ports
+ * in the bridge
+ */
+- qca8k_reg_clear(priv,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
++ regmap_clear_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
+ }
+
+ /* Set the cpu port to be the only one in the portvlan mask of
diff --git a/target/linux/generic/backport-6.1/758-v5.17-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch b/target/linux/generic/backport-6.1/758-v5.17-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch
new file mode 100644
index 0000000000..c8c050933b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/758-v5.17-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch
@@ -0,0 +1,120 @@
+From c126f118b330ccf0db0dda4a4bd6c729865a205f Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:45 +0100
+Subject: net: dsa: qca8k: add additional MIB counter and make it dynamic
+
+We are currently missing 2 additionals MIB counter present in QCA833x
+switch.
+QC832x switch have 39 MIB counter and QCA833X have 41 MIB counter.
+Add the additional MIB counter and rework the MIB function to print the
+correct supported counter from the match_data struct.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 23 ++++++++++++++++++++---
+ drivers/net/dsa/qca8k.h | 4 ++++
+ 2 files changed, 24 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -70,6 +70,8 @@ static const struct qca8k_mib_desc ar832
+ MIB_DESC(1, 0x9c, "TxExcDefer"),
+ MIB_DESC(1, 0xa0, "TxDefer"),
+ MIB_DESC(1, 0xa4, "TxLateCol"),
++ MIB_DESC(1, 0xa8, "RXUnicast"),
++ MIB_DESC(1, 0xac, "TXUnicast"),
+ };
+
+ /* The 32bit switch registers are accessed indirectly. To achieve this we need
+@@ -1605,12 +1607,16 @@ qca8k_phylink_mac_link_up(struct dsa_swi
+ static void
+ qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
+ {
++ const struct qca8k_match_data *match_data;
++ struct qca8k_priv *priv = ds->priv;
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+- for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++)
++ match_data = of_device_get_match_data(priv->dev);
++
++ for (i = 0; i < match_data->mib_count; i++)
+ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
+ ETH_GSTRING_LEN);
+ }
+@@ -1620,12 +1626,15 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ uint64_t *data)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ const struct qca8k_match_data *match_data;
+ const struct qca8k_mib_desc *mib;
+ u32 reg, i, val;
+ u32 hi = 0;
+ int ret;
+
+- for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) {
++ match_data = of_device_get_match_data(priv->dev);
++
++ for (i = 0; i < match_data->mib_count; i++) {
+ mib = &ar8327_mib[i];
+ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+
+@@ -1648,10 +1657,15 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ static int
+ qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
+ {
++ const struct qca8k_match_data *match_data;
++ struct qca8k_priv *priv = ds->priv;
++
+ if (sset != ETH_SS_STATS)
+ return 0;
+
+- return ARRAY_SIZE(ar8327_mib);
++ match_data = of_device_get_match_data(priv->dev);
++
++ return match_data->mib_count;
+ }
+
+ static int
+@@ -2173,14 +2187,17 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ static const struct qca8k_match_data qca8327 = {
+ .id = QCA8K_ID_QCA8327,
+ .reduced_package = true,
++ .mib_count = QCA8K_QCA832X_MIB_COUNT,
+ };
+
+ static const struct qca8k_match_data qca8328 = {
+ .id = QCA8K_ID_QCA8327,
++ .mib_count = QCA8K_QCA832X_MIB_COUNT,
+ };
+
+ static const struct qca8k_match_data qca833x = {
+ .id = QCA8K_ID_QCA8337,
++ .mib_count = QCA8K_QCA833X_MIB_COUNT,
+ };
+
+ static const struct of_device_id qca8k_of_match[] = {
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -21,6 +21,9 @@
+ #define PHY_ID_QCA8337 0x004dd036
+ #define QCA8K_ID_QCA8337 0x13
+
++#define QCA8K_QCA832X_MIB_COUNT 39
++#define QCA8K_QCA833X_MIB_COUNT 41
++
+ #define QCA8K_BUSY_WAIT_TIMEOUT 2000
+
+ #define QCA8K_NUM_FDB_RECORDS 2048
+@@ -279,6 +282,7 @@ struct ar8xxx_port_status {
+ struct qca8k_match_data {
+ u8 id;
+ bool reduced_package;
++ u8 mib_count;
+ };
+
+ enum {
diff --git a/target/linux/generic/backport-6.1/759-v5.17-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch b/target/linux/generic/backport-6.1/759-v5.17-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch
new file mode 100644
index 0000000000..8ad7ab472d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/759-v5.17-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch
@@ -0,0 +1,53 @@
+From 4592538bfb0d5d3c3c8a1d7071724d081412ac91 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:46 +0100
+Subject: net: dsa: qca8k: add support for port fast aging
+
+The switch supports fast aging by flushing any rule in the ARL
+table for a specific port.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 11 +++++++++++
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 12 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1790,6 +1790,16 @@ qca8k_port_bridge_leave(struct dsa_switc
+ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+ }
+
++static void
++qca8k_port_fast_age(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
++ mutex_unlock(&priv->reg_mutex);
++}
++
+ static int
+ qca8k_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+@@ -2017,6 +2027,7 @@ static const struct dsa_switch_ops qca8k
+ .port_stp_state_set = qca8k_port_stp_state_set,
+ .port_bridge_join = qca8k_port_bridge_join,
+ .port_bridge_leave = qca8k_port_bridge_leave,
++ .port_fast_age = qca8k_port_fast_age,
+ .port_fdb_add = qca8k_port_fdb_add,
+ .port_fdb_del = qca8k_port_fdb_del,
+ .port_fdb_dump = qca8k_port_fdb_dump,
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -262,6 +262,7 @@ enum qca8k_fdb_cmd {
+ QCA8K_FDB_FLUSH = 1,
+ QCA8K_FDB_LOAD = 2,
+ QCA8K_FDB_PURGE = 3,
++ QCA8K_FDB_FLUSH_PORT = 5,
+ QCA8K_FDB_NEXT = 6,
+ QCA8K_FDB_SEARCH = 7,
+ };
diff --git a/target/linux/generic/backport-6.1/760-v5.17-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch b/target/linux/generic/backport-6.1/760-v5.17-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch
new file mode 100644
index 0000000000..659e482405
--- /dev/null
+++ b/target/linux/generic/backport-6.1/760-v5.17-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch
@@ -0,0 +1,78 @@
+From 6a3bdc5209f45d2af83aa92433ab6e5cf2297aa4 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:47 +0100
+Subject: net: dsa: qca8k: add set_ageing_time support
+
+qca8k support setting ageing time in step of 7s. Add support for it and
+set the max value accepted of 7645m.
+Documentation talks about support for 10000m but that values doesn't
+make sense as the value doesn't match the max value in the reg.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 25 +++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 3 +++
+ 2 files changed, 28 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1261,6 +1261,10 @@ qca8k_setup(struct dsa_switch *ds)
+ /* We don't have interrupts for link changes, so we need to poll */
+ ds->pcs_poll = true;
+
++ /* Set min a max ageing value supported */
++ ds->ageing_time_min = 7000;
++ ds->ageing_time_max = 458745000;
++
+ return 0;
+ }
+
+@@ -1801,6 +1805,26 @@ qca8k_port_fast_age(struct dsa_switch *d
+ }
+
+ static int
++qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
++{
++ struct qca8k_priv *priv = ds->priv;
++ unsigned int secs = msecs / 1000;
++ u32 val;
++
++ /* AGE_TIME reg is set in 7s step */
++ val = secs / 7;
++
++ /* Handle case with 0 as val to NOT disable
++ * learning
++ */
++ if (!val)
++ val = 1;
++
++ return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
++ QCA8K_ATU_AGE_TIME(val));
++}
++
++static int
+ qca8k_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+ {
+@@ -2018,6 +2042,7 @@ static const struct dsa_switch_ops qca8k
+ .get_strings = qca8k_get_strings,
+ .get_ethtool_stats = qca8k_get_ethtool_stats,
+ .get_sset_count = qca8k_get_sset_count,
++ .set_ageing_time = qca8k_set_ageing_time,
+ .get_mac_eee = qca8k_get_mac_eee,
+ .set_mac_eee = qca8k_set_mac_eee,
+ .port_enable = qca8k_port_enable,
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -175,6 +175,9 @@
+ #define QCA8K_VTU_FUNC1_BUSY BIT(31)
+ #define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16)
+ #define QCA8K_VTU_FUNC1_FULL BIT(4)
++#define QCA8K_REG_ATU_CTRL 0x618
++#define QCA8K_ATU_AGE_TIME_MASK GENMASK(15, 0)
++#define QCA8K_ATU_AGE_TIME(x) FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x))
+ #define QCA8K_REG_GLOBAL_FW_CTRL0 0x620
+ #define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10)
+ #define QCA8K_REG_GLOBAL_FW_CTRL1 0x624
diff --git a/target/linux/generic/backport-6.1/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch b/target/linux/generic/backport-6.1/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch
new file mode 100644
index 0000000000..8b97939ecb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch
@@ -0,0 +1,142 @@
+From ba8f870dfa635113ce6e8095a5eb1835ecde2e9e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 22 Nov 2021 16:23:48 +0100
+Subject: net: dsa: qca8k: add support for mdb_add/del
+
+Add support for mdb add/del function. The ARL table is used to insert
+the rule. The rule will be searched, deleted and reinserted with the
+port mask updated. The function will check if the rule has to be updated
+or insert directly with no deletion of the old rule.
+If every port is removed from the port mask, the rule is removed.
+The rule is set STATIC in the ARL table (aka it doesn't age) to not be
+flushed by fast age function.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 99 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -436,6 +436,81 @@ qca8k_fdb_flush(struct qca8k_priv *priv)
+ }
+
+ static int
++qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_fdb_read(priv, &fdb);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule exist. Delete first */
++ if (!fdb.aging) {
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++ }
++
++ /* Add port to fdb portmask */
++ fdb.port_mask |= port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
++qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule doesn't exist. Why delete? */
++ if (!fdb.aging) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++
++ /* Only port in the rule is this port. Don't re insert */
++ if (fdb.port_mask == port_mask)
++ goto exit;
++
++ /* Remove port from port mask */
++ fdb.port_mask &= ~port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
+ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+ {
+ u32 reg;
+@@ -1949,6 +2024,28 @@ qca8k_port_fdb_dump(struct dsa_switch *d
+ }
+
+ static int
++qca8k_port_mdb_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
++}
++
++static int
++qca8k_port_mdb_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
++}
++
++static int
+ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+ struct netlink_ext_ack *extack)
+ {
+@@ -2056,6 +2153,8 @@ static const struct dsa_switch_ops qca8k
+ .port_fdb_add = qca8k_port_fdb_add,
+ .port_fdb_del = qca8k_port_fdb_del,
+ .port_fdb_dump = qca8k_port_fdb_dump,
++ .port_mdb_add = qca8k_port_mdb_add,
++ .port_mdb_del = qca8k_port_mdb_del,
+ .port_vlan_filtering = qca8k_port_vlan_filtering,
+ .port_vlan_add = qca8k_port_vlan_add,
+ .port_vlan_del = qca8k_port_vlan_del,
diff --git a/target/linux/generic/backport-6.1/762-v5.17-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch b/target/linux/generic/backport-6.1/762-v5.17-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch
new file mode 100644
index 0000000000..dc5a22935f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/762-v5.17-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch
@@ -0,0 +1,155 @@
+From 2c1bdbc7e7560d7de754cad277d968d56bb1899e Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 23 Nov 2021 03:59:10 +0100
+Subject: net: dsa: qca8k: add support for mirror mode
+
+The switch supports mirror mode. Only one port can set as mirror port and
+every other port can set to both ingress and egress mode. The mirror
+port is disabled and reverted to normal operation once every port is
+removed from sending packet to it.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 4 +++
+ 2 files changed, 99 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -2046,6 +2046,99 @@ qca8k_port_mdb_del(struct dsa_switch *ds
+ }
+
+ static int
++qca8k_port_mirror_add(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror,
++ bool ingress)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int monitor_port, ret;
++ u32 reg, val;
++
++ /* Check for existent entry */
++ if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
++ return -EEXIST;
++
++ ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
++ if (ret)
++ return ret;
++
++ /* QCA83xx can have only one port set to mirror mode.
++ * Check that the correct port is requested and return error otherwise.
++ * When no mirror port is set, the values is set to 0xF
++ */
++ monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
++ return -EEXIST;
++
++ /* Set the monitor port */
++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
++ mirror->to_local_port);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (ret)
++ return ret;
++
++ if (ingress) {
++ reg = QCA8K_PORT_LOOKUP_CTRL(port);
++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
++ } else {
++ reg = QCA8K_REG_PORT_HOL_CTRL1(port);
++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
++ }
++
++ ret = regmap_update_bits(priv->regmap, reg, val, val);
++ if (ret)
++ return ret;
++
++ /* Track mirror port for tx and rx to decide when the
++ * mirror port has to be disabled.
++ */
++ if (ingress)
++ priv->mirror_rx |= BIT(port);
++ else
++ priv->mirror_tx |= BIT(port);
++
++ return 0;
++}
++
++static void
++qca8k_port_mirror_del(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror)
++{
++ struct qca8k_priv *priv = ds->priv;
++ u32 reg, val;
++ int ret;
++
++ if (mirror->ingress) {
++ reg = QCA8K_PORT_LOOKUP_CTRL(port);
++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
++ } else {
++ reg = QCA8K_REG_PORT_HOL_CTRL1(port);
++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
++ }
++
++ ret = regmap_clear_bits(priv->regmap, reg, val);
++ if (ret)
++ goto err;
++
++ if (mirror->ingress)
++ priv->mirror_rx &= ~BIT(port);
++ else
++ priv->mirror_tx &= ~BIT(port);
++
++ /* No port set to send packet to mirror port. Disable mirror port */
++ if (!priv->mirror_rx && !priv->mirror_tx) {
++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (ret)
++ goto err;
++ }
++err:
++ dev_err(priv->dev, "Failed to del mirror port from %d", port);
++}
++
++static int
+ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+ struct netlink_ext_ack *extack)
+ {
+@@ -2155,6 +2248,8 @@ static const struct dsa_switch_ops qca8k
+ .port_fdb_dump = qca8k_port_fdb_dump,
+ .port_mdb_add = qca8k_port_mdb_add,
+ .port_mdb_del = qca8k_port_mdb_del,
++ .port_mirror_add = qca8k_port_mirror_add,
++ .port_mirror_del = qca8k_port_mirror_del,
+ .port_vlan_filtering = qca8k_port_vlan_filtering,
+ .port_vlan_add = qca8k_port_vlan_add,
+ .port_vlan_del = qca8k_port_vlan_del,
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -180,6 +180,7 @@
+ #define QCA8K_ATU_AGE_TIME(x) FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x))
+ #define QCA8K_REG_GLOBAL_FW_CTRL0 0x620
+ #define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10)
++#define QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM GENMASK(7, 4)
+ #define QCA8K_REG_GLOBAL_FW_CTRL1 0x624
+ #define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24)
+ #define QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16)
+@@ -201,6 +202,7 @@
+ #define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
+ #define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
+ #define QCA8K_PORT_LOOKUP_LEARN BIT(20)
++#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
+
+ #define QCA8K_REG_GLOBAL_FC_THRESH 0x800
+ #define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16)
+@@ -305,6 +307,8 @@ struct qca8k_ports_config {
+ struct qca8k_priv {
+ u8 switch_id;
+ u8 switch_revision;
++ u8 mirror_rx;
++ u8 mirror_tx;
+ bool legacy_phy_port_mapping;
+ struct qca8k_ports_config ports_config;
+ struct regmap *regmap;
diff --git a/target/linux/generic/backport-6.1/763-v5.17-net-next-net-dsa-qca8k-add-LAG-support.patch b/target/linux/generic/backport-6.1/763-v5.17-net-next-net-dsa-qca8k-add-LAG-support.patch
new file mode 100644
index 0000000000..b53f1288d5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/763-v5.17-net-next-net-dsa-qca8k-add-LAG-support.patch
@@ -0,0 +1,288 @@
+From def975307c01191b6f0170048c3724b0ed3348af Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 23 Nov 2021 03:59:11 +0100
+Subject: net: dsa: qca8k: add LAG support
+
+Add LAG support to this switch. In Documentation this is described as
+trunk mode. A max of 4 LAGs are supported and each can support up to 4
+port. The current tx mode supported is Hash mode with both L2 and L2+3
+mode.
+When no port are present in the trunk, the trunk is disabled in the
+switch.
+When a port is disconnected, the traffic is redirected to the other
+available port.
+The hash mode is global and each LAG require to have the same hash mode
+set. To change the hash mode when multiple LAG are configured, it's
+required to remove each LAG and set the desired hash mode to the last.
+An error is printed when it's asked to set a not supported hadh mode.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 33 +++++++++
+ 2 files changed, 210 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1340,6 +1340,9 @@ qca8k_setup(struct dsa_switch *ds)
+ ds->ageing_time_min = 7000;
+ ds->ageing_time_max = 458745000;
+
++ /* Set max number of LAGs supported */
++ ds->num_lag_ids = QCA8K_NUM_LAGS;
++
+ return 0;
+ }
+
+@@ -2226,6 +2229,178 @@ qca8k_get_tag_protocol(struct dsa_switch
+ return DSA_TAG_PROTO_QCA;
+ }
+
++static bool
++qca8k_lag_can_offload(struct dsa_switch *ds,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct dsa_port *dp;
++ int id, members = 0;
++
++ id = dsa_lag_id(ds->dst, lag);
++ if (id < 0 || id >= ds->num_lag_ids)
++ return false;
++
++ dsa_lag_foreach_port(dp, ds->dst, lag)
++ /* Includes the port joining the LAG */
++ members++;
++
++ if (members > QCA8K_NUM_PORTS_FOR_LAG)
++ return false;
++
++ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
++ return false;
++
++ if (info->hash_type != NETDEV_LAG_HASH_L2 ||
++ info->hash_type != NETDEV_LAG_HASH_L23)
++ return false;
++
++ return true;
++}
++
++static int
++qca8k_lag_setup_hash(struct dsa_switch *ds,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct qca8k_priv *priv = ds->priv;
++ bool unique_lag = true;
++ int i, id;
++ u32 hash;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ switch (info->hash_type) {
++ case NETDEV_LAG_HASH_L23:
++ hash |= QCA8K_TRUNK_HASH_SIP_EN;
++ hash |= QCA8K_TRUNK_HASH_DIP_EN;
++ fallthrough;
++ case NETDEV_LAG_HASH_L2:
++ hash |= QCA8K_TRUNK_HASH_SA_EN;
++ hash |= QCA8K_TRUNK_HASH_DA_EN;
++ break;
++ default: /* We should NEVER reach this */
++ return -EOPNOTSUPP;
++ }
++
++ /* Check if we are the unique configured LAG */
++ dsa_lags_foreach_id(i, ds->dst)
++ if (i != id && dsa_lag_dev(ds->dst, i)) {
++ unique_lag = false;
++ break;
++ }
++
++ /* Hash Mode is global. Make sure the same Hash Mode
++ * is set to all the 4 possible lag.
++ * If we are the unique LAG we can set whatever hash
++ * mode we want.
++ * To change hash mode it's needed to remove all LAG
++ * and change the mode with the latest.
++ */
++ if (unique_lag) {
++ priv->lag_hash_mode = hash;
++ } else if (priv->lag_hash_mode != hash) {
++ netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n");
++ return -EOPNOTSUPP;
++ }
++
++ return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
++ QCA8K_TRUNK_HASH_MASK, hash);
++}
++
++static int
++qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
++ struct net_device *lag, bool delete)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret, id, i;
++ u32 val;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ /* Read current port member */
++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
++ if (ret)
++ return ret;
++
++ /* Shift val to the correct trunk */
++ val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
++ val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
++ if (delete)
++ val &= ~BIT(port);
++ else
++ val |= BIT(port);
++
++ /* Update port member. With empty portmap disable trunk */
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
++ QCA8K_REG_GOL_TRUNK_MEMBER(id) |
++ QCA8K_REG_GOL_TRUNK_EN(id),
++ !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
++ val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
++
++ /* Search empty member if adding or port on deleting */
++ for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
++ if (ret)
++ return ret;
++
++ val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
++
++ if (delete) {
++ /* If port flagged to be disabled assume this member is
++ * empty
++ */
++ if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
++ continue;
++
++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
++ if (val != port)
++ continue;
++ } else {
++ /* If port flagged to be enabled assume this member is
++ * already set
++ */
++ if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
++ continue;
++ }
++
++ /* We have found the member to add/remove */
++ break;
++ }
++
++ /* Set port in the correct port mask or disable port if in delete mode */
++ return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
++ !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
++ port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
++}
++
++static int
++qca8k_port_lag_join(struct dsa_switch *ds, int port,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ int ret;
++
++ if (!qca8k_lag_can_offload(ds, lag, info))
++ return -EOPNOTSUPP;
++
++ ret = qca8k_lag_setup_hash(ds, lag, info);
++ if (ret)
++ return ret;
++
++ return qca8k_lag_refresh_portmap(ds, port, lag, false);
++}
++
++static int
++qca8k_port_lag_leave(struct dsa_switch *ds, int port,
++ struct net_device *lag)
++{
++ return qca8k_lag_refresh_portmap(ds, port, lag, true);
++}
++
+ static const struct dsa_switch_ops qca8k_switch_ops = {
+ .get_tag_protocol = qca8k_get_tag_protocol,
+ .setup = qca8k_setup,
+@@ -2259,6 +2434,8 @@ static const struct dsa_switch_ops qca8k
+ .phylink_mac_link_down = qca8k_phylink_mac_link_down,
+ .phylink_mac_link_up = qca8k_phylink_mac_link_up,
+ .get_phy_flags = qca8k_get_phy_flags,
++ .port_lag_join = qca8k_port_lag_join,
++ .port_lag_leave = qca8k_port_lag_leave,
+ };
+
+ static int qca8k_read_switch_id(struct qca8k_priv *priv)
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -15,6 +15,8 @@
+ #define QCA8K_NUM_PORTS 7
+ #define QCA8K_NUM_CPU_PORTS 2
+ #define QCA8K_MAX_MTU 9000
++#define QCA8K_NUM_LAGS 4
++#define QCA8K_NUM_PORTS_FOR_LAG 4
+
+ #define PHY_ID_QCA8327 0x004dd034
+ #define QCA8K_ID_QCA8327 0x12
+@@ -122,6 +124,14 @@
+ #define QCA8K_REG_EEE_CTRL 0x100
+ #define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2)
+
++/* TRUNK_HASH_EN registers */
++#define QCA8K_TRUNK_HASH_EN_CTRL 0x270
++#define QCA8K_TRUNK_HASH_SIP_EN BIT(3)
++#define QCA8K_TRUNK_HASH_DIP_EN BIT(2)
++#define QCA8K_TRUNK_HASH_SA_EN BIT(1)
++#define QCA8K_TRUNK_HASH_DA_EN BIT(0)
++#define QCA8K_TRUNK_HASH_MASK GENMASK(3, 0)
++
+ /* ACL registers */
+ #define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8))
+ #define QCA8K_PORT_VLAN_CVID_MASK GENMASK(27, 16)
+@@ -204,6 +214,28 @@
+ #define QCA8K_PORT_LOOKUP_LEARN BIT(20)
+ #define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
+
++#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700
++/* 4 max trunk first
++ * first 6 bit for member bitmap
++ * 7th bit is to enable trunk port
++ */
++#define QCA8K_REG_GOL_TRUNK_SHIFT(_i) ((_i) * 8)
++#define QCA8K_REG_GOL_TRUNK_EN_MASK BIT(7)
++#define QCA8K_REG_GOL_TRUNK_EN(_i) (QCA8K_REG_GOL_TRUNK_EN_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
++#define QCA8K_REG_GOL_TRUNK_MEMBER_MASK GENMASK(6, 0)
++#define QCA8K_REG_GOL_TRUNK_MEMBER(_i) (QCA8K_REG_GOL_TRUNK_MEMBER_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
++/* 0x704 for TRUNK 0-1 --- 0x708 for TRUNK 2-3 */
++#define QCA8K_REG_GOL_TRUNK_CTRL(_i) (0x704 + (((_i) / 2) * 4))
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK GENMASK(3, 0)
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK BIT(3)
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK GENMASK(2, 0)
++#define QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i) (((_i) / 2) * 16)
++#define QCA8K_REG_GOL_MEM_ID_SHIFT(_i) ((_i) * 4)
++/* Complex shift: FIRST shift for port THEN shift for trunk */
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j) (QCA8K_REG_GOL_MEM_ID_SHIFT(_j) + QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i))
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
++
+ #define QCA8K_REG_GLOBAL_FC_THRESH 0x800
+ #define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16)
+ #define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x)
+@@ -309,6 +341,7 @@ struct qca8k_priv {
+ u8 switch_revision;
+ u8 mirror_rx;
+ u8 mirror_tx;
++ u8 lag_hash_mode;
+ bool legacy_phy_port_mapping;
+ struct qca8k_ports_config ports_config;
+ struct regmap *regmap;
diff --git a/target/linux/generic/backport-6.1/764-v5.17-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch b/target/linux/generic/backport-6.1/764-v5.17-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch
new file mode 100644
index 0000000000..7d811be11c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/764-v5.17-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch
@@ -0,0 +1,40 @@
+From 0898ca67b86e14207d4feb3f3fea8b87cec5aab1 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 23 Nov 2021 16:44:46 +0100
+Subject: net: dsa: qca8k: fix warning in LAG feature
+
+Fix warning reported by bot.
+Make sure hash is init to 0 and fix wrong logic for hash_type in
+qca8k_lag_can_offload.
+
+Reported-by: kernel test robot <lkp@intel.com>
+Fixes: def975307c01 ("net: dsa: qca8k: add LAG support")
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Link: https://lore.kernel.org/r/20211123154446.31019-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca8k.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -2251,7 +2251,7 @@ qca8k_lag_can_offload(struct dsa_switch
+ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+ return false;
+
+- if (info->hash_type != NETDEV_LAG_HASH_L2 ||
++ if (info->hash_type != NETDEV_LAG_HASH_L2 &&
+ info->hash_type != NETDEV_LAG_HASH_L23)
+ return false;
+
+@@ -2265,8 +2265,8 @@ qca8k_lag_setup_hash(struct dsa_switch *
+ {
+ struct qca8k_priv *priv = ds->priv;
+ bool unique_lag = true;
++ u32 hash = 0;
+ int i, id;
+- u32 hash;
+
+ id = dsa_lag_id(ds->dst, lag);
+
diff --git a/target/linux/generic/backport-6.1/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch b/target/linux/generic/backport-6.1/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch
new file mode 100644
index 0000000000..77cf63b809
--- /dev/null
+++ b/target/linux/generic/backport-6.1/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch
@@ -0,0 +1,52 @@
+From 904e112ad431492b34f235f59738e8312802bbf9 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Thu, 6 Jan 2022 01:11:12 +0200
+Subject: [PATCH 1/6] net: dsa: reorder PHY initialization with MTU setup in
+ slave.c
+
+In dsa_slave_create() there are 2 sections that take rtnl_lock():
+MTU change and netdev registration. They are separated by PHY
+initialization.
+
+There isn't any strict ordering requirement except for the fact that
+netdev registration should be last. Therefore, we can perform the MTU
+change a bit later, after the PHY setup. A future change will then be
+able to merge the two rtnl_lock sections into one.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/slave.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1977,13 +1977,6 @@ int dsa_slave_create(struct dsa_port *po
+ port->slave = slave_dev;
+ dsa_slave_setup_tagger(slave_dev);
+
+- rtnl_lock();
+- ret = dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN);
+- rtnl_unlock();
+- if (ret && ret != -EOPNOTSUPP)
+- dev_warn(ds->dev, "nonfatal error %d setting MTU to %d on port %d\n",
+- ret, ETH_DATA_LEN, port->index);
+-
+ netif_carrier_off(slave_dev);
+
+ ret = dsa_slave_phy_setup(slave_dev);
+@@ -1995,6 +1988,13 @@ int dsa_slave_create(struct dsa_port *po
+ }
+
+ rtnl_lock();
++ ret = dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN);
++ rtnl_unlock();
++ if (ret && ret != -EOPNOTSUPP)
++ dev_warn(ds->dev, "nonfatal error %d setting MTU to %d on port %d\n",
++ ret, ETH_DATA_LEN, port->index);
++
++ rtnl_lock();
+
+ ret = register_netdevice(slave_dev);
+ if (ret) {
diff --git a/target/linux/generic/backport-6.1/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch b/target/linux/generic/backport-6.1/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch
new file mode 100644
index 0000000000..50aa5d8f0d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch
@@ -0,0 +1,34 @@
+From e31dbd3b6aba585231cd84a87adeb22e7c6a8c19 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Thu, 6 Jan 2022 01:11:13 +0200
+Subject: [PATCH 2/6] net: dsa: merge rtnl_lock sections in dsa_slave_create
+
+Currently dsa_slave_create() has two sequences of rtnl_lock/rtnl_unlock
+in a row. Remove the rtnl_unlock() and rtnl_lock() in between, such that
+the operation can execute slighly faster.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/slave.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1988,14 +1988,12 @@ int dsa_slave_create(struct dsa_port *po
+ }
+
+ rtnl_lock();
++
+ ret = dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN);
+- rtnl_unlock();
+ if (ret && ret != -EOPNOTSUPP)
+ dev_warn(ds->dev, "nonfatal error %d setting MTU to %d on port %d\n",
+ ret, ETH_DATA_LEN, port->index);
+
+- rtnl_lock();
+-
+ ret = register_netdevice(slave_dev);
+ if (ret) {
+ netdev_err(master, "error %d registering interface %s\n",
diff --git a/target/linux/generic/backport-6.1/765-v5.17-03-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch b/target/linux/generic/backport-6.1/765-v5.17-03-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch
new file mode 100644
index 0000000000..6c7aad6928
--- /dev/null
+++ b/target/linux/generic/backport-6.1/765-v5.17-03-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch
@@ -0,0 +1,91 @@
+From a1ff94c2973c43bc1e2677ac63ebb15b1d1ff846 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Thu, 6 Jan 2022 01:11:14 +0200
+Subject: [PATCH 3/6] net: dsa: stop updating master MTU from master.c
+
+At present there are two paths for changing the MTU of the DSA master.
+
+The first is:
+
+dsa_tree_setup
+-> dsa_tree_setup_ports
+ -> dsa_port_setup
+ -> dsa_slave_create
+ -> dsa_slave_change_mtu
+ -> dev_set_mtu(master)
+
+The second is:
+
+dsa_tree_setup
+-> dsa_tree_setup_master
+ -> dsa_master_setup
+ -> dev_set_mtu(dev)
+
+So the dev_set_mtu() call from dsa_master_setup() has been effectively
+superseded by the dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN) that is
+done from dsa_slave_create() for each user port. The later function also
+updates the master MTU according to the largest user port MTU from the
+tree. Therefore, updating the master MTU through a separate code path
+isn't needed.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/master.c | 25 +------------------------
+ 1 file changed, 1 insertion(+), 24 deletions(-)
+
+--- a/net/dsa/master.c
++++ b/net/dsa/master.c
+@@ -329,28 +329,13 @@ static const struct attribute_group dsa_
+ .attrs = dsa_slave_attrs,
+ };
+
+-static void dsa_master_reset_mtu(struct net_device *dev)
+-{
+- int err;
+-
+- rtnl_lock();
+- err = dev_set_mtu(dev, ETH_DATA_LEN);
+- if (err)
+- netdev_dbg(dev,
+- "Unable to reset MTU to exclude DSA overheads\n");
+- rtnl_unlock();
+-}
+-
+ static struct lock_class_key dsa_master_addr_list_lock_key;
+
+ int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
+ {
+- const struct dsa_device_ops *tag_ops = cpu_dp->tag_ops;
+ struct dsa_switch *ds = cpu_dp->ds;
+ struct device_link *consumer_link;
+- int mtu, ret;
+-
+- mtu = ETH_DATA_LEN + dsa_tag_protocol_overhead(tag_ops);
++ int ret;
+
+ /* The DSA master must use SET_NETDEV_DEV for this to work. */
+ consumer_link = device_link_add(ds->dev, dev->dev.parent,
+@@ -360,13 +345,6 @@ int dsa_master_setup(struct net_device *
+ "Failed to create a device link to DSA switch %s\n",
+ dev_name(ds->dev));
+
+- rtnl_lock();
+- ret = dev_set_mtu(dev, mtu);
+- rtnl_unlock();
+- if (ret)
+- netdev_warn(dev, "error %d setting MTU to %d to include DSA overhead\n",
+- ret, mtu);
+-
+ /* If we use a tagging format that doesn't have an ethertype
+ * field, make sure that all packets from this point on get
+ * sent to the tag format's receive function.
+@@ -404,7 +382,6 @@ void dsa_master_teardown(struct net_devi
+ sysfs_remove_group(&dev->dev.kobj, &dsa_group);
+ dsa_netdev_ops_set(dev, NULL);
+ dsa_master_ethtool_teardown(dev);
+- dsa_master_reset_mtu(dev);
+ dsa_master_set_promiscuity(dev, -1);
+
+ dev->dsa_ptr = NULL;
diff --git a/target/linux/generic/backport-6.1/765-v5.17-04-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch b/target/linux/generic/backport-6.1/765-v5.17-04-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch
new file mode 100644
index 0000000000..e331226fc4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/765-v5.17-04-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch
@@ -0,0 +1,78 @@
+From c146f9bc195a9dc3ad7fd000a14540e7c9df952d Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Thu, 6 Jan 2022 01:11:15 +0200
+Subject: [PATCH 4/6] net: dsa: hold rtnl_mutex when calling
+ dsa_master_{setup,teardown}
+
+DSA needs to simulate master tracking events when a binding is first
+with a DSA master established and torn down, in order to give drivers
+the simplifying guarantee that ->master_state_change calls are made
+only when the master's readiness state to pass traffic changes.
+master_state_change() provide a operational bool that DSA driver can use
+to understand if DSA master is operational or not.
+To avoid races, we need to block the reception of
+NETDEV_UP/NETDEV_CHANGE/NETDEV_GOING_DOWN events in the netdev notifier
+chain while we are changing the master's dev->dsa_ptr (this changes what
+netdev_uses_dsa(dev) reports).
+
+The dsa_master_setup() and dsa_master_teardown() functions optionally
+require the rtnl_mutex to be held, if the tagger needs the master to be
+promiscuous, these functions call dev_set_promiscuity(). Move the
+rtnl_lock() from that function and make it top-level.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/dsa2.c | 8 ++++++++
+ net/dsa/master.c | 4 ++--
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+--- a/net/dsa/dsa2.c
++++ b/net/dsa/dsa2.c
+@@ -1034,6 +1034,8 @@ static int dsa_tree_setup_master(struct
+ struct dsa_port *dp;
+ int err;
+
++ rtnl_lock();
++
+ list_for_each_entry(dp, &dst->ports, list) {
+ if (dsa_port_is_cpu(dp)) {
+ err = dsa_master_setup(dp->master, dp);
+@@ -1042,6 +1044,8 @@ static int dsa_tree_setup_master(struct
+ }
+ }
+
++ rtnl_unlock();
++
+ return 0;
+ }
+
+@@ -1049,9 +1053,13 @@ static void dsa_tree_teardown_master(str
+ {
+ struct dsa_port *dp;
+
++ rtnl_lock();
++
+ list_for_each_entry(dp, &dst->ports, list)
+ if (dsa_port_is_cpu(dp))
+ dsa_master_teardown(dp->master);
++
++ rtnl_unlock();
+ }
+
+ static int dsa_tree_setup_lags(struct dsa_switch_tree *dst)
+--- a/net/dsa/master.c
++++ b/net/dsa/master.c
+@@ -266,9 +266,9 @@ static void dsa_master_set_promiscuity(s
+ if (!ops->promisc_on_master)
+ return;
+
+- rtnl_lock();
++ ASSERT_RTNL();
++
+ dev_set_promiscuity(dev, inc);
+- rtnl_unlock();
+ }
+
+ static ssize_t tagging_show(struct device *d, struct device_attribute *attr,
diff --git a/target/linux/generic/backport-6.1/765-v5.17-05-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch b/target/linux/generic/backport-6.1/765-v5.17-05-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch
new file mode 100644
index 0000000000..e6472c61da
--- /dev/null
+++ b/target/linux/generic/backport-6.1/765-v5.17-05-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch
@@ -0,0 +1,118 @@
+From 1e3f407f3cacc5dcfe27166c412ed9bc263d82bf Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Thu, 6 Jan 2022 01:11:16 +0200
+Subject: [PATCH 5/6] net: dsa: first set up shared ports, then non-shared
+ ports
+
+After commit a57d8c217aad ("net: dsa: flush switchdev workqueue before
+tearing down CPU/DSA ports"), the port setup and teardown procedure
+became asymmetric.
+
+The fact of the matter is that user ports need the shared ports to be up
+before they can be used for CPU-initiated termination. And since we
+register net devices for the user ports, those won't be functional until
+we also call the setup for the shared (CPU, DSA) ports. But we may do
+that later, depending on the port numbering scheme of the hardware we
+are dealing with.
+
+It just makes sense that all shared ports are brought up before any user
+port is. I can't pinpoint any issue due to the current behavior, but
+let's change it nonetheless, for consistency's sake.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/dsa2.c | 50 +++++++++++++++++++++++++++++++++++++-------------
+ 1 file changed, 37 insertions(+), 13 deletions(-)
+
+--- a/net/dsa/dsa2.c
++++ b/net/dsa/dsa2.c
+@@ -999,23 +999,28 @@ static void dsa_tree_teardown_switches(s
+ dsa_switch_teardown(dp->ds);
+ }
+
+-static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)
++/* Bring shared ports up first, then non-shared ports */
++static int dsa_tree_setup_ports(struct dsa_switch_tree *dst)
+ {
+ struct dsa_port *dp;
+- int err;
++ int err = 0;
+
+ list_for_each_entry(dp, &dst->ports, list) {
+- err = dsa_switch_setup(dp->ds);
+- if (err)
+- goto teardown;
++ if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp)) {
++ err = dsa_port_setup(dp);
++ if (err)
++ goto teardown;
++ }
+ }
+
+ list_for_each_entry(dp, &dst->ports, list) {
+- err = dsa_port_setup(dp);
+- if (err) {
+- err = dsa_port_reinit_as_unused(dp);
+- if (err)
+- goto teardown;
++ if (dsa_port_is_user(dp) || dsa_port_is_unused(dp)) {
++ err = dsa_port_setup(dp);
++ if (err) {
++ err = dsa_port_reinit_as_unused(dp);
++ if (err)
++ goto teardown;
++ }
+ }
+ }
+
+@@ -1024,7 +1029,21 @@ static int dsa_tree_setup_switches(struc
+ teardown:
+ dsa_tree_teardown_ports(dst);
+
+- dsa_tree_teardown_switches(dst);
++ return err;
++}
++
++static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)
++{
++ struct dsa_port *dp;
++ int err = 0;
++
++ list_for_each_entry(dp, &dst->ports, list) {
++ err = dsa_switch_setup(dp->ds);
++ if (err) {
++ dsa_tree_teardown_switches(dst);
++ break;
++ }
++ }
+
+ return err;
+ }
+@@ -1111,10 +1130,14 @@ static int dsa_tree_setup(struct dsa_swi
+ if (err)
+ goto teardown_cpu_ports;
+
+- err = dsa_tree_setup_master(dst);
++ err = dsa_tree_setup_ports(dst);
+ if (err)
+ goto teardown_switches;
+
++ err = dsa_tree_setup_master(dst);
++ if (err)
++ goto teardown_ports;
++
+ err = dsa_tree_setup_lags(dst);
+ if (err)
+ goto teardown_master;
+@@ -1127,8 +1150,9 @@ static int dsa_tree_setup(struct dsa_swi
+
+ teardown_master:
+ dsa_tree_teardown_master(dst);
+-teardown_switches:
++teardown_ports:
+ dsa_tree_teardown_ports(dst);
++teardown_switches:
+ dsa_tree_teardown_switches(dst);
+ teardown_cpu_ports:
+ dsa_tree_teardown_cpu_ports(dst);
diff --git a/target/linux/generic/backport-6.1/765-v5.17-06-net-next-net-dsa-setup-master-before-ports.patch b/target/linux/generic/backport-6.1/765-v5.17-06-net-next-net-dsa-setup-master-before-ports.patch
new file mode 100644
index 0000000000..93cad0c98a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/765-v5.17-06-net-next-net-dsa-setup-master-before-ports.patch
@@ -0,0 +1,115 @@
+From 11fd667dac315ea3f2469961f6d2869271a46cae Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Thu, 6 Jan 2022 01:11:17 +0200
+Subject: [PATCH 6/6] net: dsa: setup master before ports
+
+It is said that as soon as a network interface is registered, all its
+resources should have already been prepared, so that it is available for
+sending and receiving traffic. One of the resources needed by a DSA
+slave interface is the master.
+
+dsa_tree_setup
+-> dsa_tree_setup_ports
+ -> dsa_port_setup
+ -> dsa_slave_create
+ -> register_netdevice
+-> dsa_tree_setup_master
+ -> dsa_master_setup
+ -> sets up master->dsa_ptr, which enables reception
+
+Therefore, there is a short period of time after register_netdevice()
+during which the master isn't prepared to pass traffic to the DSA layer
+(master->dsa_ptr is checked by eth_type_trans). Same thing during
+unregistration, there is a time frame in which packets might be missed.
+
+Note that this change opens us to another race: dsa_master_find_slave()
+will get invoked potentially earlier than the slave creation, and later
+than the slave deletion. Since dp->slave starts off as a NULL pointer,
+the earlier calls aren't a problem, but the later calls are. To avoid
+use-after-free, we should zeroize dp->slave before calling
+dsa_slave_destroy().
+
+In practice I cannot really test real life improvements brought by this
+change, since in my systems, netdevice creation races with PHY autoneg
+which takes a few seconds to complete, and that masks quite a few races.
+Effects might be noticeable in a setup with fixed links all the way to
+an external system.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/dsa2.c | 23 +++++++++++++----------
+ 1 file changed, 13 insertions(+), 10 deletions(-)
+
+--- a/net/dsa/dsa2.c
++++ b/net/dsa/dsa2.c
+@@ -545,6 +545,7 @@ static void dsa_port_teardown(struct dsa
+ struct devlink_port *dlp = &dp->devlink_port;
+ struct dsa_switch *ds = dp->ds;
+ struct dsa_mac_addr *a, *tmp;
++ struct net_device *slave;
+
+ if (!dp->setup)
+ return;
+@@ -566,9 +567,11 @@ static void dsa_port_teardown(struct dsa
+ dsa_port_link_unregister_of(dp);
+ break;
+ case DSA_PORT_TYPE_USER:
+- if (dp->slave) {
+- dsa_slave_destroy(dp->slave);
++ slave = dp->slave;
++
++ if (slave) {
+ dp->slave = NULL;
++ dsa_slave_destroy(slave);
+ }
+ break;
+ }
+@@ -1130,17 +1133,17 @@ static int dsa_tree_setup(struct dsa_swi
+ if (err)
+ goto teardown_cpu_ports;
+
+- err = dsa_tree_setup_ports(dst);
++ err = dsa_tree_setup_master(dst);
+ if (err)
+ goto teardown_switches;
+
+- err = dsa_tree_setup_master(dst);
++ err = dsa_tree_setup_ports(dst);
+ if (err)
+- goto teardown_ports;
++ goto teardown_master;
+
+ err = dsa_tree_setup_lags(dst);
+ if (err)
+- goto teardown_master;
++ goto teardown_ports;
+
+ dst->setup = true;
+
+@@ -1148,10 +1151,10 @@ static int dsa_tree_setup(struct dsa_swi
+
+ return 0;
+
+-teardown_master:
+- dsa_tree_teardown_master(dst);
+ teardown_ports:
+ dsa_tree_teardown_ports(dst);
++teardown_master:
++ dsa_tree_teardown_master(dst);
+ teardown_switches:
+ dsa_tree_teardown_switches(dst);
+ teardown_cpu_ports:
+@@ -1169,10 +1172,10 @@ static void dsa_tree_teardown(struct dsa
+
+ dsa_tree_teardown_lags(dst);
+
+- dsa_tree_teardown_master(dst);
+-
+ dsa_tree_teardown_ports(dst);
+
++ dsa_tree_teardown_master(dst);
++
+ dsa_tree_teardown_switches(dst);
+
+ dsa_tree_teardown_cpu_ports(dst);
diff --git a/target/linux/generic/backport-6.1/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch b/target/linux/generic/backport-6.1/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch
new file mode 100644
index 0000000000..bffdcb2881
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch
@@ -0,0 +1,254 @@
+From 295ab96f478d0fa56393e85406f19a867e26ce22 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 2 Feb 2022 01:03:20 +0100
+Subject: [PATCH 01/16] net: dsa: provide switch operations for tracking the
+ master state
+
+Certain drivers may need to send management traffic to the switch for
+things like register access, FDB dump, etc, to accelerate what their
+slow bus (SPI, I2C, MDIO) can already do.
+
+Ethernet is faster (especially in bulk transactions) but is also more
+unreliable, since the user may decide to bring the DSA master down (or
+not bring it up), therefore severing the link between the host and the
+attached switch.
+
+Drivers needing Ethernet-based register access already should have
+fallback logic to the slow bus if the Ethernet method fails, but that
+fallback may be based on a timeout, and the I/O to the switch may slow
+down to a halt if the master is down, because every Ethernet packet will
+have to time out. The driver also doesn't have the option to turn off
+Ethernet-based I/O momentarily, because it wouldn't know when to turn it
+back on.
+
+Which is where this change comes in. By tracking NETDEV_CHANGE,
+NETDEV_UP and NETDEV_GOING_DOWN events on the DSA master, we should know
+the exact interval of time during which this interface is reliably
+available for traffic. Provide this information to switches so they can
+use it as they wish.
+
+An helper is added dsa_port_master_is_operational() to check if a master
+port is operational.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/net/dsa.h | 17 +++++++++++++++++
+ net/dsa/dsa2.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
+ net/dsa/dsa_priv.h | 13 +++++++++++++
+ net/dsa/slave.c | 32 ++++++++++++++++++++++++++++++++
+ net/dsa/switch.c | 15 +++++++++++++++
+ 5 files changed, 123 insertions(+)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -291,6 +291,10 @@ struct dsa_port {
+ struct list_head mdbs;
+
+ bool setup;
++ /* Master state bits, valid only on CPU ports */
++ u8 master_admin_up:1;
++ u8 master_oper_up:1;
++
+ };
+
+ /* TODO: ideally DSA ports would have a single dp->link_dp member,
+@@ -456,6 +460,12 @@ static inline bool dsa_port_is_unused(st
+ return dp->type == DSA_PORT_TYPE_UNUSED;
+ }
+
++static inline bool dsa_port_master_is_operational(struct dsa_port *dp)
++{
++ return dsa_port_is_cpu(dp) && dp->master_admin_up &&
++ dp->master_oper_up;
++}
++
+ static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p)
+ {
+ return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED;
+@@ -949,6 +959,13 @@ struct dsa_switch_ops {
+ int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
+ u16 flags);
+ int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
++
++ /*
++ * DSA master tracking operations
++ */
++ void (*master_state_change)(struct dsa_switch *ds,
++ const struct net_device *master,
++ bool operational);
+ };
+
+ #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \
+--- a/net/dsa/dsa2.c
++++ b/net/dsa/dsa2.c
+@@ -1275,6 +1275,52 @@ out_unlock:
+ return err;
+ }
+
++static void dsa_tree_master_state_change(struct dsa_switch_tree *dst,
++ struct net_device *master)
++{
++ struct dsa_notifier_master_state_info info;
++ struct dsa_port *cpu_dp = master->dsa_ptr;
++
++ info.master = master;
++ info.operational = dsa_port_master_is_operational(cpu_dp);
++
++ dsa_tree_notify(dst, DSA_NOTIFIER_MASTER_STATE_CHANGE, &info);
++}
++
++void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst,
++ struct net_device *master,
++ bool up)
++{
++ struct dsa_port *cpu_dp = master->dsa_ptr;
++ bool notify = false;
++
++ if ((dsa_port_master_is_operational(cpu_dp)) !=
++ (up && cpu_dp->master_oper_up))
++ notify = true;
++
++ cpu_dp->master_admin_up = up;
++
++ if (notify)
++ dsa_tree_master_state_change(dst, master);
++}
++
++void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst,
++ struct net_device *master,
++ bool up)
++{
++ struct dsa_port *cpu_dp = master->dsa_ptr;
++ bool notify = false;
++
++ if ((dsa_port_master_is_operational(cpu_dp)) !=
++ (cpu_dp->master_admin_up && up))
++ notify = true;
++
++ cpu_dp->master_oper_up = up;
++
++ if (notify)
++ dsa_tree_master_state_change(dst, master);
++}
++
+ static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
+ {
+ struct dsa_switch_tree *dst = ds->dst;
+--- a/net/dsa/dsa_priv.h
++++ b/net/dsa/dsa_priv.h
+@@ -45,6 +45,7 @@ enum {
+ DSA_NOTIFIER_MRP_DEL_RING_ROLE,
+ DSA_NOTIFIER_TAG_8021Q_VLAN_ADD,
+ DSA_NOTIFIER_TAG_8021Q_VLAN_DEL,
++ DSA_NOTIFIER_MASTER_STATE_CHANGE,
+ };
+
+ /* DSA_NOTIFIER_AGEING_TIME */
+@@ -127,6 +128,12 @@ struct dsa_notifier_tag_8021q_vlan_info
+ u16 vid;
+ };
+
++/* DSA_NOTIFIER_MASTER_STATE_CHANGE */
++struct dsa_notifier_master_state_info {
++ const struct net_device *master;
++ bool operational;
++};
++
+ struct dsa_switchdev_event_work {
+ struct dsa_switch *ds;
+ int port;
+@@ -549,6 +556,12 @@ int dsa_tree_change_tag_proto(struct dsa
+ struct net_device *master,
+ const struct dsa_device_ops *tag_ops,
+ const struct dsa_device_ops *old_tag_ops);
++void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst,
++ struct net_device *master,
++ bool up);
++void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst,
++ struct net_device *master,
++ bool up);
+ int dsa_bridge_num_get(const struct net_device *bridge_dev, int max);
+ void dsa_bridge_num_put(const struct net_device *bridge_dev, int bridge_num);
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -2311,6 +2311,36 @@ static int dsa_slave_netdevice_event(str
+ err = dsa_port_lag_change(dp, info->lower_state_info);
+ return notifier_from_errno(err);
+ }
++ case NETDEV_CHANGE:
++ case NETDEV_UP: {
++ /* Track state of master port.
++ * DSA driver may require the master port (and indirectly
++ * the tagger) to be available for some special operation.
++ */
++ if (netdev_uses_dsa(dev)) {
++ struct dsa_port *cpu_dp = dev->dsa_ptr;
++ struct dsa_switch_tree *dst = cpu_dp->ds->dst;
++
++ /* Track when the master port is UP */
++ dsa_tree_master_oper_state_change(dst, dev,
++ netif_oper_up(dev));
++
++ /* Track when the master port is ready and can accept
++ * packet.
++ * NETDEV_UP event is not enough to flag a port as ready.
++ * We also have to wait for linkwatch_do_dev to dev_activate
++ * and emit a NETDEV_CHANGE event.
++ * We check if a master port is ready by checking if the dev
++ * have a qdisc assigned and is not noop.
++ */
++ dsa_tree_master_admin_state_change(dst, dev,
++ !qdisc_tx_is_noop(dev));
++
++ return NOTIFY_OK;
++ }
++
++ return NOTIFY_DONE;
++ }
+ case NETDEV_GOING_DOWN: {
+ struct dsa_port *dp, *cpu_dp;
+ struct dsa_switch_tree *dst;
+@@ -2322,6 +2352,8 @@ static int dsa_slave_netdevice_event(str
+ cpu_dp = dev->dsa_ptr;
+ dst = cpu_dp->ds->dst;
+
++ dsa_tree_master_admin_state_change(dst, dev, false);
++
+ list_for_each_entry(dp, &dst->ports, list) {
+ if (!dsa_is_user_port(dp->ds, dp->index))
+ continue;
+--- a/net/dsa/switch.c
++++ b/net/dsa/switch.c
+@@ -722,6 +722,18 @@ dsa_switch_mrp_del_ring_role(struct dsa_
+ return 0;
+ }
+
++static int
++dsa_switch_master_state_change(struct dsa_switch *ds,
++ struct dsa_notifier_master_state_info *info)
++{
++ if (!ds->ops->master_state_change)
++ return 0;
++
++ ds->ops->master_state_change(ds, info->master, info->operational);
++
++ return 0;
++}
++
+ static int dsa_switch_event(struct notifier_block *nb,
+ unsigned long event, void *info)
+ {
+@@ -813,6 +825,9 @@ static int dsa_switch_event(struct notif
+ case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL:
+ err = dsa_switch_tag_8021q_vlan_del(ds, info);
+ break;
++ case DSA_NOTIFIER_MASTER_STATE_CHANGE:
++ err = dsa_switch_master_state_change(ds, info);
++ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
diff --git a/target/linux/generic/backport-6.1/766-v5.18-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch b/target/linux/generic/backport-6.1/766-v5.18-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch
new file mode 100644
index 0000000000..6478d580c0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch
@@ -0,0 +1,89 @@
+From e83d56537859849f2223b90749e554831b1f3c27 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Wed, 2 Feb 2022 01:03:21 +0100
+Subject: [PATCH 02/16] net: dsa: replay master state events in
+ dsa_tree_{setup,teardown}_master
+
+In order for switch driver to be able to make simple and reliable use of
+the master tracking operations, they must also be notified of the
+initial state of the DSA master, not just of the changes. This is
+because they might enable certain features only during the time when
+they know that the DSA master is up and running.
+
+Therefore, this change explicitly checks the state of the DSA master
+under the same rtnl_mutex as we were holding during the
+dsa_master_setup() and dsa_master_teardown() call. The idea being that
+if the DSA master became operational in between the moment in which it
+became a DSA master (dsa_master_setup set dev->dsa_ptr) and the moment
+when we checked for the master being up, there is a chance that we
+would emit a ->master_state_change() call with no actual state change.
+We need to avoid that by serializing the concurrent netdevice event with
+us. If the netdevice event started before, we force it to finish before
+we begin, because we take rtnl_lock before making netdev_uses_dsa()
+return true. So we also handle that early event and do nothing on it.
+Similarly, if the dev_open() attempt is concurrent with us, it will
+attempt to take the rtnl_mutex, but we're holding it. We'll see that
+the master flag IFF_UP isn't set, then when we release the rtnl_mutex
+we'll process the NETDEV_UP notifier.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/dsa2.c | 28 ++++++++++++++++++++++++----
+ 1 file changed, 24 insertions(+), 4 deletions(-)
+
+--- a/net/dsa/dsa2.c
++++ b/net/dsa/dsa2.c
+@@ -15,6 +15,7 @@
+ #include <linux/of.h>
+ #include <linux/of_net.h>
+ #include <net/devlink.h>
++#include <net/sch_generic.h>
+
+ #include "dsa_priv.h"
+
+@@ -1060,9 +1061,18 @@ static int dsa_tree_setup_master(struct
+
+ list_for_each_entry(dp, &dst->ports, list) {
+ if (dsa_port_is_cpu(dp)) {
+- err = dsa_master_setup(dp->master, dp);
++ struct net_device *master = dp->master;
++ bool admin_up = (master->flags & IFF_UP) &&
++ !qdisc_tx_is_noop(master);
++
++ err = dsa_master_setup(master, dp);
+ if (err)
+ return err;
++
++ /* Replay master state event */
++ dsa_tree_master_admin_state_change(dst, master, admin_up);
++ dsa_tree_master_oper_state_change(dst, master,
++ netif_oper_up(master));
+ }
+ }
+
+@@ -1077,9 +1087,19 @@ static void dsa_tree_teardown_master(str
+
+ rtnl_lock();
+
+- list_for_each_entry(dp, &dst->ports, list)
+- if (dsa_port_is_cpu(dp))
+- dsa_master_teardown(dp->master);
++ list_for_each_entry(dp, &dst->ports, list) {
++ if (dsa_port_is_cpu(dp)) {
++ struct net_device *master = dp->master;
++
++ /* Synthesizing an "admin down" state is sufficient for
++ * the switches to get a notification if the master is
++ * currently up and running.
++ */
++ dsa_tree_master_admin_state_change(dst, master, false);
++
++ dsa_master_teardown(master);
++ }
++ }
+
+ rtnl_unlock();
+ }
diff --git a/target/linux/generic/backport-6.1/766-v5.18-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch b/target/linux/generic/backport-6.1/766-v5.18-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch
new file mode 100644
index 0000000000..82c94b385b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch
@@ -0,0 +1,86 @@
+From 6b0458299297ca4ab6fb295800e29a4e501d50c1 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:22 +0100
+Subject: [PATCH 03/16] net: dsa: tag_qca: convert to FIELD macro
+
+Convert driver to FIELD macro to drop redundant define.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/tag_qca.c | 34 +++++++++++++++-------------------
+ 1 file changed, 15 insertions(+), 19 deletions(-)
+
+--- a/net/dsa/tag_qca.c
++++ b/net/dsa/tag_qca.c
+@@ -4,29 +4,24 @@
+ */
+
+ #include <linux/etherdevice.h>
++#include <linux/bitfield.h>
+
+ #include "dsa_priv.h"
+
+ #define QCA_HDR_LEN 2
+ #define QCA_HDR_VERSION 0x2
+
+-#define QCA_HDR_RECV_VERSION_MASK GENMASK(15, 14)
+-#define QCA_HDR_RECV_VERSION_S 14
+-#define QCA_HDR_RECV_PRIORITY_MASK GENMASK(13, 11)
+-#define QCA_HDR_RECV_PRIORITY_S 11
+-#define QCA_HDR_RECV_TYPE_MASK GENMASK(10, 6)
+-#define QCA_HDR_RECV_TYPE_S 6
++#define QCA_HDR_RECV_VERSION GENMASK(15, 14)
++#define QCA_HDR_RECV_PRIORITY GENMASK(13, 11)
++#define QCA_HDR_RECV_TYPE GENMASK(10, 6)
+ #define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3)
+-#define QCA_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0)
++#define QCA_HDR_RECV_SOURCE_PORT GENMASK(2, 0)
+
+-#define QCA_HDR_XMIT_VERSION_MASK GENMASK(15, 14)
+-#define QCA_HDR_XMIT_VERSION_S 14
+-#define QCA_HDR_XMIT_PRIORITY_MASK GENMASK(13, 11)
+-#define QCA_HDR_XMIT_PRIORITY_S 11
+-#define QCA_HDR_XMIT_CONTROL_MASK GENMASK(10, 8)
+-#define QCA_HDR_XMIT_CONTROL_S 8
++#define QCA_HDR_XMIT_VERSION GENMASK(15, 14)
++#define QCA_HDR_XMIT_PRIORITY GENMASK(13, 11)
++#define QCA_HDR_XMIT_CONTROL GENMASK(10, 8)
+ #define QCA_HDR_XMIT_FROM_CPU BIT(7)
+-#define QCA_HDR_XMIT_DP_BIT_MASK GENMASK(6, 0)
++#define QCA_HDR_XMIT_DP_BIT GENMASK(6, 0)
+
+ static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
+@@ -40,8 +35,9 @@ static struct sk_buff *qca_tag_xmit(stru
+ phdr = dsa_etype_header_pos_tx(skb);
+
+ /* Set the version field, and set destination port information */
+- hdr = QCA_HDR_VERSION << QCA_HDR_XMIT_VERSION_S |
+- QCA_HDR_XMIT_FROM_CPU | BIT(dp->index);
++ hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION);
++ hdr |= QCA_HDR_XMIT_FROM_CPU;
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(dp->index));
+
+ *phdr = htons(hdr);
+
+@@ -62,7 +58,7 @@ static struct sk_buff *qca_tag_rcv(struc
+ hdr = ntohs(*phdr);
+
+ /* Make sure the version is correct */
+- ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S;
++ ver = FIELD_GET(QCA_HDR_RECV_VERSION, hdr);
+ if (unlikely(ver != QCA_HDR_VERSION))
+ return NULL;
+
+@@ -71,7 +67,7 @@ static struct sk_buff *qca_tag_rcv(struc
+ dsa_strip_etype_header(skb, QCA_HDR_LEN);
+
+ /* Get source port information */
+- port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK);
++ port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, hdr);
+
+ skb->dev = dsa_master_find_slave(dev, 0, port);
+ if (!skb->dev)
diff --git a/target/linux/generic/backport-6.1/766-v5.18-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch b/target/linux/generic/backport-6.1/766-v5.18-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch
new file mode 100644
index 0000000000..c1e74ceeeb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch
@@ -0,0 +1,71 @@
+From 3ec762fb13c7e7273800b94c80db1c2cc37590d1 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:23 +0100
+Subject: [PATCH 04/16] net: dsa: tag_qca: move define to include linux/dsa
+
+Move tag_qca define to include dir linux/dsa as the qca8k require access
+to the tagger define to support in-band mdio read/write using ethernet
+packet.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/dsa/tag_qca.h | 21 +++++++++++++++++++++
+ net/dsa/tag_qca.c | 16 +---------------
+ 2 files changed, 22 insertions(+), 15 deletions(-)
+ create mode 100644 include/linux/dsa/tag_qca.h
+
+--- /dev/null
++++ b/include/linux/dsa/tag_qca.h
+@@ -0,0 +1,21 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++
++#ifndef __TAG_QCA_H
++#define __TAG_QCA_H
++
++#define QCA_HDR_LEN 2
++#define QCA_HDR_VERSION 0x2
++
++#define QCA_HDR_RECV_VERSION GENMASK(15, 14)
++#define QCA_HDR_RECV_PRIORITY GENMASK(13, 11)
++#define QCA_HDR_RECV_TYPE GENMASK(10, 6)
++#define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3)
++#define QCA_HDR_RECV_SOURCE_PORT GENMASK(2, 0)
++
++#define QCA_HDR_XMIT_VERSION GENMASK(15, 14)
++#define QCA_HDR_XMIT_PRIORITY GENMASK(13, 11)
++#define QCA_HDR_XMIT_CONTROL GENMASK(10, 8)
++#define QCA_HDR_XMIT_FROM_CPU BIT(7)
++#define QCA_HDR_XMIT_DP_BIT GENMASK(6, 0)
++
++#endif /* __TAG_QCA_H */
+--- a/net/dsa/tag_qca.c
++++ b/net/dsa/tag_qca.c
+@@ -5,24 +5,10 @@
+
+ #include <linux/etherdevice.h>
+ #include <linux/bitfield.h>
++#include <linux/dsa/tag_qca.h>
+
+ #include "dsa_priv.h"
+
+-#define QCA_HDR_LEN 2
+-#define QCA_HDR_VERSION 0x2
+-
+-#define QCA_HDR_RECV_VERSION GENMASK(15, 14)
+-#define QCA_HDR_RECV_PRIORITY GENMASK(13, 11)
+-#define QCA_HDR_RECV_TYPE GENMASK(10, 6)
+-#define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3)
+-#define QCA_HDR_RECV_SOURCE_PORT GENMASK(2, 0)
+-
+-#define QCA_HDR_XMIT_VERSION GENMASK(15, 14)
+-#define QCA_HDR_XMIT_PRIORITY GENMASK(13, 11)
+-#define QCA_HDR_XMIT_CONTROL GENMASK(10, 8)
+-#define QCA_HDR_XMIT_FROM_CPU BIT(7)
+-#define QCA_HDR_XMIT_DP_BIT GENMASK(6, 0)
+-
+ static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
+ struct dsa_port *dp = dsa_slave_to_port(dev);
diff --git a/target/linux/generic/backport-6.1/766-v5.18-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch b/target/linux/generic/backport-6.1/766-v5.18-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch
new file mode 100644
index 0000000000..9394a0dabb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch
@@ -0,0 +1,27 @@
+From 101c04c3463b87061e6a3d4f72c1bc57670685a6 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:24 +0100
+Subject: [PATCH 05/16] net: dsa: tag_qca: enable promisc_on_master flag
+
+Ethernet MDIO packets are non-standard and DSA master expects the first
+6 octets to be the MAC DA. To address these kind of packet, enable
+promisc_on_master flag for the tagger.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/tag_qca.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/net/dsa/tag_qca.c
++++ b/net/dsa/tag_qca.c
+@@ -68,6 +68,7 @@ static const struct dsa_device_ops qca_n
+ .xmit = qca_tag_xmit,
+ .rcv = qca_tag_rcv,
+ .needed_headroom = QCA_HDR_LEN,
++ .promisc_on_master = true,
+ };
+
+ MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.1/766-v5.18-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch b/target/linux/generic/backport-6.1/766-v5.18-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch
new file mode 100644
index 0000000000..459454e03b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch
@@ -0,0 +1,110 @@
+From c2ee8181fddb293d296477f60b3eb4fa3ce4e1a6 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:25 +0100
+Subject: [PATCH 06/16] net: dsa: tag_qca: add define for handling mgmt
+ Ethernet packet
+
+Add all the required define to prepare support for mgmt read/write in
+Ethernet packet. Any packet of this type has to be dropped as the only
+use of these special packet is receive ack for an mgmt write request or
+receive data for an mgmt read request.
+A struct is used that emulates the Ethernet header but is used for a
+different purpose.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/dsa/tag_qca.h | 44 +++++++++++++++++++++++++++++++++++++
+ net/dsa/tag_qca.c | 15 ++++++++++---
+ 2 files changed, 56 insertions(+), 3 deletions(-)
+
+--- a/include/linux/dsa/tag_qca.h
++++ b/include/linux/dsa/tag_qca.h
+@@ -12,10 +12,54 @@
+ #define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3)
+ #define QCA_HDR_RECV_SOURCE_PORT GENMASK(2, 0)
+
++/* Packet type for recv */
++#define QCA_HDR_RECV_TYPE_NORMAL 0x0
++#define QCA_HDR_RECV_TYPE_MIB 0x1
++#define QCA_HDR_RECV_TYPE_RW_REG_ACK 0x2
++
+ #define QCA_HDR_XMIT_VERSION GENMASK(15, 14)
+ #define QCA_HDR_XMIT_PRIORITY GENMASK(13, 11)
+ #define QCA_HDR_XMIT_CONTROL GENMASK(10, 8)
+ #define QCA_HDR_XMIT_FROM_CPU BIT(7)
+ #define QCA_HDR_XMIT_DP_BIT GENMASK(6, 0)
+
++/* Packet type for xmit */
++#define QCA_HDR_XMIT_TYPE_NORMAL 0x0
++#define QCA_HDR_XMIT_TYPE_RW_REG 0x1
++
++/* Check code for a valid mgmt packet. Switch will ignore the packet
++ * with this wrong.
++ */
++#define QCA_HDR_MGMT_CHECK_CODE_VAL 0x5
++
++/* Specific define for in-band MDIO read/write with Ethernet packet */
++#define QCA_HDR_MGMT_SEQ_LEN 4 /* 4 byte for the seq */
++#define QCA_HDR_MGMT_COMMAND_LEN 4 /* 4 byte for the command */
++#define QCA_HDR_MGMT_DATA1_LEN 4 /* First 4 byte for the mdio data */
++#define QCA_HDR_MGMT_HEADER_LEN (QCA_HDR_MGMT_SEQ_LEN + \
++ QCA_HDR_MGMT_COMMAND_LEN + \
++ QCA_HDR_MGMT_DATA1_LEN)
++
++#define QCA_HDR_MGMT_DATA2_LEN 12 /* Other 12 byte for the mdio data */
++#define QCA_HDR_MGMT_PADDING_LEN 34 /* Padding to reach the min Ethernet packet */
++
++#define QCA_HDR_MGMT_PKT_LEN (QCA_HDR_MGMT_HEADER_LEN + \
++ QCA_HDR_LEN + \
++ QCA_HDR_MGMT_DATA2_LEN + \
++ QCA_HDR_MGMT_PADDING_LEN)
++
++#define QCA_HDR_MGMT_SEQ_NUM GENMASK(31, 0) /* 63, 32 */
++#define QCA_HDR_MGMT_CHECK_CODE GENMASK(31, 29) /* 31, 29 */
++#define QCA_HDR_MGMT_CMD BIT(28) /* 28 */
++#define QCA_HDR_MGMT_LENGTH GENMASK(23, 20) /* 23, 20 */
++#define QCA_HDR_MGMT_ADDR GENMASK(18, 0) /* 18, 0 */
++
++/* Special struct emulating a Ethernet header */
++struct qca_mgmt_ethhdr {
++ u32 command; /* command bit 31:0 */
++ u32 seq; /* seq 63:32 */
++ u32 mdio_data; /* first 4byte mdio */
++ __be16 hdr; /* qca hdr */
++} __packed;
++
+ #endif /* __TAG_QCA_H */
+--- a/net/dsa/tag_qca.c
++++ b/net/dsa/tag_qca.c
+@@ -32,10 +32,12 @@ static struct sk_buff *qca_tag_xmit(stru
+
+ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev)
+ {
+- u8 ver;
+- u16 hdr;
+- int port;
++ u8 ver, pk_type;
+ __be16 *phdr;
++ int port;
++ u16 hdr;
++
++ BUILD_BUG_ON(sizeof(struct qca_mgmt_ethhdr) != QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN);
+
+ if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN)))
+ return NULL;
+@@ -48,6 +50,13 @@ static struct sk_buff *qca_tag_rcv(struc
+ if (unlikely(ver != QCA_HDR_VERSION))
+ return NULL;
+
++ /* Get pk type */
++ pk_type = FIELD_GET(QCA_HDR_RECV_TYPE, hdr);
++
++ /* Ethernet MDIO read/write packet */
++ if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK)
++ return NULL;
++
+ /* Remove QCA tag and recalculate checksum */
+ skb_pull_rcsum(skb, QCA_HDR_LEN);
+ dsa_strip_etype_header(skb, QCA_HDR_LEN);
diff --git a/target/linux/generic/backport-6.1/766-v5.18-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch b/target/linux/generic/backport-6.1/766-v5.18-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch
new file mode 100644
index 0000000000..7e5dc65730
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch
@@ -0,0 +1,45 @@
+From 18be654a4345f7d937b4bfbad74bea8093e3a93c Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:26 +0100
+Subject: [PATCH 07/16] net: dsa: tag_qca: add define for handling MIB packet
+
+Add struct to correctly parse a mib Ethernet packet.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/dsa/tag_qca.h | 10 ++++++++++
+ net/dsa/tag_qca.c | 4 ++++
+ 2 files changed, 14 insertions(+)
+
+--- a/include/linux/dsa/tag_qca.h
++++ b/include/linux/dsa/tag_qca.h
+@@ -62,4 +62,14 @@ struct qca_mgmt_ethhdr {
+ __be16 hdr; /* qca hdr */
+ } __packed;
+
++enum mdio_cmd {
++ MDIO_WRITE = 0x0,
++ MDIO_READ
++};
++
++struct mib_ethhdr {
++ u32 data[3]; /* first 3 mib counter */
++ __be16 hdr; /* qca hdr */
++} __packed;
++
+ #endif /* __TAG_QCA_H */
+--- a/net/dsa/tag_qca.c
++++ b/net/dsa/tag_qca.c
+@@ -57,6 +57,10 @@ static struct sk_buff *qca_tag_rcv(struc
+ if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK)
+ return NULL;
+
++ /* Ethernet MIB counter packet */
++ if (pk_type == QCA_HDR_RECV_TYPE_MIB)
++ return NULL;
++
+ /* Remove QCA tag and recalculate checksum */
+ skb_pull_rcsum(skb, QCA_HDR_LEN);
+ dsa_strip_etype_header(skb, QCA_HDR_LEN);
diff --git a/target/linux/generic/backport-6.1/766-v5.18-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch b/target/linux/generic/backport-6.1/766-v5.18-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch
new file mode 100644
index 0000000000..ad25da30e6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch
@@ -0,0 +1,116 @@
+From 31eb6b4386ad91930417e3f5c8157a4b5e31cbd5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:27 +0100
+Subject: [PATCH 08/16] net: dsa: tag_qca: add support for handling mgmt and
+ MIB Ethernet packet
+
+Add connect/disconnect helper to assign private struct to the DSA switch.
+Add support for Ethernet mgmt and MIB if the DSA driver provide an handler
+to correctly parse and elaborate the data.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/dsa/tag_qca.h | 7 +++++++
+ net/dsa/tag_qca.c | 39 ++++++++++++++++++++++++++++++++++---
+ 2 files changed, 43 insertions(+), 3 deletions(-)
+
+--- a/include/linux/dsa/tag_qca.h
++++ b/include/linux/dsa/tag_qca.h
+@@ -72,4 +72,11 @@ struct mib_ethhdr {
+ __be16 hdr; /* qca hdr */
+ } __packed;
+
++struct qca_tagger_data {
++ void (*rw_reg_ack_handler)(struct dsa_switch *ds,
++ struct sk_buff *skb);
++ void (*mib_autocast_handler)(struct dsa_switch *ds,
++ struct sk_buff *skb);
++};
++
+ #endif /* __TAG_QCA_H */
+--- a/net/dsa/tag_qca.c
++++ b/net/dsa/tag_qca.c
+@@ -5,6 +5,7 @@
+
+ #include <linux/etherdevice.h>
+ #include <linux/bitfield.h>
++#include <net/dsa.h>
+ #include <linux/dsa/tag_qca.h>
+
+ #include "dsa_priv.h"
+@@ -32,6 +33,9 @@ static struct sk_buff *qca_tag_xmit(stru
+
+ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev)
+ {
++ struct qca_tagger_data *tagger_data;
++ struct dsa_port *dp = dev->dsa_ptr;
++ struct dsa_switch *ds = dp->ds;
+ u8 ver, pk_type;
+ __be16 *phdr;
+ int port;
+@@ -39,6 +43,8 @@ static struct sk_buff *qca_tag_rcv(struc
+
+ BUILD_BUG_ON(sizeof(struct qca_mgmt_ethhdr) != QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN);
+
++ tagger_data = ds->tagger_data;
++
+ if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN)))
+ return NULL;
+
+@@ -53,13 +59,19 @@ static struct sk_buff *qca_tag_rcv(struc
+ /* Get pk type */
+ pk_type = FIELD_GET(QCA_HDR_RECV_TYPE, hdr);
+
+- /* Ethernet MDIO read/write packet */
+- if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK)
++ /* Ethernet mgmt read/write packet */
++ if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK) {
++ if (likely(tagger_data->rw_reg_ack_handler))
++ tagger_data->rw_reg_ack_handler(ds, skb);
+ return NULL;
++ }
+
+ /* Ethernet MIB counter packet */
+- if (pk_type == QCA_HDR_RECV_TYPE_MIB)
++ if (pk_type == QCA_HDR_RECV_TYPE_MIB) {
++ if (likely(tagger_data->mib_autocast_handler))
++ tagger_data->mib_autocast_handler(ds, skb);
+ return NULL;
++ }
+
+ /* Remove QCA tag and recalculate checksum */
+ skb_pull_rcsum(skb, QCA_HDR_LEN);
+@@ -75,9 +87,30 @@ static struct sk_buff *qca_tag_rcv(struc
+ return skb;
+ }
+
++static int qca_tag_connect(struct dsa_switch *ds)
++{
++ struct qca_tagger_data *tagger_data;
++
++ tagger_data = kzalloc(sizeof(*tagger_data), GFP_KERNEL);
++ if (!tagger_data)
++ return -ENOMEM;
++
++ ds->tagger_data = tagger_data;
++
++ return 0;
++}
++
++static void qca_tag_disconnect(struct dsa_switch *ds)
++{
++ kfree(ds->tagger_data);
++ ds->tagger_data = NULL;
++}
++
+ static const struct dsa_device_ops qca_netdev_ops = {
+ .name = "qca",
+ .proto = DSA_TAG_PROTO_QCA,
++ .connect = qca_tag_connect,
++ .disconnect = qca_tag_disconnect,
+ .xmit = qca_tag_xmit,
+ .rcv = qca_tag_rcv,
+ .needed_headroom = QCA_HDR_LEN,
diff --git a/target/linux/generic/backport-6.1/766-v5.18-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch b/target/linux/generic/backport-6.1/766-v5.18-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch
new file mode 100644
index 0000000000..eb21cc3912
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch
@@ -0,0 +1,67 @@
+From cddbec19466a1dfb4d45ddd507d9f09f991d54ae Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:28 +0100
+Subject: [PATCH 09/16] net: dsa: qca8k: add tracking state of master port
+
+MDIO/MIB Ethernet require the master port and the tagger availabale to
+correctly work. Use the new api master_state_change to track when master
+is operational or not and set a bool in qca8k_priv.
+We cache the first cached master available and we check if other cpu
+port are operational when the cached one goes down.
+This cached master will later be used by mdio read/write and mib request to
+correctly use the working function.
+
+qca8k implementation for MDIO/MIB Ethernet is bad. CPU port0 is the only
+one that answers with the ack packet or sends MIB Ethernet packets. For
+this reason the master_state_change ignore CPU port6 and only checks
+CPU port0 if it's operational and enables this mode.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 15 +++++++++++++++
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 16 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -2401,6 +2401,20 @@ qca8k_port_lag_leave(struct dsa_switch *
+ return qca8k_lag_refresh_portmap(ds, port, lag, true);
+ }
+
++static void
++qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
++ bool operational)
++{
++ struct dsa_port *dp = master->dsa_ptr;
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Ethernet MIB/MDIO is only supported for CPU port 0 */
++ if (dp->index != 0)
++ return;
++
++ priv->mgmt_master = operational ? (struct net_device *)master : NULL;
++}
++
+ static const struct dsa_switch_ops qca8k_switch_ops = {
+ .get_tag_protocol = qca8k_get_tag_protocol,
+ .setup = qca8k_setup,
+@@ -2436,6 +2450,7 @@ static const struct dsa_switch_ops qca8k
+ .get_phy_flags = qca8k_get_phy_flags,
+ .port_lag_join = qca8k_port_lag_join,
+ .port_lag_leave = qca8k_port_lag_leave,
++ .master_state_change = qca8k_master_change,
+ };
+
+ static int qca8k_read_switch_id(struct qca8k_priv *priv)
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -353,6 +353,7 @@ struct qca8k_priv {
+ struct dsa_switch_ops ops;
+ struct gpio_desc *reset_gpio;
+ unsigned int port_mtu[QCA8K_NUM_PORTS];
++ struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
+ };
+
+ struct qca8k_mib_desc {
diff --git a/target/linux/generic/backport-6.1/766-v5.18-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch b/target/linux/generic/backport-6.1/766-v5.18-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch
new file mode 100644
index 0000000000..07c5ba4621
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch
@@ -0,0 +1,363 @@
+From 5950c7c0a68c915b336c70f79388626e2d576ab7 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:29 +0100
+Subject: [PATCH 10/16] net: dsa: qca8k: add support for mgmt read/write in
+ Ethernet packet
+
+Add qca8k side support for mgmt read/write in Ethernet packet.
+qca8k supports some specially crafted Ethernet packet that can be used
+for mgmt read/write instead of the legacy method uart/internal mdio.
+This add support for the qca8k side to craft the packet and enqueue it.
+Each port and the qca8k_priv have a special struct to put data in it.
+The completion API is used to wait for the packet to be received back
+with the requested data.
+
+The various steps are:
+1. Craft the special packet with the qca hdr set to mgmt read/write
+ mode.
+2. Set the lock in the dedicated mgmt struct.
+3. Increment the seq number and set it in the mgmt pkt
+4. Reinit the completion.
+5. Enqueue the packet.
+6. Wait the packet to be received.
+7. Use the data set by the tagger to complete the mdio operation.
+
+If the completion timeouts or the ack value is not true, the legacy
+mdio way is used.
+
+It has to be considered that in the initial setup mdio is still used and
+mdio is still used until DSA is ready to accept and tag packet.
+
+tag_proto_connect() is used to fill the required handler for the tagger
+to correctly parse and elaborate the special Ethernet mdio packet.
+
+Locking is added to qca8k_master_change() to make sure no mgmt Ethernet
+are in progress.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 225 ++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 13 +++
+ 2 files changed, 238 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -20,6 +20,7 @@
+ #include <linux/phylink.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/etherdevice.h>
++#include <linux/dsa/tag_qca.h>
+
+ #include "qca8k.h"
+
+@@ -170,6 +171,194 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r
+ return regmap_update_bits(priv->regmap, reg, mask, write_val);
+ }
+
++static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
++{
++ struct qca8k_mgmt_eth_data *mgmt_eth_data;
++ struct qca8k_priv *priv = ds->priv;
++ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++ u8 len, cmd;
++
++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
++ mgmt_eth_data = &priv->mgmt_eth_data;
++
++ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
++ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
++
++ /* Make sure the seq match the requested packet */
++ if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
++ mgmt_eth_data->ack = true;
++
++ if (cmd == MDIO_READ) {
++ mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
++
++ /* Get the rest of the 12 byte of data */
++ if (len > QCA_HDR_MGMT_DATA1_LEN)
++ memcpy(mgmt_eth_data->data + 1, skb->data,
++ QCA_HDR_MGMT_DATA2_LEN);
++ }
++
++ complete(&mgmt_eth_data->rw_done);
++}
++
++static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val,
++ int priority)
++{
++ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++ struct sk_buff *skb;
++ u16 hdr;
++
++ skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
++ if (!skb)
++ return NULL;
++
++ skb_reset_mac_header(skb);
++ skb_set_network_header(skb, skb->len);
++
++ mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN);
++
++ hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION);
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority);
++ hdr |= QCA_HDR_XMIT_FROM_CPU;
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
++
++ mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, 4);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
++ QCA_HDR_MGMT_CHECK_CODE_VAL);
++
++ if (cmd == MDIO_WRITE)
++ mgmt_ethhdr->mdio_data = *val;
++
++ mgmt_ethhdr->hdr = htons(hdr);
++
++ skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
++
++ return skb;
++}
++
++static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
++{
++ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++
++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
++ mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
++}
++
++static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val)
++{
++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
++ struct sk_buff *skb;
++ bool ack;
++ int ret;
++
++ skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL,
++ QCA8K_ETHERNET_MDIO_PRIORITY);
++ if (!skb)
++ return -ENOMEM;
++
++ mutex_lock(&mgmt_eth_data->mutex);
++
++ /* Check mgmt_master if is operational */
++ if (!priv->mgmt_master) {
++ kfree_skb(skb);
++ mutex_unlock(&mgmt_eth_data->mutex);
++ return -EINVAL;
++ }
++
++ skb->dev = priv->mgmt_master;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the mdio pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
++
++ *val = mgmt_eth_data->data[0];
++ ack = mgmt_eth_data->ack;
++
++ mutex_unlock(&mgmt_eth_data->mutex);
++
++ if (ret <= 0)
++ return -ETIMEDOUT;
++
++ if (!ack)
++ return -EINVAL;
++
++ return 0;
++}
++
++static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 val)
++{
++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
++ struct sk_buff *skb;
++ bool ack;
++ int ret;
++
++ skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, &val,
++ QCA8K_ETHERNET_MDIO_PRIORITY);
++ if (!skb)
++ return -ENOMEM;
++
++ mutex_lock(&mgmt_eth_data->mutex);
++
++ /* Check mgmt_master if is operational */
++ if (!priv->mgmt_master) {
++ kfree_skb(skb);
++ mutex_unlock(&mgmt_eth_data->mutex);
++ return -EINVAL;
++ }
++
++ skb->dev = priv->mgmt_master;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the mdio pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
++
++ ack = mgmt_eth_data->ack;
++
++ mutex_unlock(&mgmt_eth_data->mutex);
++
++ if (ret <= 0)
++ return -ETIMEDOUT;
++
++ if (!ack)
++ return -EINVAL;
++
++ return 0;
++}
++
++static int
++qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++{
++ u32 val = 0;
++ int ret;
++
++ ret = qca8k_read_eth(priv, reg, &val);
++ if (ret)
++ return ret;
++
++ val &= ~mask;
++ val |= write_val;
++
++ return qca8k_write_eth(priv, reg, val);
++}
++
+ static int
+ qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+ {
+@@ -178,6 +367,9 @@ qca8k_regmap_read(void *ctx, uint32_t re
+ u16 r1, r2, page;
+ int ret;
+
++ if (!qca8k_read_eth(priv, reg, val))
++ return 0;
++
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+@@ -201,6 +393,9 @@ qca8k_regmap_write(void *ctx, uint32_t r
+ u16 r1, r2, page;
+ int ret;
+
++ if (!qca8k_write_eth(priv, reg, val))
++ return 0;
++
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+@@ -225,6 +420,9 @@ qca8k_regmap_update_bits(void *ctx, uint
+ u32 val;
+ int ret;
+
++ if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
++ return 0;
++
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+@@ -2412,7 +2610,30 @@ qca8k_master_change(struct dsa_switch *d
+ if (dp->index != 0)
+ return;
+
++ mutex_lock(&priv->mgmt_eth_data.mutex);
++
+ priv->mgmt_master = operational ? (struct net_device *)master : NULL;
++
++ mutex_unlock(&priv->mgmt_eth_data.mutex);
++}
++
++static int qca8k_connect_tag_protocol(struct dsa_switch *ds,
++ enum dsa_tag_protocol proto)
++{
++ struct qca_tagger_data *tagger_data;
++
++ switch (proto) {
++ case DSA_TAG_PROTO_QCA:
++ tagger_data = ds->tagger_data;
++
++ tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler;
++
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ return 0;
+ }
+
+ static const struct dsa_switch_ops qca8k_switch_ops = {
+@@ -2451,6 +2672,7 @@ static const struct dsa_switch_ops qca8k
+ .port_lag_join = qca8k_port_lag_join,
+ .port_lag_leave = qca8k_port_lag_leave,
+ .master_state_change = qca8k_master_change,
++ .connect_tag_protocol = qca8k_connect_tag_protocol,
+ };
+
+ static int qca8k_read_switch_id(struct qca8k_priv *priv)
+@@ -2530,6 +2752,9 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ if (!priv->ds)
+ return -ENOMEM;
+
++ mutex_init(&priv->mgmt_eth_data.mutex);
++ init_completion(&priv->mgmt_eth_data.rw_done);
++
+ priv->ds->dev = &mdiodev->dev;
+ priv->ds->num_ports = QCA8K_NUM_PORTS;
+ priv->ds->priv = priv;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -11,6 +11,10 @@
+ #include <linux/delay.h>
+ #include <linux/regmap.h>
+ #include <linux/gpio.h>
++#include <linux/dsa/tag_qca.h>
++
++#define QCA8K_ETHERNET_MDIO_PRIORITY 7
++#define QCA8K_ETHERNET_TIMEOUT 100
+
+ #define QCA8K_NUM_PORTS 7
+ #define QCA8K_NUM_CPU_PORTS 2
+@@ -328,6 +332,14 @@ enum {
+ QCA8K_CPU_PORT6,
+ };
+
++struct qca8k_mgmt_eth_data {
++ struct completion rw_done;
++ struct mutex mutex; /* Enforce one mdio read/write at time */
++ bool ack;
++ u32 seq;
++ u32 data[4];
++};
++
+ struct qca8k_ports_config {
+ bool sgmii_rx_clk_falling_edge;
+ bool sgmii_tx_clk_falling_edge;
+@@ -354,6 +366,7 @@ struct qca8k_priv {
+ struct gpio_desc *reset_gpio;
+ unsigned int port_mtu[QCA8K_NUM_PORTS];
+ struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
++ struct qca8k_mgmt_eth_data mgmt_eth_data;
+ };
+
+ struct qca8k_mib_desc {
diff --git a/target/linux/generic/backport-6.1/766-v5.18-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch b/target/linux/generic/backport-6.1/766-v5.18-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch
new file mode 100644
index 0000000000..0dcf279433
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch
@@ -0,0 +1,226 @@
+From 5c957c7ca78cce5e4b96866722b0115bd758d945 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:30 +0100
+Subject: [PATCH 11/16] net: dsa: qca8k: add support for mib autocast in
+ Ethernet packet
+
+The switch can autocast MIB counter using Ethernet packet.
+Add support for this and provide a handler for the tagger.
+The switch will send packet with MIB counter for each port, the switch
+will use completion API to wait for the correct packet to be received
+and will complete the task only when each packet is received.
+Although the handler will drop all the other packet, we still have to
+consume each MIB packet to complete the request. This is done to prevent
+mixed data with concurrent ethtool request.
+
+connect_tag_protocol() is used to add the handler to the tag_qca tagger,
+master_state_change() use the MIB lock to make sure no MIB Ethernet is
+in progress.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 106 +++++++++++++++++++++++++++++++++++++++-
+ drivers/net/dsa/qca8k.h | 17 ++++++-
+ 2 files changed, 121 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -830,7 +830,10 @@ qca8k_mib_init(struct qca8k_priv *priv)
+ int ret;
+
+ mutex_lock(&priv->reg_mutex);
+- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) |
++ QCA8K_MIB_BUSY);
+ if (ret)
+ goto exit;
+
+@@ -1901,6 +1904,97 @@ qca8k_get_strings(struct dsa_switch *ds,
+ ETH_GSTRING_LEN);
+ }
+
++static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb)
++{
++ const struct qca8k_match_data *match_data;
++ struct qca8k_mib_eth_data *mib_eth_data;
++ struct qca8k_priv *priv = ds->priv;
++ const struct qca8k_mib_desc *mib;
++ struct mib_ethhdr *mib_ethhdr;
++ int i, mib_len, offset = 0;
++ u64 *data;
++ u8 port;
++
++ mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb);
++ mib_eth_data = &priv->mib_eth_data;
++
++ /* The switch autocast every port. Ignore other packet and
++ * parse only the requested one.
++ */
++ port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr));
++ if (port != mib_eth_data->req_port)
++ goto exit;
++
++ match_data = device_get_match_data(priv->dev);
++ data = mib_eth_data->data;
++
++ for (i = 0; i < match_data->mib_count; i++) {
++ mib = &ar8327_mib[i];
++
++ /* First 3 mib are present in the skb head */
++ if (i < 3) {
++ data[i] = mib_ethhdr->data[i];
++ continue;
++ }
++
++ mib_len = sizeof(uint32_t);
++
++ /* Some mib are 64 bit wide */
++ if (mib->size == 2)
++ mib_len = sizeof(uint64_t);
++
++ /* Copy the mib value from packet to the */
++ memcpy(data + i, skb->data + offset, mib_len);
++
++ /* Set the offset for the next mib */
++ offset += mib_len;
++ }
++
++exit:
++ /* Complete on receiving all the mib packet */
++ if (refcount_dec_and_test(&mib_eth_data->port_parsed))
++ complete(&mib_eth_data->rw_done);
++}
++
++static int
++qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data)
++{
++ struct dsa_port *dp = dsa_to_port(ds, port);
++ struct qca8k_mib_eth_data *mib_eth_data;
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ mib_eth_data = &priv->mib_eth_data;
++
++ mutex_lock(&mib_eth_data->mutex);
++
++ reinit_completion(&mib_eth_data->rw_done);
++
++ mib_eth_data->req_port = dp->index;
++ mib_eth_data->data = data;
++ refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS);
++
++ mutex_lock(&priv->reg_mutex);
++
++ /* Send mib autocast request */
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) |
++ QCA8K_MIB_BUSY);
++
++ mutex_unlock(&priv->reg_mutex);
++
++ if (ret)
++ goto exit;
++
++ ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT);
++
++exit:
++ mutex_unlock(&mib_eth_data->mutex);
++
++ return ret;
++}
++
+ static void
+ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
+ uint64_t *data)
+@@ -1912,6 +2006,10 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ u32 hi = 0;
+ int ret;
+
++ if (priv->mgmt_master &&
++ qca8k_get_ethtool_stats_eth(ds, port, data) > 0)
++ return;
++
+ match_data = of_device_get_match_data(priv->dev);
+
+ for (i = 0; i < match_data->mib_count; i++) {
+@@ -2611,9 +2709,11 @@ qca8k_master_change(struct dsa_switch *d
+ return;
+
+ mutex_lock(&priv->mgmt_eth_data.mutex);
++ mutex_lock(&priv->mib_eth_data.mutex);
+
+ priv->mgmt_master = operational ? (struct net_device *)master : NULL;
+
++ mutex_unlock(&priv->mib_eth_data.mutex);
+ mutex_unlock(&priv->mgmt_eth_data.mutex);
+ }
+
+@@ -2627,6 +2727,7 @@ static int qca8k_connect_tag_protocol(st
+ tagger_data = ds->tagger_data;
+
+ tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler;
++ tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler;
+
+ break;
+ default:
+@@ -2755,6 +2856,9 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ mutex_init(&priv->mgmt_eth_data.mutex);
+ init_completion(&priv->mgmt_eth_data.rw_done);
+
++ mutex_init(&priv->mib_eth_data.mutex);
++ init_completion(&priv->mib_eth_data.rw_done);
++
+ priv->ds->dev = &mdiodev->dev;
+ priv->ds->num_ports = QCA8K_NUM_PORTS;
+ priv->ds->priv = priv;
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -67,7 +67,7 @@
+ #define QCA8K_REG_MODULE_EN 0x030
+ #define QCA8K_MODULE_EN_MIB BIT(0)
+ #define QCA8K_REG_MIB 0x034
+-#define QCA8K_MIB_FLUSH BIT(24)
++#define QCA8K_MIB_FUNC GENMASK(26, 24)
+ #define QCA8K_MIB_CPU_KEEP BIT(20)
+ #define QCA8K_MIB_BUSY BIT(17)
+ #define QCA8K_MDIO_MASTER_CTRL 0x3c
+@@ -317,6 +317,12 @@ enum qca8k_vlan_cmd {
+ QCA8K_VLAN_READ = 6,
+ };
+
++enum qca8k_mid_cmd {
++ QCA8K_MIB_FLUSH = 1,
++ QCA8K_MIB_FLUSH_PORT = 2,
++ QCA8K_MIB_CAST = 3,
++};
++
+ struct ar8xxx_port_status {
+ int enabled;
+ };
+@@ -340,6 +346,14 @@ struct qca8k_mgmt_eth_data {
+ u32 data[4];
+ };
+
++struct qca8k_mib_eth_data {
++ struct completion rw_done;
++ struct mutex mutex; /* Process one command at time */
++ refcount_t port_parsed; /* Counter to track parsed port */
++ u8 req_port;
++ u64 *data; /* pointer to ethtool data */
++};
++
+ struct qca8k_ports_config {
+ bool sgmii_rx_clk_falling_edge;
+ bool sgmii_tx_clk_falling_edge;
+@@ -367,6 +381,7 @@ struct qca8k_priv {
+ unsigned int port_mtu[QCA8K_NUM_PORTS];
+ struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
+ struct qca8k_mgmt_eth_data mgmt_eth_data;
++ struct qca8k_mib_eth_data mib_eth_data;
+ };
+
+ struct qca8k_mib_desc {
diff --git a/target/linux/generic/backport-6.1/766-v5.18-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch b/target/linux/generic/backport-6.1/766-v5.18-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch
new file mode 100644
index 0000000000..f5899eb590
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch
@@ -0,0 +1,287 @@
+From 2cd5485663847d468dc207b3ff85fb1fab44d97f Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:31 +0100
+Subject: [PATCH 12/16] net: dsa: qca8k: add support for phy read/write with
+ mgmt Ethernet
+
+Use mgmt Ethernet also for phy read/write if availabale. Use a different
+seq number to make sure we receive the correct packet.
+On any error, we fallback to the legacy mdio read/write.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 216 ++++++++++++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca8k.h | 1 +
+ 2 files changed, 217 insertions(+)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -867,6 +867,199 @@ qca8k_port_set_status(struct qca8k_priv
+ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+ }
+
++static int
++qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
++ struct sk_buff *read_skb, u32 *val)
++{
++ struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL);
++ bool ack;
++ int ret;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the copy pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ ack = mgmt_eth_data->ack;
++
++ if (ret <= 0)
++ return -ETIMEDOUT;
++
++ if (!ack)
++ return -EINVAL;
++
++ *val = mgmt_eth_data->data[0];
++
++ return 0;
++}
++
++static int
++qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
++ int regnum, u16 data)
++{
++ struct sk_buff *write_skb, *clear_skb, *read_skb;
++ struct qca8k_mgmt_eth_data *mgmt_eth_data;
++ u32 write_val, clear_val = 0, val;
++ struct net_device *mgmt_master;
++ int ret, ret1;
++ bool ack;
++
++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
++ return -EINVAL;
++
++ mgmt_eth_data = &priv->mgmt_eth_data;
++
++ write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
++ QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
++ QCA8K_MDIO_MASTER_REG_ADDR(regnum);
++
++ if (read) {
++ write_val |= QCA8K_MDIO_MASTER_READ;
++ } else {
++ write_val |= QCA8K_MDIO_MASTER_WRITE;
++ write_val |= QCA8K_MDIO_MASTER_DATA(data);
++ }
++
++ /* Prealloc all the needed skb before the lock */
++ write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL,
++ &write_val, QCA8K_ETHERNET_PHY_PRIORITY);
++ if (!write_skb)
++ return -ENOMEM;
++
++ clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL,
++ &clear_val, QCA8K_ETHERNET_PHY_PRIORITY);
++ if (!write_skb) {
++ ret = -ENOMEM;
++ goto err_clear_skb;
++ }
++
++ read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL,
++ &clear_val, QCA8K_ETHERNET_PHY_PRIORITY);
++ if (!write_skb) {
++ ret = -ENOMEM;
++ goto err_read_skb;
++ }
++
++ /* Actually start the request:
++ * 1. Send mdio master packet
++ * 2. Busy Wait for mdio master command
++ * 3. Get the data if we are reading
++ * 4. Reset the mdio master (even with error)
++ */
++ mutex_lock(&mgmt_eth_data->mutex);
++
++ /* Check if mgmt_master is operational */
++ mgmt_master = priv->mgmt_master;
++ if (!mgmt_master) {
++ mutex_unlock(&mgmt_eth_data->mutex);
++ ret = -EINVAL;
++ goto err_mgmt_master;
++ }
++
++ read_skb->dev = mgmt_master;
++ clear_skb->dev = mgmt_master;
++ write_skb->dev = mgmt_master;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the write pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(write_skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ ack = mgmt_eth_data->ack;
++
++ if (ret <= 0) {
++ ret = -ETIMEDOUT;
++ kfree_skb(read_skb);
++ goto exit;
++ }
++
++ if (!ack) {
++ ret = -EINVAL;
++ kfree_skb(read_skb);
++ goto exit;
++ }
++
++ ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1,
++ !(val & QCA8K_MDIO_MASTER_BUSY), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
++ mgmt_eth_data, read_skb, &val);
++
++ if (ret < 0 && ret1 < 0) {
++ ret = ret1;
++ goto exit;
++ }
++
++ if (read) {
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the read pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(read_skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ ack = mgmt_eth_data->ack;
++
++ if (ret <= 0) {
++ ret = -ETIMEDOUT;
++ goto exit;
++ }
++
++ if (!ack) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK;
++ } else {
++ kfree_skb(read_skb);
++ }
++exit:
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the clear pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(clear_skb);
++
++ wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ mutex_unlock(&mgmt_eth_data->mutex);
++
++ return ret;
++
++ /* Error handling before lock */
++err_mgmt_master:
++ kfree_skb(read_skb);
++err_read_skb:
++ kfree_skb(clear_skb);
++err_clear_skb:
++ kfree_skb(write_skb);
++
++ return ret;
++}
++
+ static u32
+ qca8k_port_to_phy(int port)
+ {
+@@ -989,6 +1182,12 @@ qca8k_internal_mdio_write(struct mii_bus
+ {
+ struct qca8k_priv *priv = slave_bus->priv;
+ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ /* Use mdio Ethernet when available, fallback to legacy one on error */
++ ret = qca8k_phy_eth_command(priv, false, phy, regnum, data);
++ if (!ret)
++ return 0;
+
+ return qca8k_mdio_write(bus, phy, regnum, data);
+ }
+@@ -998,6 +1197,12 @@ qca8k_internal_mdio_read(struct mii_bus
+ {
+ struct qca8k_priv *priv = slave_bus->priv;
+ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ /* Use mdio Ethernet when available, fallback to legacy one on error */
++ ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0);
++ if (ret >= 0)
++ return ret;
+
+ return qca8k_mdio_read(bus, phy, regnum);
+ }
+@@ -1006,6 +1211,7 @@ static int
+ qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data)
+ {
+ struct qca8k_priv *priv = ds->priv;
++ int ret;
+
+ /* Check if the legacy mapping should be used and the
+ * port is not correctly mapped to the right PHY in the
+@@ -1014,6 +1220,11 @@ qca8k_phy_write(struct dsa_switch *ds, i
+ if (priv->legacy_phy_port_mapping)
+ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+
++ /* Use mdio Ethernet when available, fallback to legacy one on error */
++ ret = qca8k_phy_eth_command(priv, false, port, regnum, 0);
++ if (!ret)
++ return ret;
++
+ return qca8k_mdio_write(priv->bus, port, regnum, data);
+ }
+
+@@ -1030,6 +1241,11 @@ qca8k_phy_read(struct dsa_switch *ds, in
+ if (priv->legacy_phy_port_mapping)
+ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+
++ /* Use mdio Ethernet when available, fallback to legacy one on error */
++ ret = qca8k_phy_eth_command(priv, true, port, regnum, 0);
++ if (ret >= 0)
++ return ret;
++
+ ret = qca8k_mdio_read(priv->bus, port, regnum);
+
+ if (ret < 0)
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -14,6 +14,7 @@
+ #include <linux/dsa/tag_qca.h>
+
+ #define QCA8K_ETHERNET_MDIO_PRIORITY 7
++#define QCA8K_ETHERNET_PHY_PRIORITY 6
+ #define QCA8K_ETHERNET_TIMEOUT 100
+
+ #define QCA8K_NUM_PORTS 7
diff --git a/target/linux/generic/backport-6.1/766-v5.18-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch b/target/linux/generic/backport-6.1/766-v5.18-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch
new file mode 100644
index 0000000000..4ac0bc32fd
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch
@@ -0,0 +1,208 @@
+From 4264350acb75430d5021a1d7de56a33faf69a097 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:32 +0100
+Subject: [PATCH 13/16] net: dsa: qca8k: move page cache to driver priv
+
+There can be multiple qca8k switch on the same system. Move the static
+qca8k_current_page to qca8k_priv and make it specific for each switch.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 42 ++++++++++++++++++++---------------------
+ drivers/net/dsa/qca8k.h | 9 +++++++++
+ 2 files changed, 29 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -75,12 +75,6 @@ static const struct qca8k_mib_desc ar832
+ MIB_DESC(1, 0xac, "TXUnicast"),
+ };
+
+-/* The 32bit switch registers are accessed indirectly. To achieve this we need
+- * to set the page of the register. Track the last page that was set to reduce
+- * mdio writes
+- */
+-static u16 qca8k_current_page = 0xffff;
+-
+ static void
+ qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
+ {
+@@ -134,11 +128,13 @@ qca8k_mii_write32(struct mii_bus *bus, i
+ }
+
+ static int
+-qca8k_set_page(struct mii_bus *bus, u16 page)
++qca8k_set_page(struct qca8k_priv *priv, u16 page)
+ {
++ u16 *cached_page = &priv->mdio_cache.page;
++ struct mii_bus *bus = priv->bus;
+ int ret;
+
+- if (page == qca8k_current_page)
++ if (page == *cached_page)
+ return 0;
+
+ ret = bus->write(bus, 0x18, 0, page);
+@@ -148,7 +144,7 @@ qca8k_set_page(struct mii_bus *bus, u16
+ return ret;
+ }
+
+- qca8k_current_page = page;
++ *cached_page = page;
+ usleep_range(1000, 2000);
+ return 0;
+ }
+@@ -374,7 +370,7 @@ qca8k_regmap_read(void *ctx, uint32_t re
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- ret = qca8k_set_page(bus, page);
++ ret = qca8k_set_page(priv, page);
+ if (ret < 0)
+ goto exit;
+
+@@ -400,7 +396,7 @@ qca8k_regmap_write(void *ctx, uint32_t r
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- ret = qca8k_set_page(bus, page);
++ ret = qca8k_set_page(priv, page);
+ if (ret < 0)
+ goto exit;
+
+@@ -427,7 +423,7 @@ qca8k_regmap_update_bits(void *ctx, uint
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- ret = qca8k_set_page(bus, page);
++ ret = qca8k_set_page(priv, page);
+ if (ret < 0)
+ goto exit;
+
+@@ -1098,8 +1094,9 @@ qca8k_mdio_busy_wait(struct mii_bus *bus
+ }
+
+ static int
+-qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data)
++qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data)
+ {
++ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 val;
+ int ret;
+@@ -1116,7 +1113,7 @@ qca8k_mdio_write(struct mii_bus *bus, in
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- ret = qca8k_set_page(bus, page);
++ ret = qca8k_set_page(priv, page);
+ if (ret)
+ goto exit;
+
+@@ -1135,8 +1132,9 @@ exit:
+ }
+
+ static int
+-qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum)
++qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum)
+ {
++ struct mii_bus *bus = priv->bus;
+ u16 r1, r2, page;
+ u32 val;
+ int ret;
+@@ -1152,7 +1150,7 @@ qca8k_mdio_read(struct mii_bus *bus, int
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+- ret = qca8k_set_page(bus, page);
++ ret = qca8k_set_page(priv, page);
+ if (ret)
+ goto exit;
+
+@@ -1181,7 +1179,6 @@ static int
+ qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data)
+ {
+ struct qca8k_priv *priv = slave_bus->priv;
+- struct mii_bus *bus = priv->bus;
+ int ret;
+
+ /* Use mdio Ethernet when available, fallback to legacy one on error */
+@@ -1189,14 +1186,13 @@ qca8k_internal_mdio_write(struct mii_bus
+ if (!ret)
+ return 0;
+
+- return qca8k_mdio_write(bus, phy, regnum, data);
++ return qca8k_mdio_write(priv, phy, regnum, data);
+ }
+
+ static int
+ qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum)
+ {
+ struct qca8k_priv *priv = slave_bus->priv;
+- struct mii_bus *bus = priv->bus;
+ int ret;
+
+ /* Use mdio Ethernet when available, fallback to legacy one on error */
+@@ -1204,7 +1200,7 @@ qca8k_internal_mdio_read(struct mii_bus
+ if (ret >= 0)
+ return ret;
+
+- return qca8k_mdio_read(bus, phy, regnum);
++ return qca8k_mdio_read(priv, phy, regnum);
+ }
+
+ static int
+@@ -1225,7 +1221,7 @@ qca8k_phy_write(struct dsa_switch *ds, i
+ if (!ret)
+ return ret;
+
+- return qca8k_mdio_write(priv->bus, port, regnum, data);
++ return qca8k_mdio_write(priv, port, regnum, data);
+ }
+
+ static int
+@@ -1246,7 +1242,7 @@ qca8k_phy_read(struct dsa_switch *ds, in
+ if (ret >= 0)
+ return ret;
+
+- ret = qca8k_mdio_read(priv->bus, port, regnum);
++ ret = qca8k_mdio_read(priv, port, regnum);
+
+ if (ret < 0)
+ return 0xffff;
+@@ -3060,6 +3056,8 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ return PTR_ERR(priv->regmap);
+ }
+
++ priv->mdio_cache.page = 0xffff;
++
+ /* Check the detected switch id */
+ ret = qca8k_read_switch_id(priv);
+ if (ret)
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -363,6 +363,14 @@ struct qca8k_ports_config {
+ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+ };
+
++struct qca8k_mdio_cache {
++/* The 32bit switch registers are accessed indirectly. To achieve this we need
++ * to set the page of the register. Track the last page that was set to reduce
++ * mdio writes
++ */
++ u16 page;
++};
++
+ struct qca8k_priv {
+ u8 switch_id;
+ u8 switch_revision;
+@@ -383,6 +391,7 @@ struct qca8k_priv {
+ struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
+ struct qca8k_mgmt_eth_data mgmt_eth_data;
+ struct qca8k_mib_eth_data mib_eth_data;
++ struct qca8k_mdio_cache mdio_cache;
+ };
+
+ struct qca8k_mib_desc {
diff --git a/target/linux/generic/backport-6.1/766-v5.18-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch b/target/linux/generic/backport-6.1/766-v5.18-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch
new file mode 100644
index 0000000000..e2cb2721ce
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch
@@ -0,0 +1,164 @@
+From 2481d206fae7884cd07014fd1318e63af35e99eb Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:33 +0100
+Subject: [PATCH 14/16] net: dsa: qca8k: cache lo and hi for mdio write
+
+From Documentation, we can cache lo and hi the same way we do with the
+page. This massively reduce the mdio write as 3/4 of the time as we only
+require to write the lo or hi part for a mdio write.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 61 +++++++++++++++++++++++++++++++++--------
+ drivers/net/dsa/qca8k.h | 5 ++++
+ 2 files changed, 54 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -89,6 +89,44 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u
+ }
+
+ static int
++qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo)
++{
++ u16 *cached_lo = &priv->mdio_cache.lo;
++ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ if (lo == *cached_lo)
++ return 0;
++
++ ret = bus->write(bus, phy_id, regnum, lo);
++ if (ret < 0)
++ dev_err_ratelimited(&bus->dev,
++ "failed to write qca8k 32bit lo register\n");
++
++ *cached_lo = lo;
++ return 0;
++}
++
++static int
++qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi)
++{
++ u16 *cached_hi = &priv->mdio_cache.hi;
++ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ if (hi == *cached_hi)
++ return 0;
++
++ ret = bus->write(bus, phy_id, regnum, hi);
++ if (ret < 0)
++ dev_err_ratelimited(&bus->dev,
++ "failed to write qca8k 32bit hi register\n");
++
++ *cached_hi = hi;
++ return 0;
++}
++
++static int
+ qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
+ {
+ int ret;
+@@ -111,7 +149,7 @@ qca8k_mii_read32(struct mii_bus *bus, in
+ }
+
+ static void
+-qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
++qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val)
+ {
+ u16 lo, hi;
+ int ret;
+@@ -119,12 +157,9 @@ qca8k_mii_write32(struct mii_bus *bus, i
+ lo = val & 0xffff;
+ hi = (u16)(val >> 16);
+
+- ret = bus->write(bus, phy_id, regnum, lo);
++ ret = qca8k_set_lo(priv, phy_id, regnum, lo);
+ if (ret >= 0)
+- ret = bus->write(bus, phy_id, regnum + 1, hi);
+- if (ret < 0)
+- dev_err_ratelimited(&bus->dev,
+- "failed to write qca8k 32bit register\n");
++ ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi);
+ }
+
+ static int
+@@ -400,7 +435,7 @@ qca8k_regmap_write(void *ctx, uint32_t r
+ if (ret < 0)
+ goto exit;
+
+- qca8k_mii_write32(bus, 0x10 | r2, r1, val);
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+
+ exit:
+ mutex_unlock(&bus->mdio_lock);
+@@ -433,7 +468,7 @@ qca8k_regmap_update_bits(void *ctx, uint
+
+ val &= ~mask;
+ val |= write_val;
+- qca8k_mii_write32(bus, 0x10 | r2, r1, val);
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+
+ exit:
+ mutex_unlock(&bus->mdio_lock);
+@@ -1117,14 +1152,14 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ if (ret)
+ goto exit;
+
+- qca8k_mii_write32(bus, 0x10 | r2, r1, val);
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+
+ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+
+ exit:
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
+
+ mutex_unlock(&bus->mdio_lock);
+
+@@ -1154,7 +1189,7 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ if (ret)
+ goto exit;
+
+- qca8k_mii_write32(bus, 0x10 | r2, r1, val);
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+
+ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+@@ -1165,7 +1200,7 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+
+ exit:
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
+
+ mutex_unlock(&bus->mdio_lock);
+
+@@ -3057,6 +3092,8 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ }
+
+ priv->mdio_cache.page = 0xffff;
++ priv->mdio_cache.lo = 0xffff;
++ priv->mdio_cache.hi = 0xffff;
+
+ /* Check the detected switch id */
+ ret = qca8k_read_switch_id(priv);
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -369,6 +369,11 @@ struct qca8k_mdio_cache {
+ * mdio writes
+ */
+ u16 page;
++/* lo and hi can also be cached and from Documentation we can skip one
++ * extra mdio write if lo or hi is didn't change.
++ */
++ u16 lo;
++ u16 hi;
+ };
+
+ struct qca8k_priv {
diff --git a/target/linux/generic/backport-6.1/766-v5.18-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch b/target/linux/generic/backport-6.1/766-v5.18-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch
new file mode 100644
index 0000000000..5acd13dbad
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch
@@ -0,0 +1,206 @@
+From 90386223f44e2a751d7e9e9ac8f78ea33358a891 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:34 +0100
+Subject: [PATCH 15/16] net: dsa: qca8k: add support for larger read/write size
+ with mgmt Ethernet
+
+mgmt Ethernet packet can read/write up to 16byte at times. The len reg
+is limited to 15 (0xf). The switch actually sends and accepts data in 4
+different steps of len values.
+Len steps:
+- 0: nothing
+- 1-4: first 4 byte
+- 5-6: first 12 byte
+- 7-15: all 16 byte
+
+In the alloc skb function we check if the len is 16 and we fix it to a
+len of 15. It the read/write function interest to extract the real asked
+data. The tagger handler will always copy the fully 16byte with a READ
+command. This is useful for some big regs like the fdb reg that are
+more than 4byte of data. This permits to introduce a bulk function that
+will send and request the entire entry in one go.
+Write function is changed and it does now require to pass the pointer to
+val to also handle array val.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 61 +++++++++++++++++++++++++++--------------
+ 1 file changed, 41 insertions(+), 20 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -222,7 +222,9 @@ static void qca8k_rw_reg_ack_handler(str
+ if (cmd == MDIO_READ) {
+ mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
+
+- /* Get the rest of the 12 byte of data */
++ /* Get the rest of the 12 byte of data.
++ * The read/write function will extract the requested data.
++ */
+ if (len > QCA_HDR_MGMT_DATA1_LEN)
+ memcpy(mgmt_eth_data->data + 1, skb->data,
+ QCA_HDR_MGMT_DATA2_LEN);
+@@ -232,16 +234,30 @@ static void qca8k_rw_reg_ack_handler(str
+ }
+
+ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val,
+- int priority)
++ int priority, unsigned int len)
+ {
+ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++ unsigned int real_len;
+ struct sk_buff *skb;
++ u32 *data2;
+ u16 hdr;
+
+ skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
+ if (!skb)
+ return NULL;
+
++ /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte
++ * Actually for some reason the steps are:
++ * 0: nothing
++ * 1-4: first 4 byte
++ * 5-6: first 12 byte
++ * 7-15: all 16 byte
++ */
++ if (len == 16)
++ real_len = 15;
++ else
++ real_len = len;
++
+ skb_reset_mac_header(skb);
+ skb_set_network_header(skb, skb->len);
+
+@@ -254,7 +270,7 @@ static struct sk_buff *qca8k_alloc_mdio_
+ hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
+
+ mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, 4);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
+ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
+ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
+ QCA_HDR_MGMT_CHECK_CODE_VAL);
+@@ -264,7 +280,9 @@ static struct sk_buff *qca8k_alloc_mdio_
+
+ mgmt_ethhdr->hdr = htons(hdr);
+
+- skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
++ data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
++ if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN)
++ memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
+
+ return skb;
+ }
+@@ -277,7 +295,7 @@ static void qca8k_mdio_header_fill_seq_n
+ mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
+ }
+
+-static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val)
++static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+ {
+ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
+ struct sk_buff *skb;
+@@ -285,7 +303,7 @@ static int qca8k_read_eth(struct qca8k_p
+ int ret;
+
+ skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL,
+- QCA8K_ETHERNET_MDIO_PRIORITY);
++ QCA8K_ETHERNET_MDIO_PRIORITY, len);
+ if (!skb)
+ return -ENOMEM;
+
+@@ -313,6 +331,9 @@ static int qca8k_read_eth(struct qca8k_p
+ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
+
+ *val = mgmt_eth_data->data[0];
++ if (len > QCA_HDR_MGMT_DATA1_LEN)
++ memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN);
++
+ ack = mgmt_eth_data->ack;
+
+ mutex_unlock(&mgmt_eth_data->mutex);
+@@ -326,15 +347,15 @@ static int qca8k_read_eth(struct qca8k_p
+ return 0;
+ }
+
+-static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 val)
++static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+ {
+ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
+ struct sk_buff *skb;
+ bool ack;
+ int ret;
+
+- skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, &val,
+- QCA8K_ETHERNET_MDIO_PRIORITY);
++ skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val,
++ QCA8K_ETHERNET_MDIO_PRIORITY, len);
+ if (!skb)
+ return -ENOMEM;
+
+@@ -380,14 +401,14 @@ qca8k_regmap_update_bits_eth(struct qca8
+ u32 val = 0;
+ int ret;
+
+- ret = qca8k_read_eth(priv, reg, &val);
++ ret = qca8k_read_eth(priv, reg, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ val &= ~mask;
+ val |= write_val;
+
+- return qca8k_write_eth(priv, reg, val);
++ return qca8k_write_eth(priv, reg, &val, sizeof(val));
+ }
+
+ static int
+@@ -398,7 +419,7 @@ qca8k_regmap_read(void *ctx, uint32_t re
+ u16 r1, r2, page;
+ int ret;
+
+- if (!qca8k_read_eth(priv, reg, val))
++ if (!qca8k_read_eth(priv, reg, val, sizeof(val)))
+ return 0;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+@@ -424,7 +445,7 @@ qca8k_regmap_write(void *ctx, uint32_t r
+ u16 r1, r2, page;
+ int ret;
+
+- if (!qca8k_write_eth(priv, reg, val))
++ if (!qca8k_write_eth(priv, reg, &val, sizeof(val)))
+ return 0;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+@@ -959,21 +980,21 @@ qca8k_phy_eth_command(struct qca8k_priv
+ }
+
+ /* Prealloc all the needed skb before the lock */
+- write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL,
+- &write_val, QCA8K_ETHERNET_PHY_PRIORITY);
++ write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val,
++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val));
+ if (!write_skb)
+ return -ENOMEM;
+
+- clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL,
+- &clear_val, QCA8K_ETHERNET_PHY_PRIORITY);
++ clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val,
++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
+ if (!write_skb) {
+ ret = -ENOMEM;
+ goto err_clear_skb;
+ }
+
+- read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL,
+- &clear_val, QCA8K_ETHERNET_PHY_PRIORITY);
+- if (!write_skb) {
++ read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val,
++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
++ if (!read_skb) {
+ ret = -ENOMEM;
+ goto err_read_skb;
+ }
diff --git a/target/linux/generic/backport-6.1/766-v5.18-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch b/target/linux/generic/backport-6.1/766-v5.18-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch
new file mode 100644
index 0000000000..f26c6b91ac
--- /dev/null
+++ b/target/linux/generic/backport-6.1/766-v5.18-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch
@@ -0,0 +1,104 @@
+From 4f3701fc599820568ba4395070d34e4248800fc0 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Wed, 2 Feb 2022 01:03:35 +0100
+Subject: [PATCH 16/16] net: dsa: qca8k: introduce qca8k_bulk_read/write
+ function
+
+Introduce qca8k_bulk_read/write() function to use mgmt Ethernet way to
+read/write packet in bulk. Make use of this new function in the fdb
+function and while at it reduce the reg for fdb_read from 4 to 3 as the
+max bit for the ARL(fdb) table is 83 bits.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 55 ++++++++++++++++++++++++++++++++---------
+ 1 file changed, 43 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -412,6 +412,43 @@ qca8k_regmap_update_bits_eth(struct qca8
+ }
+
+ static int
++qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ int i, count = len / sizeof(u32), ret;
++
++ if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len))
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ ret = regmap_read(priv->regmap, reg + (i * 4), val + i);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int
++qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ int i, count = len / sizeof(u32), ret;
++ u32 tmp;
++
++ if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len))
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ tmp = val[i];
++
++ ret = regmap_write(priv->regmap, reg + (i * 4), tmp);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int
+ qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+@@ -546,17 +583,13 @@ qca8k_busy_wait(struct qca8k_priv *priv,
+ static int
+ qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+ {
+- u32 reg[4], val;
+- int i, ret;
++ u32 reg[3];
++ int ret;
+
+ /* load the ARL table into an array */
+- for (i = 0; i < 4; i++) {
+- ret = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4), &val);
+- if (ret < 0)
+- return ret;
+-
+- reg[i] = val;
+- }
++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++ if (ret)
++ return ret;
+
+ /* vid - 83:72 */
+ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
+@@ -580,7 +613,6 @@ qca8k_fdb_write(struct qca8k_priv *priv,
+ u8 aging)
+ {
+ u32 reg[3] = { 0 };
+- int i;
+
+ /* vid - 83:72 */
+ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
+@@ -597,8 +629,7 @@ qca8k_fdb_write(struct qca8k_priv *priv,
+ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+
+ /* load the array into the ARL table */
+- for (i = 0; i < 3; i++)
+- qca8k_write(priv, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]);
++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
+ }
+
+ static int
diff --git a/target/linux/generic/backport-6.1/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch b/target/linux/generic/backport-6.1/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch
new file mode 100644
index 0000000000..34607c223c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch
@@ -0,0 +1,28 @@
+From c3664d913dc115cab4a5fdb5634df4887048000e Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Fri, 4 Feb 2022 13:03:36 +0300
+Subject: [PATCH 1/1] net: dsa: qca8k: check correct variable in
+ qca8k_phy_eth_command()
+
+This is a copy and paste bug. It was supposed to check "clear_skb"
+instead of "write_skb".
+
+Fixes: 2cd548566384 ("net: dsa: qca8k: add support for phy read/write with mgmt Ethernet")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1018,7 +1018,7 @@ qca8k_phy_eth_command(struct qca8k_priv
+
+ clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val,
+ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
+- if (!write_skb) {
++ if (!clear_skb) {
+ ret = -ENOMEM;
+ goto err_clear_skb;
+ }
diff --git a/target/linux/generic/backport-6.1/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch b/target/linux/generic/backport-6.1/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch
new file mode 100644
index 0000000000..d8cf266309
--- /dev/null
+++ b/target/linux/generic/backport-6.1/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch
@@ -0,0 +1,34 @@
+From 4f5e483b8c7a644733db941a1ae00173baa7b463 Mon Sep 17 00:00:00 2001
+From: kernel test robot <lkp@intel.com>
+Date: Thu, 10 Feb 2022 06:13:04 +0800
+Subject: [PATCH 1/1] net: dsa: qca8k: fix noderef.cocci warnings
+
+drivers/net/dsa/qca8k.c:422:37-43: ERROR: application of sizeof to pointer
+
+ sizeof when applied to a pointer typed expression gives the size of
+ the pointer
+
+Generated by: scripts/coccinelle/misc/noderef.cocci
+
+Fixes: 90386223f44e ("net: dsa: qca8k: add support for larger read/write size with mgmt Ethernet")
+CC: Ansuel Smith <ansuelsmth@gmail.com>
+Reported-by: kernel test robot <lkp@intel.com>
+Signed-off-by: kernel test robot <lkp@intel.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Link: https://lore.kernel.org/r/20220209221304.GA17529@d2214a582157
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca8k.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -456,7 +456,7 @@ qca8k_regmap_read(void *ctx, uint32_t re
+ u16 r1, r2, page;
+ int ret;
+
+- if (!qca8k_read_eth(priv, reg, val, sizeof(val)))
++ if (!qca8k_read_eth(priv, reg, val, sizeof(*val)))
+ return 0;
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
diff --git a/target/linux/generic/backport-6.1/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch b/target/linux/generic/backport-6.1/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch
new file mode 100644
index 0000000000..57df4c126e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch
@@ -0,0 +1,79 @@
+From 69fd055957a02309ffdc23d887a01988b6e5bab1 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sat, 16 Apr 2022 01:30:12 +0200
+Subject: [PATCH 1/6] net: dsa: qca8k: drop MTU tracking from qca8k_priv
+
+DSA set the CPU port based on the largest MTU of all the slave ports.
+Based on this we can drop the MTU array from qca8k_priv and set the
+port_change_mtu logic on DSA changing MTU of the CPU port as the switch
+have a global MTU settingfor each port.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 26 +++++++++-----------------
+ drivers/net/dsa/qca8k.h | 1 -
+ 2 files changed, 9 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1803,16 +1803,6 @@ qca8k_setup(struct dsa_switch *ds)
+ QCA8K_PORT_HOL_CTRL1_WRED_EN,
+ mask);
+ }
+-
+- /* Set initial MTU for every port.
+- * We have only have a general MTU setting. So track
+- * every port and set the max across all port.
+- * Set per port MTU to 1500 as the MTU change function
+- * will add the overhead and if its set to 1518 then it
+- * will apply the overhead again and we will end up with
+- * MTU of 1536 instead of 1518
+- */
+- priv->port_mtu[i] = ETH_DATA_LEN;
+ }
+
+ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
+@@ -2525,13 +2515,16 @@ static int
+ qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+ {
+ struct qca8k_priv *priv = ds->priv;
+- int ret, i, mtu = 0;
+-
+- priv->port_mtu[port] = new_mtu;
++ int ret;
+
+- for (i = 0; i < QCA8K_NUM_PORTS; i++)
+- if (priv->port_mtu[i] > mtu)
+- mtu = priv->port_mtu[i];
++ /* We have only have a general MTU setting.
++ * DSA always set the CPU port's MTU to the largest MTU of the slave
++ * ports.
++ * Setting MTU just for the CPU port is sufficient to correctly set a
++ * value for every port.
++ */
++ if (!dsa_is_cpu_port(ds, port))
++ return 0;
+
+ /* To change the MAX_FRAME_SIZE the cpu ports must be off or
+ * the switch panics.
+@@ -2545,7 +2538,7 @@ qca8k_port_change_mtu(struct dsa_switch
+ qca8k_port_set_status(priv, 6, 0);
+
+ /* Include L2 header / FCS length */
+- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN);
++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN);
+
+ if (priv->port_sts[0].enabled)
+ qca8k_port_set_status(priv, 0, 1);
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -392,7 +392,6 @@ struct qca8k_priv {
+ struct device *dev;
+ struct dsa_switch_ops ops;
+ struct gpio_desc *reset_gpio;
+- unsigned int port_mtu[QCA8K_NUM_PORTS];
+ struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
+ struct qca8k_mgmt_eth_data mgmt_eth_data;
+ struct qca8k_mib_eth_data mib_eth_data;
diff --git a/target/linux/generic/backport-6.1/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch b/target/linux/generic/backport-6.1/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch
new file mode 100644
index 0000000000..3cacd7e4fd
--- /dev/null
+++ b/target/linux/generic/backport-6.1/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch
@@ -0,0 +1,116 @@
+From 2b8fd87af7f156942971789abac8ee2bb60c03bc Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sat, 16 Apr 2022 01:30:13 +0200
+Subject: [PATCH 2/6] net: dsa: qca8k: drop port_sts from qca8k_priv
+
+Port_sts is a thing of the past for this driver. It was something
+present on the initial implementation of this driver and parts of the
+original struct were dropped over time. Using an array of int to store if
+a port is enabled or not to handle PM operation seems overkill. Switch
+and use a simple u8 to store the port status where each bit correspond
+to a port. (bit is set port is enabled, bit is not set, port is disabled)
+Also add some comments to better describe why we need to track port
+status.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 15 +++++++++------
+ drivers/net/dsa/qca8k.h | 9 ++++-----
+ 2 files changed, 13 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -2494,7 +2494,7 @@ qca8k_port_enable(struct dsa_switch *ds,
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+
+ qca8k_port_set_status(priv, port, 1);
+- priv->port_sts[port].enabled = 1;
++ priv->port_enabled_map |= BIT(port);
+
+ if (dsa_is_user_port(ds, port))
+ phy_support_asym_pause(phy);
+@@ -2508,7 +2508,7 @@ qca8k_port_disable(struct dsa_switch *ds
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+
+ qca8k_port_set_status(priv, port, 0);
+- priv->port_sts[port].enabled = 0;
++ priv->port_enabled_map &= ~BIT(port);
+ }
+
+ static int
+@@ -2531,19 +2531,19 @@ qca8k_port_change_mtu(struct dsa_switch
+ * Turn off both cpu ports before applying the new value to prevent
+ * this.
+ */
+- if (priv->port_sts[0].enabled)
++ if (priv->port_enabled_map & BIT(0))
+ qca8k_port_set_status(priv, 0, 0);
+
+- if (priv->port_sts[6].enabled)
++ if (priv->port_enabled_map & BIT(6))
+ qca8k_port_set_status(priv, 6, 0);
+
+ /* Include L2 header / FCS length */
+ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN);
+
+- if (priv->port_sts[0].enabled)
++ if (priv->port_enabled_map & BIT(0))
+ qca8k_port_set_status(priv, 0, 1);
+
+- if (priv->port_sts[6].enabled)
++ if (priv->port_enabled_map & BIT(6))
+ qca8k_port_set_status(priv, 6, 1);
+
+ return ret;
+@@ -3199,13 +3199,16 @@ static void qca8k_sw_shutdown(struct mdi
+ static void
+ qca8k_set_pm(struct qca8k_priv *priv, int enable)
+ {
+- int i;
++ int port;
+
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- if (!priv->port_sts[i].enabled)
++ for (port = 0; port < QCA8K_NUM_PORTS; port++) {
++ /* Do not enable on resume if the port was
++ * disabled before.
++ */
++ if (!(priv->port_enabled_map & BIT(port)))
+ continue;
+
+- qca8k_port_set_status(priv, i, enable);
++ qca8k_port_set_status(priv, port, enable);
+ }
+ }
+
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -324,10 +324,6 @@ enum qca8k_mid_cmd {
+ QCA8K_MIB_CAST = 3,
+ };
+
+-struct ar8xxx_port_status {
+- int enabled;
+-};
+-
+ struct qca8k_match_data {
+ u8 id;
+ bool reduced_package;
+@@ -382,11 +378,14 @@ struct qca8k_priv {
+ u8 mirror_rx;
+ u8 mirror_tx;
+ u8 lag_hash_mode;
++ /* Each bit correspond to a port. This switch can support a max of 7 port.
++ * Bit 1: port enabled. Bit 0: port disabled.
++ */
++ u8 port_enabled_map;
+ bool legacy_phy_port_mapping;
+ struct qca8k_ports_config ports_config;
+ struct regmap *regmap;
+ struct mii_bus *bus;
+- struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS];
+ struct dsa_switch *ds;
+ struct mutex reg_mutex;
+ struct device *dev;
diff --git a/target/linux/generic/backport-6.1/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch b/target/linux/generic/backport-6.1/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch
new file mode 100644
index 0000000000..12c3221077
--- /dev/null
+++ b/target/linux/generic/backport-6.1/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch
@@ -0,0 +1,173 @@
+From 8255212e4130bd2dc1463286a3dddb74797bbdc1 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sat, 16 Apr 2022 01:30:14 +0200
+Subject: [PATCH 3/6] net: dsa: qca8k: rework and simplify mdiobus logic
+
+In an attempt to reduce qca8k_priv space, rework and simplify mdiobus
+logic.
+We now declare a mdiobus instead of relying on DSA phy_read/write even
+if a mdio node is not present. This is all to make the qca8k ops static
+and not switch specific. With a legacy implementation where port doesn't
+have a phy map declared in the dts with a mdio node, we declare a
+'qca8k-legacy' mdiobus. The conversion logic is used as legacy read and
+write ops are used instead of the internal one.
+Also drop the legacy_phy_port_mapping as we now declare mdiobus with ops
+that already address the workaround.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 95 +++++++++++++----------------------------
+ drivers/net/dsa/qca8k.h | 1 -
+ 2 files changed, 29 insertions(+), 67 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1291,83 +1291,63 @@ qca8k_internal_mdio_read(struct mii_bus
+ }
+
+ static int
+-qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data)
++qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data)
+ {
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+
+- /* Check if the legacy mapping should be used and the
+- * port is not correctly mapped to the right PHY in the
+- * devicetree
+- */
+- if (priv->legacy_phy_port_mapping)
+- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+-
+- /* Use mdio Ethernet when available, fallback to legacy one on error */
+- ret = qca8k_phy_eth_command(priv, false, port, regnum, 0);
+- if (!ret)
+- return ret;
+-
+- return qca8k_mdio_write(priv, port, regnum, data);
++ return qca8k_internal_mdio_write(slave_bus, port, regnum, data);
+ }
+
+ static int
+-qca8k_phy_read(struct dsa_switch *ds, int port, int regnum)
++qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum)
+ {
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- /* Check if the legacy mapping should be used and the
+- * port is not correctly mapped to the right PHY in the
+- * devicetree
+- */
+- if (priv->legacy_phy_port_mapping)
+- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+-
+- /* Use mdio Ethernet when available, fallback to legacy one on error */
+- ret = qca8k_phy_eth_command(priv, true, port, regnum, 0);
+- if (ret >= 0)
+- return ret;
+-
+- ret = qca8k_mdio_read(priv, port, regnum);
+-
+- if (ret < 0)
+- return 0xffff;
++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+
+- return ret;
++ return qca8k_internal_mdio_read(slave_bus, port, regnum);
+ }
+
+ static int
+-qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio)
++qca8k_mdio_register(struct qca8k_priv *priv)
+ {
+ struct dsa_switch *ds = priv->ds;
++ struct device_node *mdio;
+ struct mii_bus *bus;
+
+ bus = devm_mdiobus_alloc(ds->dev);
+-
+ if (!bus)
+ return -ENOMEM;
+
+ bus->priv = (void *)priv;
+- bus->name = "qca8k slave mii";
+- bus->read = qca8k_internal_mdio_read;
+- bus->write = qca8k_internal_mdio_write;
+- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d",
+- ds->index);
+-
+ bus->parent = ds->dev;
+ bus->phy_mask = ~ds->phys_mii_mask;
+-
+ ds->slave_mii_bus = bus;
+
+- return devm_of_mdiobus_register(priv->dev, bus, mdio);
++ /* Check if the devicetree declare the port:phy mapping */
++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
++ if (of_device_is_available(mdio)) {
++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", ds->index);
++ bus->name = "qca8k slave mii";
++ bus->read = qca8k_internal_mdio_read;
++ bus->write = qca8k_internal_mdio_write;
++ return devm_of_mdiobus_register(priv->dev, bus, mdio);
++ }
++
++ /* If a mapping can't be found the legacy mapping is used,
++ * using the qca8k_port_to_phy function
++ */
++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d",
++ ds->dst->index, ds->index);
++ bus->name = "qca8k-legacy slave mii";
++ bus->read = qca8k_legacy_mdio_read;
++ bus->write = qca8k_legacy_mdio_write;
++ return devm_mdiobus_register(priv->dev, bus);
+ }
+
+ static int
+ qca8k_setup_mdio_bus(struct qca8k_priv *priv)
+ {
+ u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg;
+- struct device_node *ports, *port, *mdio;
++ struct device_node *ports, *port;
+ phy_interface_t mode;
+ int err;
+
+@@ -1429,24 +1409,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+ QCA8K_MDIO_MASTER_EN);
+ }
+
+- /* Check if the devicetree declare the port:phy mapping */
+- mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
+- if (of_device_is_available(mdio)) {
+- err = qca8k_mdio_register(priv, mdio);
+- if (err)
+- of_node_put(mdio);
+-
+- return err;
+- }
+-
+- /* If a mapping can't be found the legacy mapping is used,
+- * using the qca8k_port_to_phy function
+- */
+- priv->legacy_phy_port_mapping = true;
+- priv->ops.phy_read = qca8k_phy_read;
+- priv->ops.phy_write = qca8k_phy_write;
+-
+- return 0;
++ return qca8k_mdio_register(priv);
+ }
+
+ static int
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -382,7 +382,6 @@ struct qca8k_priv {
+ * Bit 1: port enabled. Bit 0: port disabled.
+ */
+ u8 port_enabled_map;
+- bool legacy_phy_port_mapping;
+ struct qca8k_ports_config ports_config;
+ struct regmap *regmap;
+ struct mii_bus *bus;
diff --git a/target/linux/generic/backport-6.1/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch b/target/linux/generic/backport-6.1/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch
new file mode 100644
index 0000000000..8641000abb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch
@@ -0,0 +1,39 @@
+From 2349b83a2486c55b9dd225326f0172a84a43c5e4 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sat, 16 Apr 2022 01:30:15 +0200
+Subject: [PATCH 4/6] net: dsa: qca8k: drop dsa_switch_ops from qca8k_priv
+
+Now that dsa_switch_ops is not switch specific anymore, we can drop it
+from qca8k_priv and use the static ops directly for the dsa_switch
+pointer.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 3 +--
+ drivers/net/dsa/qca8k.h | 1 -
+ 2 files changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -3121,8 +3121,7 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ priv->ds->dev = &mdiodev->dev;
+ priv->ds->num_ports = QCA8K_NUM_PORTS;
+ priv->ds->priv = priv;
+- priv->ops = qca8k_switch_ops;
+- priv->ds->ops = &priv->ops;
++ priv->ds->ops = &qca8k_switch_ops;
+ mutex_init(&priv->reg_mutex);
+ dev_set_drvdata(&mdiodev->dev, priv);
+
+--- a/drivers/net/dsa/qca8k.h
++++ b/drivers/net/dsa/qca8k.h
+@@ -388,7 +388,6 @@ struct qca8k_priv {
+ struct dsa_switch *ds;
+ struct mutex reg_mutex;
+ struct device *dev;
+- struct dsa_switch_ops ops;
+ struct gpio_desc *reset_gpio;
+ struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
+ struct qca8k_mgmt_eth_data mgmt_eth_data;
diff --git a/target/linux/generic/backport-6.1/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch b/target/linux/generic/backport-6.1/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch
new file mode 100644
index 0000000000..b14b22091b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch
@@ -0,0 +1,33 @@
+From 6cfc03b602200c5cbbd8d906fd905547814e83df Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sat, 16 Apr 2022 01:30:16 +0200
+Subject: [PATCH 5/6] net: dsa: qca8k: correctly handle mdio read error
+
+Restore original way to handle mdio read error by returning 0xffff.
+This was wrongly changed when the internal_mdio_read was introduced,
+now that both legacy and internal use the same function, make sure that
+they behave the same way.
+
+Fixes: ce062a0adbfe ("net: dsa: qca8k: fix kernel panic with legacy mdio mapping")
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1287,7 +1287,12 @@ qca8k_internal_mdio_read(struct mii_bus
+ if (ret >= 0)
+ return ret;
+
+- return qca8k_mdio_read(priv, phy, regnum);
++ ret = qca8k_mdio_read(priv, phy, regnum);
++
++ if (ret < 0)
++ return 0xffff;
++
++ return ret;
+ }
+
+ static int
diff --git a/target/linux/generic/backport-6.1/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch b/target/linux/generic/backport-6.1/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch
new file mode 100644
index 0000000000..094686f11b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch
@@ -0,0 +1,44 @@
+From 8d1af50842bf2774f4edc57054206e909117469b Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sat, 16 Apr 2022 01:30:17 +0200
+Subject: [PATCH 6/6] net: dsa: qca8k: unify bus id naming with legacy and OF
+ mdio bus
+
+Add support for multiple switch with OF mdio bus declaration.
+Unify the bus id naming and use the same logic for both legacy and OF
+mdio bus.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca8k.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/qca8k.c
++++ b/drivers/net/dsa/qca8k.c
+@@ -1323,6 +1323,8 @@ qca8k_mdio_register(struct qca8k_priv *p
+ return -ENOMEM;
+
+ bus->priv = (void *)priv;
++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d",
++ ds->dst->index, ds->index);
+ bus->parent = ds->dev;
+ bus->phy_mask = ~ds->phys_mii_mask;
+ ds->slave_mii_bus = bus;
+@@ -1330,7 +1332,6 @@ qca8k_mdio_register(struct qca8k_priv *p
+ /* Check if the devicetree declare the port:phy mapping */
+ mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
+ if (of_device_is_available(mdio)) {
+- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", ds->index);
+ bus->name = "qca8k slave mii";
+ bus->read = qca8k_internal_mdio_read;
+ bus->write = qca8k_internal_mdio_write;
+@@ -1340,8 +1341,6 @@ qca8k_mdio_register(struct qca8k_priv *p
+ /* If a mapping can't be found the legacy mapping is used,
+ * using the qca8k_port_to_phy function
+ */
+- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d",
+- ds->dst->index, ds->index);
+ bus->name = "qca8k-legacy slave mii";
+ bus->read = qca8k_legacy_mdio_read;
+ bus->write = qca8k_legacy_mdio_write;
diff --git a/target/linux/generic/backport-6.1/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch b/target/linux/generic/backport-6.1/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch
new file mode 100644
index 0000000000..1534113f1d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch
@@ -0,0 +1,7389 @@
+From 4bbaf764e1e1786eb937fdb62172f656f512e116 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 13 Jul 2022 22:53:50 +0200
+Subject: [PATCH 1/1] net: dsa: qca8k: move driver to qca dir
+
+Move qca8k driver to qca dir in preparation for code split and
+introduction of ipq4019 switch based on qca8k.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/Kconfig | 8 --------
+ drivers/net/dsa/Makefile | 1 -
+ drivers/net/dsa/qca/Kconfig | 8 ++++++++
+ drivers/net/dsa/qca/Makefile | 1 +
+ drivers/net/dsa/{ => qca}/qca8k.c | 0
+ drivers/net/dsa/{ => qca}/qca8k.h | 0
+ 6 files changed, 9 insertions(+), 9 deletions(-)
+ rename drivers/net/dsa/{ => qca}/qca8k.c (100%)
+ rename drivers/net/dsa/{ => qca}/qca8k.h (100%)
+
+--- a/drivers/net/dsa/Kconfig
++++ b/drivers/net/dsa/Kconfig
+@@ -60,14 +60,6 @@ source "drivers/net/dsa/sja1105/Kconfig"
+
+ source "drivers/net/dsa/xrs700x/Kconfig"
+
+-config NET_DSA_QCA8K
+- tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
+- select NET_DSA_TAG_QCA
+- select REGMAP
+- help
+- This enables support for the Qualcomm Atheros QCA8K Ethernet
+- switch chips.
+-
+ config NET_DSA_REALTEK_SMI
+ tristate "Realtek SMI Ethernet switch family support"
+ select NET_DSA_TAG_RTL4_A
+--- a/drivers/net/dsa/Makefile
++++ b/drivers/net/dsa/Makefile
+@@ -8,7 +8,6 @@ endif
+ obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
+ obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o
+ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
+-obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
+ obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o
+ realtek-smi-objs := realtek-smi-core.o rtl8366.o rtl8366rb.o
+ obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
+--- a/drivers/net/dsa/qca/Kconfig
++++ b/drivers/net/dsa/qca/Kconfig
+@@ -7,3 +7,11 @@ config NET_DSA_AR9331
+ help
+ This enables support for the Qualcomm Atheros AR9331 built-in Ethernet
+ switch.
++
++config NET_DSA_QCA8K
++ tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
++ select NET_DSA_TAG_QCA
++ select REGMAP
++ help
++ This enables support for the Qualcomm Atheros QCA8K Ethernet
++ switch chips.
+--- a/drivers/net/dsa/qca/Makefile
++++ b/drivers/net/dsa/qca/Makefile
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o
++obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
+--- /dev/null
++++ b/drivers/net/dsa/qca/qca8k.c
+@@ -0,0 +1,3243 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
++ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
++ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2016 John Crispin <john@phrozen.org>
++ */
++
++#include <linux/module.h>
++#include <linux/phy.h>
++#include <linux/netdevice.h>
++#include <linux/bitfield.h>
++#include <linux/regmap.h>
++#include <net/dsa.h>
++#include <linux/of_net.h>
++#include <linux/of_mdio.h>
++#include <linux/of_platform.h>
++#include <linux/if_bridge.h>
++#include <linux/mdio.h>
++#include <linux/phylink.h>
++#include <linux/gpio/consumer.h>
++#include <linux/etherdevice.h>
++#include <linux/dsa/tag_qca.h>
++
++#include "qca8k.h"
++
++#define MIB_DESC(_s, _o, _n) \
++ { \
++ .size = (_s), \
++ .offset = (_o), \
++ .name = (_n), \
++ }
++
++static const struct qca8k_mib_desc ar8327_mib[] = {
++ MIB_DESC(1, 0x00, "RxBroad"),
++ MIB_DESC(1, 0x04, "RxPause"),
++ MIB_DESC(1, 0x08, "RxMulti"),
++ MIB_DESC(1, 0x0c, "RxFcsErr"),
++ MIB_DESC(1, 0x10, "RxAlignErr"),
++ MIB_DESC(1, 0x14, "RxRunt"),
++ MIB_DESC(1, 0x18, "RxFragment"),
++ MIB_DESC(1, 0x1c, "Rx64Byte"),
++ MIB_DESC(1, 0x20, "Rx128Byte"),
++ MIB_DESC(1, 0x24, "Rx256Byte"),
++ MIB_DESC(1, 0x28, "Rx512Byte"),
++ MIB_DESC(1, 0x2c, "Rx1024Byte"),
++ MIB_DESC(1, 0x30, "Rx1518Byte"),
++ MIB_DESC(1, 0x34, "RxMaxByte"),
++ MIB_DESC(1, 0x38, "RxTooLong"),
++ MIB_DESC(2, 0x3c, "RxGoodByte"),
++ MIB_DESC(2, 0x44, "RxBadByte"),
++ MIB_DESC(1, 0x4c, "RxOverFlow"),
++ MIB_DESC(1, 0x50, "Filtered"),
++ MIB_DESC(1, 0x54, "TxBroad"),
++ MIB_DESC(1, 0x58, "TxPause"),
++ MIB_DESC(1, 0x5c, "TxMulti"),
++ MIB_DESC(1, 0x60, "TxUnderRun"),
++ MIB_DESC(1, 0x64, "Tx64Byte"),
++ MIB_DESC(1, 0x68, "Tx128Byte"),
++ MIB_DESC(1, 0x6c, "Tx256Byte"),
++ MIB_DESC(1, 0x70, "Tx512Byte"),
++ MIB_DESC(1, 0x74, "Tx1024Byte"),
++ MIB_DESC(1, 0x78, "Tx1518Byte"),
++ MIB_DESC(1, 0x7c, "TxMaxByte"),
++ MIB_DESC(1, 0x80, "TxOverSize"),
++ MIB_DESC(2, 0x84, "TxByte"),
++ MIB_DESC(1, 0x8c, "TxCollision"),
++ MIB_DESC(1, 0x90, "TxAbortCol"),
++ MIB_DESC(1, 0x94, "TxMultiCol"),
++ MIB_DESC(1, 0x98, "TxSingleCol"),
++ MIB_DESC(1, 0x9c, "TxExcDefer"),
++ MIB_DESC(1, 0xa0, "TxDefer"),
++ MIB_DESC(1, 0xa4, "TxLateCol"),
++ MIB_DESC(1, 0xa8, "RXUnicast"),
++ MIB_DESC(1, 0xac, "TXUnicast"),
++};
++
++static void
++qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
++{
++ regaddr >>= 1;
++ *r1 = regaddr & 0x1e;
++
++ regaddr >>= 5;
++ *r2 = regaddr & 0x7;
++
++ regaddr >>= 3;
++ *page = regaddr & 0x3ff;
++}
++
++static int
++qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo)
++{
++ u16 *cached_lo = &priv->mdio_cache.lo;
++ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ if (lo == *cached_lo)
++ return 0;
++
++ ret = bus->write(bus, phy_id, regnum, lo);
++ if (ret < 0)
++ dev_err_ratelimited(&bus->dev,
++ "failed to write qca8k 32bit lo register\n");
++
++ *cached_lo = lo;
++ return 0;
++}
++
++static int
++qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi)
++{
++ u16 *cached_hi = &priv->mdio_cache.hi;
++ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ if (hi == *cached_hi)
++ return 0;
++
++ ret = bus->write(bus, phy_id, regnum, hi);
++ if (ret < 0)
++ dev_err_ratelimited(&bus->dev,
++ "failed to write qca8k 32bit hi register\n");
++
++ *cached_hi = hi;
++ return 0;
++}
++
++static int
++qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
++{
++ int ret;
++
++ ret = bus->read(bus, phy_id, regnum);
++ if (ret >= 0) {
++ *val = ret;
++ ret = bus->read(bus, phy_id, regnum + 1);
++ *val |= ret << 16;
++ }
++
++ if (ret < 0) {
++ dev_err_ratelimited(&bus->dev,
++ "failed to read qca8k 32bit register\n");
++ *val = 0;
++ return ret;
++ }
++
++ return 0;
++}
++
++static void
++qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val)
++{
++ u16 lo, hi;
++ int ret;
++
++ lo = val & 0xffff;
++ hi = (u16)(val >> 16);
++
++ ret = qca8k_set_lo(priv, phy_id, regnum, lo);
++ if (ret >= 0)
++ ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi);
++}
++
++static int
++qca8k_set_page(struct qca8k_priv *priv, u16 page)
++{
++ u16 *cached_page = &priv->mdio_cache.page;
++ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ if (page == *cached_page)
++ return 0;
++
++ ret = bus->write(bus, 0x18, 0, page);
++ if (ret < 0) {
++ dev_err_ratelimited(&bus->dev,
++ "failed to set qca8k page\n");
++ return ret;
++ }
++
++ *cached_page = page;
++ usleep_range(1000, 2000);
++ return 0;
++}
++
++static int
++qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
++{
++ return regmap_read(priv->regmap, reg, val);
++}
++
++static int
++qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
++{
++ return regmap_write(priv->regmap, reg, val);
++}
++
++static int
++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++{
++ return regmap_update_bits(priv->regmap, reg, mask, write_val);
++}
++
++static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
++{
++ struct qca8k_mgmt_eth_data *mgmt_eth_data;
++ struct qca8k_priv *priv = ds->priv;
++ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++ u8 len, cmd;
++
++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
++ mgmt_eth_data = &priv->mgmt_eth_data;
++
++ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
++ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
++
++ /* Make sure the seq match the requested packet */
++ if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
++ mgmt_eth_data->ack = true;
++
++ if (cmd == MDIO_READ) {
++ mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
++
++ /* Get the rest of the 12 byte of data.
++ * The read/write function will extract the requested data.
++ */
++ if (len > QCA_HDR_MGMT_DATA1_LEN)
++ memcpy(mgmt_eth_data->data + 1, skb->data,
++ QCA_HDR_MGMT_DATA2_LEN);
++ }
++
++ complete(&mgmt_eth_data->rw_done);
++}
++
++static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val,
++ int priority, unsigned int len)
++{
++ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++ unsigned int real_len;
++ struct sk_buff *skb;
++ u32 *data2;
++ u16 hdr;
++
++ skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
++ if (!skb)
++ return NULL;
++
++ /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte
++ * Actually for some reason the steps are:
++ * 0: nothing
++ * 1-4: first 4 byte
++ * 5-6: first 12 byte
++ * 7-15: all 16 byte
++ */
++ if (len == 16)
++ real_len = 15;
++ else
++ real_len = len;
++
++ skb_reset_mac_header(skb);
++ skb_set_network_header(skb, skb->len);
++
++ mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN);
++
++ hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION);
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority);
++ hdr |= QCA_HDR_XMIT_FROM_CPU;
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
++
++ mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
++ QCA_HDR_MGMT_CHECK_CODE_VAL);
++
++ if (cmd == MDIO_WRITE)
++ mgmt_ethhdr->mdio_data = *val;
++
++ mgmt_ethhdr->hdr = htons(hdr);
++
++ data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
++ if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN)
++ memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
++
++ return skb;
++}
++
++static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
++{
++ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++
++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
++ mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
++}
++
++static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
++ struct sk_buff *skb;
++ bool ack;
++ int ret;
++
++ skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL,
++ QCA8K_ETHERNET_MDIO_PRIORITY, len);
++ if (!skb)
++ return -ENOMEM;
++
++ mutex_lock(&mgmt_eth_data->mutex);
++
++ /* Check mgmt_master if is operational */
++ if (!priv->mgmt_master) {
++ kfree_skb(skb);
++ mutex_unlock(&mgmt_eth_data->mutex);
++ return -EINVAL;
++ }
++
++ skb->dev = priv->mgmt_master;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the mdio pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
++
++ *val = mgmt_eth_data->data[0];
++ if (len > QCA_HDR_MGMT_DATA1_LEN)
++ memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN);
++
++ ack = mgmt_eth_data->ack;
++
++ mutex_unlock(&mgmt_eth_data->mutex);
++
++ if (ret <= 0)
++ return -ETIMEDOUT;
++
++ if (!ack)
++ return -EINVAL;
++
++ return 0;
++}
++
++static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
++ struct sk_buff *skb;
++ bool ack;
++ int ret;
++
++ skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val,
++ QCA8K_ETHERNET_MDIO_PRIORITY, len);
++ if (!skb)
++ return -ENOMEM;
++
++ mutex_lock(&mgmt_eth_data->mutex);
++
++ /* Check mgmt_master if is operational */
++ if (!priv->mgmt_master) {
++ kfree_skb(skb);
++ mutex_unlock(&mgmt_eth_data->mutex);
++ return -EINVAL;
++ }
++
++ skb->dev = priv->mgmt_master;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the mdio pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
++
++ ack = mgmt_eth_data->ack;
++
++ mutex_unlock(&mgmt_eth_data->mutex);
++
++ if (ret <= 0)
++ return -ETIMEDOUT;
++
++ if (!ack)
++ return -EINVAL;
++
++ return 0;
++}
++
++static int
++qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++{
++ u32 val = 0;
++ int ret;
++
++ ret = qca8k_read_eth(priv, reg, &val, sizeof(val));
++ if (ret)
++ return ret;
++
++ val &= ~mask;
++ val |= write_val;
++
++ return qca8k_write_eth(priv, reg, &val, sizeof(val));
++}
++
++static int
++qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ int i, count = len / sizeof(u32), ret;
++
++ if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len))
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ ret = regmap_read(priv->regmap, reg + (i * 4), val + i);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int
++qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ int i, count = len / sizeof(u32), ret;
++ u32 tmp;
++
++ if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len))
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ tmp = val[i];
++
++ ret = regmap_write(priv->regmap, reg + (i * 4), tmp);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int
++qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ int ret;
++
++ if (!qca8k_read_eth(priv, reg, val, sizeof(*val)))
++ return 0;
++
++ qca8k_split_addr(reg, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val);
++
++exit:
++ mutex_unlock(&bus->mdio_lock);
++ return ret;
++}
++
++static int
++qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ int ret;
++
++ if (!qca8k_write_eth(priv, reg, &val, sizeof(val)))
++ return 0;
++
++ qca8k_split_addr(reg, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret < 0)
++ goto exit;
++
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++
++exit:
++ mutex_unlock(&bus->mdio_lock);
++ return ret;
++}
++
++static int
++qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ u32 val;
++ int ret;
++
++ if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
++ return 0;
++
++ qca8k_split_addr(reg, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
++ if (ret < 0)
++ goto exit;
++
++ val &= ~mask;
++ val |= write_val;
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++
++exit:
++ mutex_unlock(&bus->mdio_lock);
++
++ return ret;
++}
++
++static const struct regmap_range qca8k_readable_ranges[] = {
++ regmap_reg_range(0x0000, 0x00e4), /* Global control */
++ regmap_reg_range(0x0100, 0x0168), /* EEE control */
++ regmap_reg_range(0x0200, 0x0270), /* Parser control */
++ regmap_reg_range(0x0400, 0x0454), /* ACL */
++ regmap_reg_range(0x0600, 0x0718), /* Lookup */
++ regmap_reg_range(0x0800, 0x0b70), /* QM */
++ regmap_reg_range(0x0c00, 0x0c80), /* PKT */
++ regmap_reg_range(0x0e00, 0x0e98), /* L3 */
++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */
++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */
++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */
++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */
++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */
++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */
++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */
++
++};
++
++static const struct regmap_access_table qca8k_readable_table = {
++ .yes_ranges = qca8k_readable_ranges,
++ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
++};
++
++static struct regmap_config qca8k_regmap_config = {
++ .reg_bits = 16,
++ .val_bits = 32,
++ .reg_stride = 4,
++ .max_register = 0x16ac, /* end MIB - Port6 range */
++ .reg_read = qca8k_regmap_read,
++ .reg_write = qca8k_regmap_write,
++ .reg_update_bits = qca8k_regmap_update_bits,
++ .rd_table = &qca8k_readable_table,
++ .disable_locking = true, /* Locking is handled by qca8k read/write */
++ .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
++};
++
++static int
++qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
++{
++ u32 val;
++
++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
++}
++
++static int
++qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
++{
++ u32 reg[3];
++ int ret;
++
++ /* load the ARL table into an array */
++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++ if (ret)
++ return ret;
++
++ /* vid - 83:72 */
++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
++ /* aging - 67:64 */
++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
++ /* portmask - 54:48 */
++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
++ /* mac - 47:0 */
++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
++
++ return 0;
++}
++
++static void
++qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
++ u8 aging)
++{
++ u32 reg[3] = { 0 };
++
++ /* vid - 83:72 */
++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
++ /* aging - 67:64 */
++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
++ /* portmask - 54:48 */
++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
++ /* mac - 47:0 */
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
++
++ /* load the array into the ARL table */
++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++}
++
++static int
++qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
++{
++ u32 reg;
++ int ret;
++
++ /* Set the command and FDB index */
++ reg = QCA8K_ATU_FUNC_BUSY;
++ reg |= cmd;
++ if (port >= 0) {
++ reg |= QCA8K_ATU_FUNC_PORT_EN;
++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
++ }
++
++ /* Write the function register triggering the table access */
++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
++ if (ret)
++ return ret;
++
++ /* wait for completion */
++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY);
++ if (ret)
++ return ret;
++
++ /* Check for table full violation when adding an entry */
++ if (cmd == QCA8K_FDB_LOAD) {
++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, &reg);
++ if (ret < 0)
++ return ret;
++ if (reg & QCA8K_ATU_FUNC_FULL)
++ return -1;
++ }
++
++ return 0;
++}
++
++static int
++qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
++{
++ int ret;
++
++ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
++ if (ret < 0)
++ return ret;
++
++ return qca8k_fdb_read(priv, fdb);
++}
++
++static int
++qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask,
++ u16 vid, u8 aging)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_write(priv, vid, port_mask, mac, aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int
++qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_write(priv, vid, port_mask, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static void
++qca8k_fdb_flush(struct qca8k_priv *priv)
++{
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
++ mutex_unlock(&priv->reg_mutex);
++}
++
++static int
++qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_fdb_read(priv, &fdb);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule exist. Delete first */
++ if (!fdb.aging) {
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++ }
++
++ /* Add port to fdb portmask */
++ fdb.port_mask |= port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
++qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule doesn't exist. Why delete? */
++ if (!fdb.aging) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++
++ /* Only port in the rule is this port. Don't re insert */
++ if (fdb.port_mask == port_mask)
++ goto exit;
++
++ /* Remove port from port mask */
++ fdb.port_mask &= ~port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
++qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
++{
++ u32 reg;
++ int ret;
++
++ /* Set the command and VLAN index */
++ reg = QCA8K_VTU_FUNC1_BUSY;
++ reg |= cmd;
++ reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
++
++ /* Write the function register triggering the table access */
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
++ if (ret)
++ return ret;
++
++ /* wait for completion */
++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
++ if (ret)
++ return ret;
++
++ /* Check for table full violation when adding an entry */
++ if (cmd == QCA8K_VLAN_LOAD) {
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, &reg);
++ if (ret < 0)
++ return ret;
++ if (reg & QCA8K_VTU_FUNC1_FULL)
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static int
++qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
++{
++ u32 reg;
++ int ret;
++
++ /*
++ We do the right thing with VLAN 0 and treat it as untagged while
++ preserving the tag on egress.
++ */
++ if (vid == 0)
++ return 0;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
++ if (ret < 0)
++ goto out;
++
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
++ if (ret < 0)
++ goto out;
++ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++ if (untagged)
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
++ else
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
++
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ if (ret)
++ goto out;
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
++
++out:
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int
++qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
++{
++ u32 reg, mask;
++ int ret, i;
++ bool del;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
++ if (ret < 0)
++ goto out;
++
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
++ if (ret < 0)
++ goto out;
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
++
++ /* Check if we're the last member to be removed */
++ del = true;
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
++
++ if ((reg & mask) != mask) {
++ del = false;
++ break;
++ }
++ }
++
++ if (del) {
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
++ } else {
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ if (ret)
++ goto out;
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
++ }
++
++out:
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int
++qca8k_mib_init(struct qca8k_priv *priv)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) |
++ QCA8K_MIB_BUSY);
++ if (ret)
++ goto exit;
++
++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
++ if (ret)
++ goto exit;
++
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
++ if (ret)
++ goto exit;
++
++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static void
++qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
++{
++ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
++
++ /* Port 0 and 6 have no internal PHY */
++ if (port > 0 && port < 6)
++ mask |= QCA8K_PORT_STATUS_LINK_AUTO;
++
++ if (enable)
++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
++ else
++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
++}
++
++static int
++qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
++ struct sk_buff *read_skb, u32 *val)
++{
++ struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL);
++ bool ack;
++ int ret;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the copy pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ ack = mgmt_eth_data->ack;
++
++ if (ret <= 0)
++ return -ETIMEDOUT;
++
++ if (!ack)
++ return -EINVAL;
++
++ *val = mgmt_eth_data->data[0];
++
++ return 0;
++}
++
++static int
++qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
++ int regnum, u16 data)
++{
++ struct sk_buff *write_skb, *clear_skb, *read_skb;
++ struct qca8k_mgmt_eth_data *mgmt_eth_data;
++ u32 write_val, clear_val = 0, val;
++ struct net_device *mgmt_master;
++ int ret, ret1;
++ bool ack;
++
++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
++ return -EINVAL;
++
++ mgmt_eth_data = &priv->mgmt_eth_data;
++
++ write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
++ QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
++ QCA8K_MDIO_MASTER_REG_ADDR(regnum);
++
++ if (read) {
++ write_val |= QCA8K_MDIO_MASTER_READ;
++ } else {
++ write_val |= QCA8K_MDIO_MASTER_WRITE;
++ write_val |= QCA8K_MDIO_MASTER_DATA(data);
++ }
++
++ /* Prealloc all the needed skb before the lock */
++ write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val,
++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val));
++ if (!write_skb)
++ return -ENOMEM;
++
++ clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val,
++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
++ if (!clear_skb) {
++ ret = -ENOMEM;
++ goto err_clear_skb;
++ }
++
++ read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val,
++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
++ if (!read_skb) {
++ ret = -ENOMEM;
++ goto err_read_skb;
++ }
++
++ /* Actually start the request:
++ * 1. Send mdio master packet
++ * 2. Busy Wait for mdio master command
++ * 3. Get the data if we are reading
++ * 4. Reset the mdio master (even with error)
++ */
++ mutex_lock(&mgmt_eth_data->mutex);
++
++ /* Check if mgmt_master is operational */
++ mgmt_master = priv->mgmt_master;
++ if (!mgmt_master) {
++ mutex_unlock(&mgmt_eth_data->mutex);
++ ret = -EINVAL;
++ goto err_mgmt_master;
++ }
++
++ read_skb->dev = mgmt_master;
++ clear_skb->dev = mgmt_master;
++ write_skb->dev = mgmt_master;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the write pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(write_skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ ack = mgmt_eth_data->ack;
++
++ if (ret <= 0) {
++ ret = -ETIMEDOUT;
++ kfree_skb(read_skb);
++ goto exit;
++ }
++
++ if (!ack) {
++ ret = -EINVAL;
++ kfree_skb(read_skb);
++ goto exit;
++ }
++
++ ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1,
++ !(val & QCA8K_MDIO_MASTER_BUSY), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
++ mgmt_eth_data, read_skb, &val);
++
++ if (ret < 0 && ret1 < 0) {
++ ret = ret1;
++ goto exit;
++ }
++
++ if (read) {
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the read pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(read_skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ ack = mgmt_eth_data->ack;
++
++ if (ret <= 0) {
++ ret = -ETIMEDOUT;
++ goto exit;
++ }
++
++ if (!ack) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK;
++ } else {
++ kfree_skb(read_skb);
++ }
++exit:
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the clear pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(clear_skb);
++
++ wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ mutex_unlock(&mgmt_eth_data->mutex);
++
++ return ret;
++
++ /* Error handling before lock */
++err_mgmt_master:
++ kfree_skb(read_skb);
++err_read_skb:
++ kfree_skb(clear_skb);
++err_clear_skb:
++ kfree_skb(write_skb);
++
++ return ret;
++}
++
++static u32
++qca8k_port_to_phy(int port)
++{
++ /* From Andrew Lunn:
++ * Port 0 has no internal phy.
++ * Port 1 has an internal PHY at MDIO address 0.
++ * Port 2 has an internal PHY at MDIO address 1.
++ * ...
++ * Port 5 has an internal PHY at MDIO address 4.
++ * Port 6 has no internal PHY.
++ */
++
++ return port - 1;
++}
++
++static int
++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)
++{
++ u16 r1, r2, page;
++ u32 val;
++ int ret, ret1;
++
++ qca8k_split_addr(reg, &r1, &r2, &page);
++
++ ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
++ bus, 0x10 | r2, r1, &val);
++
++ /* Check if qca8k_read has failed for a different reason
++ * before returnting -ETIMEDOUT
++ */
++ if (ret < 0 && ret1 < 0)
++ return ret1;
++
++ return ret;
++}
++
++static int
++qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data)
++{
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ u32 val;
++ int ret;
++
++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
++ return -EINVAL;
++
++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
++ QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
++ QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
++ QCA8K_MDIO_MASTER_DATA(data);
++
++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret)
++ goto exit;
++
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++
++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_BUSY);
++
++exit:
++ /* even if the busy_wait timeouts try to clear the MASTER_EN */
++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
++
++ mutex_unlock(&bus->mdio_lock);
++
++ return ret;
++}
++
++static int
++qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum)
++{
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ u32 val;
++ int ret;
++
++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
++ return -EINVAL;
++
++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
++ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
++ QCA8K_MDIO_MASTER_REG_ADDR(regnum);
++
++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret)
++ goto exit;
++
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++
++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_BUSY);
++ if (ret)
++ goto exit;
++
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
++
++exit:
++ /* even if the busy_wait timeouts try to clear the MASTER_EN */
++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
++
++ mutex_unlock(&bus->mdio_lock);
++
++ if (ret >= 0)
++ ret = val & QCA8K_MDIO_MASTER_DATA_MASK;
++
++ return ret;
++}
++
++static int
++qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data)
++{
++ struct qca8k_priv *priv = slave_bus->priv;
++ int ret;
++
++ /* Use mdio Ethernet when available, fallback to legacy one on error */
++ ret = qca8k_phy_eth_command(priv, false, phy, regnum, data);
++ if (!ret)
++ return 0;
++
++ return qca8k_mdio_write(priv, phy, regnum, data);
++}
++
++static int
++qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum)
++{
++ struct qca8k_priv *priv = slave_bus->priv;
++ int ret;
++
++ /* Use mdio Ethernet when available, fallback to legacy one on error */
++ ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0);
++ if (ret >= 0)
++ return ret;
++
++ ret = qca8k_mdio_read(priv, phy, regnum);
++
++ if (ret < 0)
++ return 0xffff;
++
++ return ret;
++}
++
++static int
++qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data)
++{
++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
++
++ return qca8k_internal_mdio_write(slave_bus, port, regnum, data);
++}
++
++static int
++qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum)
++{
++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
++
++ return qca8k_internal_mdio_read(slave_bus, port, regnum);
++}
++
++static int
++qca8k_mdio_register(struct qca8k_priv *priv)
++{
++ struct dsa_switch *ds = priv->ds;
++ struct device_node *mdio;
++ struct mii_bus *bus;
++
++ bus = devm_mdiobus_alloc(ds->dev);
++ if (!bus)
++ return -ENOMEM;
++
++ bus->priv = (void *)priv;
++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d",
++ ds->dst->index, ds->index);
++ bus->parent = ds->dev;
++ bus->phy_mask = ~ds->phys_mii_mask;
++ ds->slave_mii_bus = bus;
++
++ /* Check if the devicetree declare the port:phy mapping */
++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
++ if (of_device_is_available(mdio)) {
++ bus->name = "qca8k slave mii";
++ bus->read = qca8k_internal_mdio_read;
++ bus->write = qca8k_internal_mdio_write;
++ return devm_of_mdiobus_register(priv->dev, bus, mdio);
++ }
++
++ /* If a mapping can't be found the legacy mapping is used,
++ * using the qca8k_port_to_phy function
++ */
++ bus->name = "qca8k-legacy slave mii";
++ bus->read = qca8k_legacy_mdio_read;
++ bus->write = qca8k_legacy_mdio_write;
++ return devm_mdiobus_register(priv->dev, bus);
++}
++
++static int
++qca8k_setup_mdio_bus(struct qca8k_priv *priv)
++{
++ u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg;
++ struct device_node *ports, *port;
++ phy_interface_t mode;
++ int err;
++
++ ports = of_get_child_by_name(priv->dev->of_node, "ports");
++ if (!ports)
++ ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports");
++
++ if (!ports)
++ return -EINVAL;
++
++ for_each_available_child_of_node(ports, port) {
++ err = of_property_read_u32(port, "reg", &reg);
++ if (err) {
++ of_node_put(port);
++ of_node_put(ports);
++ return err;
++ }
++
++ if (!dsa_is_user_port(priv->ds, reg))
++ continue;
++
++ of_get_phy_mode(port, &mode);
++
++ if (of_property_read_bool(port, "phy-handle") &&
++ mode != PHY_INTERFACE_MODE_INTERNAL)
++ external_mdio_mask |= BIT(reg);
++ else
++ internal_mdio_mask |= BIT(reg);
++ }
++
++ of_node_put(ports);
++ if (!external_mdio_mask && !internal_mdio_mask) {
++ dev_err(priv->dev, "no PHYs are defined.\n");
++ return -EINVAL;
++ }
++
++ /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
++ * the MDIO_MASTER register also _disconnects_ the external MDC
++ * passthrough to the internal PHYs. It's not possible to use both
++ * configurations at the same time!
++ *
++ * Because this came up during the review process:
++ * If the external mdio-bus driver is capable magically disabling
++ * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's
++ * accessors for the time being, it would be possible to pull this
++ * off.
++ */
++ if (!!external_mdio_mask && !!internal_mdio_mask) {
++ dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
++ return -EINVAL;
++ }
++
++ if (external_mdio_mask) {
++ /* Make sure to disable the internal mdio bus in cases
++ * a dt-overlay and driver reload changed the configuration
++ */
++
++ return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_EN);
++ }
++
++ return qca8k_mdio_register(priv);
++}
++
++static int
++qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
++{
++ u32 mask = 0;
++ int ret = 0;
++
++ /* SoC specific settings for ipq8064.
++ * If more device require this consider adding
++ * a dedicated binding.
++ */
++ if (of_machine_is_compatible("qcom,ipq8064"))
++ mask |= QCA8K_MAC_PWR_RGMII0_1_8V;
++
++ /* SoC specific settings for ipq8065 */
++ if (of_machine_is_compatible("qcom,ipq8065"))
++ mask |= QCA8K_MAC_PWR_RGMII1_1_8V;
++
++ if (mask) {
++ ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL,
++ QCA8K_MAC_PWR_RGMII0_1_8V |
++ QCA8K_MAC_PWR_RGMII1_1_8V,
++ mask);
++ }
++
++ return ret;
++}
++
++static int qca8k_find_cpu_port(struct dsa_switch *ds)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Find the connected cpu port. Valid port are 0 or 6 */
++ if (dsa_is_cpu_port(ds, 0))
++ return 0;
++
++ dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
++
++ if (dsa_is_cpu_port(ds, 6))
++ return 6;
++
++ return -EINVAL;
++}
++
++static int
++qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
++{
++ struct device_node *node = priv->dev->of_node;
++ const struct qca8k_match_data *data;
++ u32 val = 0;
++ int ret;
++
++ /* QCA8327 require to set to the correct mode.
++ * His bigger brother QCA8328 have the 172 pin layout.
++ * Should be applied by default but we set this just to make sure.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ data = of_device_get_match_data(priv->dev);
++
++ /* Set the correct package of 148 pin for QCA8327 */
++ if (data->reduced_package)
++ val |= QCA8327_PWS_PACKAGE148_EN;
++
++ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
++ val);
++ if (ret)
++ return ret;
++ }
++
++ if (of_property_read_bool(node, "qca,ignore-power-on-sel"))
++ val |= QCA8K_PWS_POWER_ON_SEL;
++
++ if (of_property_read_bool(node, "qca,led-open-drain")) {
++ if (!(val & QCA8K_PWS_POWER_ON_SEL)) {
++ dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set.");
++ return -EINVAL;
++ }
++
++ val |= QCA8K_PWS_LED_OPEN_EN_CSR;
++ }
++
++ return qca8k_rmw(priv, QCA8K_REG_PWS,
++ QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL,
++ val);
++}
++
++static int
++qca8k_parse_port_config(struct qca8k_priv *priv)
++{
++ int port, cpu_port_index = -1, ret;
++ struct device_node *port_dn;
++ phy_interface_t mode;
++ struct dsa_port *dp;
++ u32 delay;
++
++ /* We have 2 CPU port. Check them */
++ for (port = 0; port < QCA8K_NUM_PORTS; port++) {
++ /* Skip every other port */
++ if (port != 0 && port != 6)
++ continue;
++
++ dp = dsa_to_port(priv->ds, port);
++ port_dn = dp->dn;
++ cpu_port_index++;
++
++ if (!of_device_is_available(port_dn))
++ continue;
++
++ ret = of_get_phy_mode(port_dn, &mode);
++ if (ret)
++ continue;
++
++ switch (mode) {
++ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ case PHY_INTERFACE_MODE_SGMII:
++ delay = 0;
++
++ if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay))
++ /* Switch regs accept value in ns, convert ps to ns */
++ delay = delay / 1000;
++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_TXID)
++ delay = 1;
++
++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) {
++ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
++ delay = 3;
++ }
++
++ priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay;
++
++ delay = 0;
++
++ if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay))
++ /* Switch regs accept value in ns, convert ps to ns */
++ delay = delay / 1000;
++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_RXID)
++ delay = 2;
++
++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) {
++ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
++ delay = 3;
++ }
++
++ priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay;
++
++ /* Skip sgmii parsing for rgmii* mode */
++ if (mode == PHY_INTERFACE_MODE_RGMII ||
++ mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_TXID ||
++ mode == PHY_INTERFACE_MODE_RGMII_RXID)
++ break;
++
++ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
++ priv->ports_config.sgmii_tx_clk_falling_edge = true;
++
++ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
++ priv->ports_config.sgmii_rx_clk_falling_edge = true;
++
++ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) {
++ priv->ports_config.sgmii_enable_pll = true;
++
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling");
++ priv->ports_config.sgmii_enable_pll = false;
++ }
++
++ if (priv->switch_revision < 2)
++ dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more.");
++ }
++
++ break;
++ default:
++ continue;
++ }
++ }
++
++ return 0;
++}
++
++static int
++qca8k_setup(struct dsa_switch *ds)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ int cpu_port, ret, i;
++ u32 mask;
++
++ cpu_port = qca8k_find_cpu_port(ds);
++ if (cpu_port < 0) {
++ dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
++ return cpu_port;
++ }
++
++ /* Parse CPU port config to be later used in phy_link mac_config */
++ ret = qca8k_parse_port_config(priv);
++ if (ret)
++ return ret;
++
++ ret = qca8k_setup_mdio_bus(priv);
++ if (ret)
++ return ret;
++
++ ret = qca8k_setup_of_pws_reg(priv);
++ if (ret)
++ return ret;
++
++ ret = qca8k_setup_mac_pwr_sel(priv);
++ if (ret)
++ return ret;
++
++ /* Make sure MAC06 is disabled */
++ ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL,
++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
++ if (ret) {
++ dev_err(priv->dev, "failed disabling MAC06 exchange");
++ return ret;
++ }
++
++ /* Enable CPU Port */
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
++ if (ret) {
++ dev_err(priv->dev, "failed enabling CPU port");
++ return ret;
++ }
++
++ /* Enable MIB counters */
++ ret = qca8k_mib_init(priv);
++ if (ret)
++ dev_warn(priv->dev, "mib init failed");
++
++ /* Initial setup of all ports */
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ /* Disable forwarding by default on all ports */
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_MEMBER, 0);
++ if (ret)
++ return ret;
++
++ /* Enable QCA header mode on all cpu ports */
++ if (dsa_is_cpu_port(ds, i)) {
++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
++ if (ret) {
++ dev_err(priv->dev, "failed enabling QCA header mode");
++ return ret;
++ }
++ }
++
++ /* Disable MAC by default on all user ports */
++ if (dsa_is_user_port(ds, i))
++ qca8k_port_set_status(priv, i, 0);
++ }
++
++ /* Forward all unknown frames to CPU port for Linux processing
++ * Notice that in multi-cpu config only one port should be set
++ * for igmp, unknown, multicast and broadcast packet
++ */
++ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));
++ if (ret)
++ return ret;
++
++ /* Setup connection between CPU port & user ports
++ * Configure specific switch configuration for ports
++ */
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ /* CPU port gets connected to all user ports of the switch */
++ if (dsa_is_cpu_port(ds, i)) {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
++ if (ret)
++ return ret;
++ }
++
++ /* Individual user ports get connected to CPU port only */
++ if (dsa_is_user_port(ds, i)) {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_MEMBER,
++ BIT(cpu_port));
++ if (ret)
++ return ret;
++
++ /* Enable ARP Auto-learning by default */
++ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_LEARN);
++ if (ret)
++ return ret;
++
++ /* For port based vlans to work we need to set the
++ * default egress vid
++ */
++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
++ QCA8K_EGREES_VLAN_PORT_MASK(i),
++ QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));
++ if (ret)
++ return ret;
++
++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
++ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
++ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
++ if (ret)
++ return ret;
++ }
++
++ /* The port 5 of the qca8337 have some problem in flood condition. The
++ * original legacy driver had some specific buffer and priority settings
++ * for the different port suggested by the QCA switch team. Add this
++ * missing settings to improve switch stability under load condition.
++ * This problem is limited to qca8337 and other qca8k switch are not affected.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8337) {
++ switch (i) {
++ /* The 2 CPU port and port 5 requires some different
++ * priority than any other ports.
++ */
++ case 0:
++ case 5:
++ case 6:
++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
++ break;
++ default:
++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
++ }
++ qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
++
++ mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_WRED_EN;
++ qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
++ QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_WRED_EN,
++ mask);
++ }
++ }
++
++ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
++ qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
++ QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK |
++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,
++ mask);
++ }
++
++ /* Setup our port MTUs to match power on defaults */
++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
++ if (ret)
++ dev_warn(priv->dev, "failed setting MTU settings");
++
++ /* Flush the FDB table */
++ qca8k_fdb_flush(priv);
++
++ /* We don't have interrupts for link changes, so we need to poll */
++ ds->pcs_poll = true;
++
++ /* Set min a max ageing value supported */
++ ds->ageing_time_min = 7000;
++ ds->ageing_time_max = 458745000;
++
++ /* Set max number of LAGs supported */
++ ds->num_lag_ids = QCA8K_NUM_LAGS;
++
++ return 0;
++}
++
++static void
++qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index,
++ u32 reg)
++{
++ u32 delay, val = 0;
++ int ret;
++
++ /* Delay can be declared in 3 different way.
++ * Mode to rgmii and internal-delay standard binding defined
++ * rgmii-id or rgmii-tx/rx phy mode set.
++ * The parse logic set a delay different than 0 only when one
++ * of the 3 different way is used. In all other case delay is
++ * not enabled. With ID or TX/RXID delay is enabled and set
++ * to the default and recommended value.
++ */
++ if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) {
++ delay = priv->ports_config.rgmii_tx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
++ }
++
++ if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) {
++ delay = priv->ports_config.rgmii_rx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
++ }
++
++ /* Set RGMII delay based on the selected values */
++ ret = qca8k_rmw(priv, reg,
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN,
++ val);
++ if (ret)
++ dev_err(priv->dev, "Failed to set internal delay for CPU port%d",
++ cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6);
++}
++
++static void
++qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
++ const struct phylink_link_state *state)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int cpu_port_index, ret;
++ u32 reg, val;
++
++ switch (port) {
++ case 0: /* 1st CPU port */
++ if (state->interface != PHY_INTERFACE_MODE_RGMII &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
++ state->interface != PHY_INTERFACE_MODE_SGMII)
++ return;
++
++ reg = QCA8K_REG_PORT0_PAD_CTRL;
++ cpu_port_index = QCA8K_CPU_PORT0;
++ break;
++ case 1:
++ case 2:
++ case 3:
++ case 4:
++ case 5:
++ /* Internal PHY, nothing to do */
++ return;
++ case 6: /* 2nd CPU port / external PHY */
++ if (state->interface != PHY_INTERFACE_MODE_RGMII &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
++ state->interface != PHY_INTERFACE_MODE_SGMII &&
++ state->interface != PHY_INTERFACE_MODE_1000BASEX)
++ return;
++
++ reg = QCA8K_REG_PORT6_PAD_CTRL;
++ cpu_port_index = QCA8K_CPU_PORT6;
++ break;
++ default:
++ dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
++ return;
++ }
++
++ if (port != 6 && phylink_autoneg_inband(mode)) {
++ dev_err(ds->dev, "%s: in-band negotiation unsupported\n",
++ __func__);
++ return;
++ }
++
++ switch (state->interface) {
++ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
++
++ /* Configure rgmii delay */
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
++
++ /* QCA8337 requires to set rgmii rx delay for all ports.
++ * This is enabled through PORT5_PAD_CTRL for all ports,
++ * rather than individual port registers.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8337)
++ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
++ break;
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_1000BASEX:
++ /* Enable SGMII on the port */
++ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
++
++ /* Enable/disable SerDes auto-negotiation as necessary */
++ ret = qca8k_read(priv, QCA8K_REG_PWS, &val);
++ if (ret)
++ return;
++ if (phylink_autoneg_inband(mode))
++ val &= ~QCA8K_PWS_SERDES_AEN_DIS;
++ else
++ val |= QCA8K_PWS_SERDES_AEN_DIS;
++ qca8k_write(priv, QCA8K_REG_PWS, val);
++
++ /* Configure the SGMII parameters */
++ ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
++ if (ret)
++ return;
++
++ val |= QCA8K_SGMII_EN_SD;
++
++ if (priv->ports_config.sgmii_enable_pll)
++ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
++ QCA8K_SGMII_EN_TX;
++
++ if (dsa_is_cpu_port(ds, port)) {
++ /* CPU port, we're talking to the CPU MAC, be a PHY */
++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
++ val |= QCA8K_SGMII_MODE_CTRL_PHY;
++ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
++ val |= QCA8K_SGMII_MODE_CTRL_MAC;
++ } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
++ val |= QCA8K_SGMII_MODE_CTRL_BASEX;
++ }
++
++ qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
++
++ /* From original code is reported port instability as SGMII also
++ * require delay set. Apply advised values here or take them from DT.
++ */
++ if (state->interface == PHY_INTERFACE_MODE_SGMII)
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
++
++ /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
++ * falling edge is set writing in the PORT0 PAD reg
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8327 ||
++ priv->switch_id == QCA8K_ID_QCA8337)
++ reg = QCA8K_REG_PORT0_PAD_CTRL;
++
++ val = 0;
++
++ /* SGMII Clock phase configuration */
++ if (priv->ports_config.sgmii_rx_clk_falling_edge)
++ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE;
++
++ if (priv->ports_config.sgmii_tx_clk_falling_edge)
++ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
++
++ if (val)
++ ret = qca8k_rmw(priv, reg,
++ QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
++ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
++ val);
++
++ break;
++ default:
++ dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
++ phy_modes(state->interface), port);
++ return;
++ }
++}
++
++static void
++qca8k_phylink_validate(struct dsa_switch *ds, int port,
++ unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
++
++ switch (port) {
++ case 0: /* 1st CPU port */
++ if (state->interface != PHY_INTERFACE_MODE_NA &&
++ state->interface != PHY_INTERFACE_MODE_RGMII &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
++ state->interface != PHY_INTERFACE_MODE_SGMII)
++ goto unsupported;
++ break;
++ case 1:
++ case 2:
++ case 3:
++ case 4:
++ case 5:
++ /* Internal PHY */
++ if (state->interface != PHY_INTERFACE_MODE_NA &&
++ state->interface != PHY_INTERFACE_MODE_GMII &&
++ state->interface != PHY_INTERFACE_MODE_INTERNAL)
++ goto unsupported;
++ break;
++ case 6: /* 2nd CPU port / external PHY */
++ if (state->interface != PHY_INTERFACE_MODE_NA &&
++ state->interface != PHY_INTERFACE_MODE_RGMII &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
++ state->interface != PHY_INTERFACE_MODE_SGMII &&
++ state->interface != PHY_INTERFACE_MODE_1000BASEX)
++ goto unsupported;
++ break;
++ default:
++unsupported:
++ linkmode_zero(supported);
++ return;
++ }
++
++ phylink_set_port_modes(mask);
++ phylink_set(mask, Autoneg);
++
++ phylink_set(mask, 1000baseT_Full);
++ phylink_set(mask, 10baseT_Half);
++ phylink_set(mask, 10baseT_Full);
++ phylink_set(mask, 100baseT_Half);
++ phylink_set(mask, 100baseT_Full);
++
++ if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
++ phylink_set(mask, 1000baseX_Full);
++
++ phylink_set(mask, Pause);
++ phylink_set(mask, Asym_Pause);
++
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
++}
++
++static int
++qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port,
++ struct phylink_link_state *state)
++{
++ struct qca8k_priv *priv = ds->priv;
++ u32 reg;
++ int ret;
++
++ ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), &reg);
++ if (ret < 0)
++ return ret;
++
++ state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
++ state->an_complete = state->link;
++ state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO);
++ state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL :
++ DUPLEX_HALF;
++
++ switch (reg & QCA8K_PORT_STATUS_SPEED) {
++ case QCA8K_PORT_STATUS_SPEED_10:
++ state->speed = SPEED_10;
++ break;
++ case QCA8K_PORT_STATUS_SPEED_100:
++ state->speed = SPEED_100;
++ break;
++ case QCA8K_PORT_STATUS_SPEED_1000:
++ state->speed = SPEED_1000;
++ break;
++ default:
++ state->speed = SPEED_UNKNOWN;
++ break;
++ }
++
++ state->pause = MLO_PAUSE_NONE;
++ if (reg & QCA8K_PORT_STATUS_RXFLOW)
++ state->pause |= MLO_PAUSE_RX;
++ if (reg & QCA8K_PORT_STATUS_TXFLOW)
++ state->pause |= MLO_PAUSE_TX;
++
++ return 1;
++}
++
++static void
++qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
++ phy_interface_t interface)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ qca8k_port_set_status(priv, port, 0);
++}
++
++static void
++qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
++ phy_interface_t interface, struct phy_device *phydev,
++ int speed, int duplex, bool tx_pause, bool rx_pause)
++{
++ struct qca8k_priv *priv = ds->priv;
++ u32 reg;
++
++ if (phylink_autoneg_inband(mode)) {
++ reg = QCA8K_PORT_STATUS_LINK_AUTO;
++ } else {
++ switch (speed) {
++ case SPEED_10:
++ reg = QCA8K_PORT_STATUS_SPEED_10;
++ break;
++ case SPEED_100:
++ reg = QCA8K_PORT_STATUS_SPEED_100;
++ break;
++ case SPEED_1000:
++ reg = QCA8K_PORT_STATUS_SPEED_1000;
++ break;
++ default:
++ reg = QCA8K_PORT_STATUS_LINK_AUTO;
++ break;
++ }
++
++ if (duplex == DUPLEX_FULL)
++ reg |= QCA8K_PORT_STATUS_DUPLEX;
++
++ if (rx_pause || dsa_is_cpu_port(ds, port))
++ reg |= QCA8K_PORT_STATUS_RXFLOW;
++
++ if (tx_pause || dsa_is_cpu_port(ds, port))
++ reg |= QCA8K_PORT_STATUS_TXFLOW;
++ }
++
++ reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
++
++ qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
++}
++
++static void
++qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
++{
++ const struct qca8k_match_data *match_data;
++ struct qca8k_priv *priv = ds->priv;
++ int i;
++
++ if (stringset != ETH_SS_STATS)
++ return;
++
++ match_data = of_device_get_match_data(priv->dev);
++
++ for (i = 0; i < match_data->mib_count; i++)
++ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
++ ETH_GSTRING_LEN);
++}
++
++static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb)
++{
++ const struct qca8k_match_data *match_data;
++ struct qca8k_mib_eth_data *mib_eth_data;
++ struct qca8k_priv *priv = ds->priv;
++ const struct qca8k_mib_desc *mib;
++ struct mib_ethhdr *mib_ethhdr;
++ int i, mib_len, offset = 0;
++ u64 *data;
++ u8 port;
++
++ mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb);
++ mib_eth_data = &priv->mib_eth_data;
++
++ /* The switch autocast every port. Ignore other packet and
++ * parse only the requested one.
++ */
++ port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr));
++ if (port != mib_eth_data->req_port)
++ goto exit;
++
++ match_data = device_get_match_data(priv->dev);
++ data = mib_eth_data->data;
++
++ for (i = 0; i < match_data->mib_count; i++) {
++ mib = &ar8327_mib[i];
++
++ /* First 3 mib are present in the skb head */
++ if (i < 3) {
++ data[i] = mib_ethhdr->data[i];
++ continue;
++ }
++
++ mib_len = sizeof(uint32_t);
++
++ /* Some mib are 64 bit wide */
++ if (mib->size == 2)
++ mib_len = sizeof(uint64_t);
++
++ /* Copy the mib value from packet to the */
++ memcpy(data + i, skb->data + offset, mib_len);
++
++ /* Set the offset for the next mib */
++ offset += mib_len;
++ }
++
++exit:
++ /* Complete on receiving all the mib packet */
++ if (refcount_dec_and_test(&mib_eth_data->port_parsed))
++ complete(&mib_eth_data->rw_done);
++}
++
++static int
++qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data)
++{
++ struct dsa_port *dp = dsa_to_port(ds, port);
++ struct qca8k_mib_eth_data *mib_eth_data;
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ mib_eth_data = &priv->mib_eth_data;
++
++ mutex_lock(&mib_eth_data->mutex);
++
++ reinit_completion(&mib_eth_data->rw_done);
++
++ mib_eth_data->req_port = dp->index;
++ mib_eth_data->data = data;
++ refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS);
++
++ mutex_lock(&priv->reg_mutex);
++
++ /* Send mib autocast request */
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) |
++ QCA8K_MIB_BUSY);
++
++ mutex_unlock(&priv->reg_mutex);
++
++ if (ret)
++ goto exit;
++
++ ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT);
++
++exit:
++ mutex_unlock(&mib_eth_data->mutex);
++
++ return ret;
++}
++
++static void
++qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
++ uint64_t *data)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ const struct qca8k_match_data *match_data;
++ const struct qca8k_mib_desc *mib;
++ u32 reg, i, val;
++ u32 hi = 0;
++ int ret;
++
++ if (priv->mgmt_master &&
++ qca8k_get_ethtool_stats_eth(ds, port, data) > 0)
++ return;
++
++ match_data = of_device_get_match_data(priv->dev);
++
++ for (i = 0; i < match_data->mib_count; i++) {
++ mib = &ar8327_mib[i];
++ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
++
++ ret = qca8k_read(priv, reg, &val);
++ if (ret < 0)
++ continue;
++
++ if (mib->size == 2) {
++ ret = qca8k_read(priv, reg + 4, &hi);
++ if (ret < 0)
++ continue;
++ }
++
++ data[i] = val;
++ if (mib->size == 2)
++ data[i] |= (u64)hi << 32;
++ }
++}
++
++static int
++qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
++{
++ const struct qca8k_match_data *match_data;
++ struct qca8k_priv *priv = ds->priv;
++
++ if (sset != ETH_SS_STATS)
++ return 0;
++
++ match_data = of_device_get_match_data(priv->dev);
++
++ return match_data->mib_count;
++}
++
++static int
++qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
++ u32 reg;
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, &reg);
++ if (ret < 0)
++ goto exit;
++
++ if (eee->eee_enabled)
++ reg |= lpi_en;
++ else
++ reg &= ~lpi_en;
++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
++qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
++{
++ /* Nothing to do on the port's MAC */
++ return 0;
++}
++
++static void
++qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u32 stp_state;
++
++ switch (state) {
++ case BR_STATE_DISABLED:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED;
++ break;
++ case BR_STATE_BLOCKING:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING;
++ break;
++ case BR_STATE_LISTENING:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING;
++ break;
++ case BR_STATE_LEARNING:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
++ break;
++ case BR_STATE_FORWARDING:
++ default:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
++ break;
++ }
++
++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
++}
++
++static int
++qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ int port_mask, cpu_port;
++ int i, ret;
++
++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
++ port_mask = BIT(cpu_port);
++
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
++ if (dsa_to_port(ds, i)->bridge_dev != br)
++ continue;
++ /* Add this port to the portvlan mask of the other ports
++ * in the bridge
++ */
++ ret = regmap_set_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
++ if (ret)
++ return ret;
++ if (i != port)
++ port_mask |= BIT(i);
++ }
++
++ /* Add all other ports to this ports portvlan mask */
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_MEMBER, port_mask);
++
++ return ret;
++}
++
++static void
++qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ int cpu_port, i;
++
++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
++
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
++ if (dsa_to_port(ds, i)->bridge_dev != br)
++ continue;
++ /* Remove this port to the portvlan mask of the other ports
++ * in the bridge
++ */
++ regmap_clear_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
++ }
++
++ /* Set the cpu port to be the only one in the portvlan mask of
++ * this port
++ */
++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
++}
++
++static void
++qca8k_port_fast_age(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
++ mutex_unlock(&priv->reg_mutex);
++}
++
++static int
++qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
++{
++ struct qca8k_priv *priv = ds->priv;
++ unsigned int secs = msecs / 1000;
++ u32 val;
++
++ /* AGE_TIME reg is set in 7s step */
++ val = secs / 7;
++
++ /* Handle case with 0 as val to NOT disable
++ * learning
++ */
++ if (!val)
++ val = 1;
++
++ return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
++ QCA8K_ATU_AGE_TIME(val));
++}
++
++static int
++qca8k_port_enable(struct dsa_switch *ds, int port,
++ struct phy_device *phy)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++
++ qca8k_port_set_status(priv, port, 1);
++ priv->port_enabled_map |= BIT(port);
++
++ if (dsa_is_user_port(ds, port))
++ phy_support_asym_pause(phy);
++
++ return 0;
++}
++
++static void
++qca8k_port_disable(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++
++ qca8k_port_set_status(priv, port, 0);
++ priv->port_enabled_map &= ~BIT(port);
++}
++
++static int
++qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ /* We have only have a general MTU setting.
++ * DSA always set the CPU port's MTU to the largest MTU of the slave
++ * ports.
++ * Setting MTU just for the CPU port is sufficient to correctly set a
++ * value for every port.
++ */
++ if (!dsa_is_cpu_port(ds, port))
++ return 0;
++
++ /* To change the MAX_FRAME_SIZE the cpu ports must be off or
++ * the switch panics.
++ * Turn off both cpu ports before applying the new value to prevent
++ * this.
++ */
++ if (priv->port_enabled_map & BIT(0))
++ qca8k_port_set_status(priv, 0, 0);
++
++ if (priv->port_enabled_map & BIT(6))
++ qca8k_port_set_status(priv, 6, 0);
++
++ /* Include L2 header / FCS length */
++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN);
++
++ if (priv->port_enabled_map & BIT(0))
++ qca8k_port_set_status(priv, 0, 1);
++
++ if (priv->port_enabled_map & BIT(6))
++ qca8k_port_set_status(priv, 6, 1);
++
++ return ret;
++}
++
++static int
++qca8k_port_max_mtu(struct dsa_switch *ds, int port)
++{
++ return QCA8K_MAX_MTU;
++}
++
++static int
++qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
++ u16 port_mask, u16 vid)
++{
++ /* Set the vid to the port vlan id if no vid is set */
++ if (!vid)
++ vid = QCA8K_PORT_VID_DEF;
++
++ return qca8k_fdb_add(priv, addr, port_mask, vid,
++ QCA8K_ATU_STATUS_STATIC);
++}
++
++static int
++qca8k_port_fdb_add(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u16 port_mask = BIT(port);
++
++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
++}
++
++static int
++qca8k_port_fdb_del(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u16 port_mask = BIT(port);
++
++ if (!vid)
++ vid = QCA8K_PORT_VID_DEF;
++
++ return qca8k_fdb_del(priv, addr, port_mask, vid);
++}
++
++static int
++qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
++ dsa_fdb_dump_cb_t *cb, void *data)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ struct qca8k_fdb _fdb = { 0 };
++ int cnt = QCA8K_NUM_FDB_RECORDS;
++ bool is_static;
++ int ret = 0;
++
++ mutex_lock(&priv->reg_mutex);
++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
++ if (!_fdb.aging)
++ break;
++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
++ ret = cb(_fdb.mac, _fdb.vid, is_static, data);
++ if (ret)
++ break;
++ }
++ mutex_unlock(&priv->reg_mutex);
++
++ return 0;
++}
++
++static int
++qca8k_port_mdb_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
++}
++
++static int
++qca8k_port_mdb_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
++}
++
++static int
++qca8k_port_mirror_add(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror,
++ bool ingress)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int monitor_port, ret;
++ u32 reg, val;
++
++ /* Check for existent entry */
++ if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
++ return -EEXIST;
++
++ ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
++ if (ret)
++ return ret;
++
++ /* QCA83xx can have only one port set to mirror mode.
++ * Check that the correct port is requested and return error otherwise.
++ * When no mirror port is set, the values is set to 0xF
++ */
++ monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
++ return -EEXIST;
++
++ /* Set the monitor port */
++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
++ mirror->to_local_port);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (ret)
++ return ret;
++
++ if (ingress) {
++ reg = QCA8K_PORT_LOOKUP_CTRL(port);
++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
++ } else {
++ reg = QCA8K_REG_PORT_HOL_CTRL1(port);
++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
++ }
++
++ ret = regmap_update_bits(priv->regmap, reg, val, val);
++ if (ret)
++ return ret;
++
++ /* Track mirror port for tx and rx to decide when the
++ * mirror port has to be disabled.
++ */
++ if (ingress)
++ priv->mirror_rx |= BIT(port);
++ else
++ priv->mirror_tx |= BIT(port);
++
++ return 0;
++}
++
++static void
++qca8k_port_mirror_del(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror)
++{
++ struct qca8k_priv *priv = ds->priv;
++ u32 reg, val;
++ int ret;
++
++ if (mirror->ingress) {
++ reg = QCA8K_PORT_LOOKUP_CTRL(port);
++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
++ } else {
++ reg = QCA8K_REG_PORT_HOL_CTRL1(port);
++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
++ }
++
++ ret = regmap_clear_bits(priv->regmap, reg, val);
++ if (ret)
++ goto err;
++
++ if (mirror->ingress)
++ priv->mirror_rx &= ~BIT(port);
++ else
++ priv->mirror_tx &= ~BIT(port);
++
++ /* No port set to send packet to mirror port. Disable mirror port */
++ if (!priv->mirror_rx && !priv->mirror_tx) {
++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (ret)
++ goto err;
++ }
++err:
++ dev_err(priv->dev, "Failed to del mirror port from %d", port);
++}
++
++static int
++qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
++ struct netlink_ext_ack *extack)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ if (vlan_filtering) {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
++ } else {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
++ }
++
++ return ret;
++}
++
++static int
++qca8k_port_vlan_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_vlan *vlan,
++ struct netlink_ext_ack *extack)
++{
++ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
++ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
++ if (ret) {
++ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
++ return ret;
++ }
++
++ if (pvid) {
++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
++ QCA8K_EGREES_VLAN_PORT_MASK(port),
++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
++ if (ret)
++ return ret;
++
++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
++ QCA8K_PORT_VLAN_CVID(vlan->vid) |
++ QCA8K_PORT_VLAN_SVID(vlan->vid));
++ }
++
++ return ret;
++}
++
++static int
++qca8k_port_vlan_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_vlan *vlan)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ ret = qca8k_vlan_del(priv, port, vlan->vid);
++ if (ret)
++ dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
++
++ return ret;
++}
++
++static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Communicate to the phy internal driver the switch revision.
++ * Based on the switch revision different values needs to be
++ * set to the dbg and mmd reg on the phy.
++ * The first 2 bit are used to communicate the switch revision
++ * to the phy driver.
++ */
++ if (port > 0 && port < 6)
++ return priv->switch_revision;
++
++ return 0;
++}
++
++static enum dsa_tag_protocol
++qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
++ enum dsa_tag_protocol mp)
++{
++ return DSA_TAG_PROTO_QCA;
++}
++
++static bool
++qca8k_lag_can_offload(struct dsa_switch *ds,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct dsa_port *dp;
++ int id, members = 0;
++
++ id = dsa_lag_id(ds->dst, lag);
++ if (id < 0 || id >= ds->num_lag_ids)
++ return false;
++
++ dsa_lag_foreach_port(dp, ds->dst, lag)
++ /* Includes the port joining the LAG */
++ members++;
++
++ if (members > QCA8K_NUM_PORTS_FOR_LAG)
++ return false;
++
++ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
++ return false;
++
++ if (info->hash_type != NETDEV_LAG_HASH_L2 &&
++ info->hash_type != NETDEV_LAG_HASH_L23)
++ return false;
++
++ return true;
++}
++
++static int
++qca8k_lag_setup_hash(struct dsa_switch *ds,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct qca8k_priv *priv = ds->priv;
++ bool unique_lag = true;
++ u32 hash = 0;
++ int i, id;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ switch (info->hash_type) {
++ case NETDEV_LAG_HASH_L23:
++ hash |= QCA8K_TRUNK_HASH_SIP_EN;
++ hash |= QCA8K_TRUNK_HASH_DIP_EN;
++ fallthrough;
++ case NETDEV_LAG_HASH_L2:
++ hash |= QCA8K_TRUNK_HASH_SA_EN;
++ hash |= QCA8K_TRUNK_HASH_DA_EN;
++ break;
++ default: /* We should NEVER reach this */
++ return -EOPNOTSUPP;
++ }
++
++ /* Check if we are the unique configured LAG */
++ dsa_lags_foreach_id(i, ds->dst)
++ if (i != id && dsa_lag_dev(ds->dst, i)) {
++ unique_lag = false;
++ break;
++ }
++
++ /* Hash Mode is global. Make sure the same Hash Mode
++ * is set to all the 4 possible lag.
++ * If we are the unique LAG we can set whatever hash
++ * mode we want.
++ * To change hash mode it's needed to remove all LAG
++ * and change the mode with the latest.
++ */
++ if (unique_lag) {
++ priv->lag_hash_mode = hash;
++ } else if (priv->lag_hash_mode != hash) {
++ netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n");
++ return -EOPNOTSUPP;
++ }
++
++ return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
++ QCA8K_TRUNK_HASH_MASK, hash);
++}
++
++static int
++qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
++ struct net_device *lag, bool delete)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret, id, i;
++ u32 val;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ /* Read current port member */
++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
++ if (ret)
++ return ret;
++
++ /* Shift val to the correct trunk */
++ val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
++ val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
++ if (delete)
++ val &= ~BIT(port);
++ else
++ val |= BIT(port);
++
++ /* Update port member. With empty portmap disable trunk */
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
++ QCA8K_REG_GOL_TRUNK_MEMBER(id) |
++ QCA8K_REG_GOL_TRUNK_EN(id),
++ !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
++ val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
++
++ /* Search empty member if adding or port on deleting */
++ for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
++ if (ret)
++ return ret;
++
++ val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
++
++ if (delete) {
++ /* If port flagged to be disabled assume this member is
++ * empty
++ */
++ if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
++ continue;
++
++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
++ if (val != port)
++ continue;
++ } else {
++ /* If port flagged to be enabled assume this member is
++ * already set
++ */
++ if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
++ continue;
++ }
++
++ /* We have found the member to add/remove */
++ break;
++ }
++
++ /* Set port in the correct port mask or disable port if in delete mode */
++ return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
++ !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
++ port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
++}
++
++static int
++qca8k_port_lag_join(struct dsa_switch *ds, int port,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ int ret;
++
++ if (!qca8k_lag_can_offload(ds, lag, info))
++ return -EOPNOTSUPP;
++
++ ret = qca8k_lag_setup_hash(ds, lag, info);
++ if (ret)
++ return ret;
++
++ return qca8k_lag_refresh_portmap(ds, port, lag, false);
++}
++
++static int
++qca8k_port_lag_leave(struct dsa_switch *ds, int port,
++ struct net_device *lag)
++{
++ return qca8k_lag_refresh_portmap(ds, port, lag, true);
++}
++
++static void
++qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
++ bool operational)
++{
++ struct dsa_port *dp = master->dsa_ptr;
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Ethernet MIB/MDIO is only supported for CPU port 0 */
++ if (dp->index != 0)
++ return;
++
++ mutex_lock(&priv->mgmt_eth_data.mutex);
++ mutex_lock(&priv->mib_eth_data.mutex);
++
++ priv->mgmt_master = operational ? (struct net_device *)master : NULL;
++
++ mutex_unlock(&priv->mib_eth_data.mutex);
++ mutex_unlock(&priv->mgmt_eth_data.mutex);
++}
++
++static int qca8k_connect_tag_protocol(struct dsa_switch *ds,
++ enum dsa_tag_protocol proto)
++{
++ struct qca_tagger_data *tagger_data;
++
++ switch (proto) {
++ case DSA_TAG_PROTO_QCA:
++ tagger_data = ds->tagger_data;
++
++ tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler;
++ tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler;
++
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ return 0;
++}
++
++static const struct dsa_switch_ops qca8k_switch_ops = {
++ .get_tag_protocol = qca8k_get_tag_protocol,
++ .setup = qca8k_setup,
++ .get_strings = qca8k_get_strings,
++ .get_ethtool_stats = qca8k_get_ethtool_stats,
++ .get_sset_count = qca8k_get_sset_count,
++ .set_ageing_time = qca8k_set_ageing_time,
++ .get_mac_eee = qca8k_get_mac_eee,
++ .set_mac_eee = qca8k_set_mac_eee,
++ .port_enable = qca8k_port_enable,
++ .port_disable = qca8k_port_disable,
++ .port_change_mtu = qca8k_port_change_mtu,
++ .port_max_mtu = qca8k_port_max_mtu,
++ .port_stp_state_set = qca8k_port_stp_state_set,
++ .port_bridge_join = qca8k_port_bridge_join,
++ .port_bridge_leave = qca8k_port_bridge_leave,
++ .port_fast_age = qca8k_port_fast_age,
++ .port_fdb_add = qca8k_port_fdb_add,
++ .port_fdb_del = qca8k_port_fdb_del,
++ .port_fdb_dump = qca8k_port_fdb_dump,
++ .port_mdb_add = qca8k_port_mdb_add,
++ .port_mdb_del = qca8k_port_mdb_del,
++ .port_mirror_add = qca8k_port_mirror_add,
++ .port_mirror_del = qca8k_port_mirror_del,
++ .port_vlan_filtering = qca8k_port_vlan_filtering,
++ .port_vlan_add = qca8k_port_vlan_add,
++ .port_vlan_del = qca8k_port_vlan_del,
++ .phylink_validate = qca8k_phylink_validate,
++ .phylink_mac_link_state = qca8k_phylink_mac_link_state,
++ .phylink_mac_config = qca8k_phylink_mac_config,
++ .phylink_mac_link_down = qca8k_phylink_mac_link_down,
++ .phylink_mac_link_up = qca8k_phylink_mac_link_up,
++ .get_phy_flags = qca8k_get_phy_flags,
++ .port_lag_join = qca8k_port_lag_join,
++ .port_lag_leave = qca8k_port_lag_leave,
++ .master_state_change = qca8k_master_change,
++ .connect_tag_protocol = qca8k_connect_tag_protocol,
++};
++
++static int qca8k_read_switch_id(struct qca8k_priv *priv)
++{
++ const struct qca8k_match_data *data;
++ u32 val;
++ u8 id;
++ int ret;
++
++ /* get the switches ID from the compatible */
++ data = of_device_get_match_data(priv->dev);
++ if (!data)
++ return -ENODEV;
++
++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
++ if (ret < 0)
++ return -ENODEV;
++
++ id = QCA8K_MASK_CTRL_DEVICE_ID(val);
++ if (id != data->id) {
++ dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
++ return -ENODEV;
++ }
++
++ priv->switch_id = id;
++
++ /* Save revision to communicate to the internal PHY driver */
++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
++
++ return 0;
++}
++
++static int
++qca8k_sw_probe(struct mdio_device *mdiodev)
++{
++ struct qca8k_priv *priv;
++ int ret;
++
++ /* allocate the private data struct so that we can probe the switches
++ * ID register
++ */
++ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->bus = mdiodev->bus;
++ priv->dev = &mdiodev->dev;
++
++ priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset",
++ GPIOD_ASIS);
++ if (IS_ERR(priv->reset_gpio))
++ return PTR_ERR(priv->reset_gpio);
++
++ if (priv->reset_gpio) {
++ gpiod_set_value_cansleep(priv->reset_gpio, 1);
++ /* The active low duration must be greater than 10 ms
++ * and checkpatch.pl wants 20 ms.
++ */
++ msleep(20);
++ gpiod_set_value_cansleep(priv->reset_gpio, 0);
++ }
++
++ /* Start by setting up the register mapping */
++ priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv,
++ &qca8k_regmap_config);
++ if (IS_ERR(priv->regmap)) {
++ dev_err(priv->dev, "regmap initialization failed");
++ return PTR_ERR(priv->regmap);
++ }
++
++ priv->mdio_cache.page = 0xffff;
++ priv->mdio_cache.lo = 0xffff;
++ priv->mdio_cache.hi = 0xffff;
++
++ /* Check the detected switch id */
++ ret = qca8k_read_switch_id(priv);
++ if (ret)
++ return ret;
++
++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
++ if (!priv->ds)
++ return -ENOMEM;
++
++ mutex_init(&priv->mgmt_eth_data.mutex);
++ init_completion(&priv->mgmt_eth_data.rw_done);
++
++ mutex_init(&priv->mib_eth_data.mutex);
++ init_completion(&priv->mib_eth_data.rw_done);
++
++ priv->ds->dev = &mdiodev->dev;
++ priv->ds->num_ports = QCA8K_NUM_PORTS;
++ priv->ds->priv = priv;
++ priv->ds->ops = &qca8k_switch_ops;
++ mutex_init(&priv->reg_mutex);
++ dev_set_drvdata(&mdiodev->dev, priv);
++
++ return dsa_register_switch(priv->ds);
++}
++
++static void
++qca8k_sw_remove(struct mdio_device *mdiodev)
++{
++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
++ int i;
++
++ if (!priv)
++ return;
++
++ for (i = 0; i < QCA8K_NUM_PORTS; i++)
++ qca8k_port_set_status(priv, i, 0);
++
++ dsa_unregister_switch(priv->ds);
++
++ dev_set_drvdata(&mdiodev->dev, NULL);
++}
++
++static void qca8k_sw_shutdown(struct mdio_device *mdiodev)
++{
++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
++
++ if (!priv)
++ return;
++
++ dsa_switch_shutdown(priv->ds);
++
++ dev_set_drvdata(&mdiodev->dev, NULL);
++}
++
++#ifdef CONFIG_PM_SLEEP
++static void
++qca8k_set_pm(struct qca8k_priv *priv, int enable)
++{
++ int port;
++
++ for (port = 0; port < QCA8K_NUM_PORTS; port++) {
++ /* Do not enable on resume if the port was
++ * disabled before.
++ */
++ if (!(priv->port_enabled_map & BIT(port)))
++ continue;
++
++ qca8k_port_set_status(priv, port, enable);
++ }
++}
++
++static int qca8k_suspend(struct device *dev)
++{
++ struct qca8k_priv *priv = dev_get_drvdata(dev);
++
++ qca8k_set_pm(priv, 0);
++
++ return dsa_switch_suspend(priv->ds);
++}
++
++static int qca8k_resume(struct device *dev)
++{
++ struct qca8k_priv *priv = dev_get_drvdata(dev);
++
++ qca8k_set_pm(priv, 1);
++
++ return dsa_switch_resume(priv->ds);
++}
++#endif /* CONFIG_PM_SLEEP */
++
++static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
++ qca8k_suspend, qca8k_resume);
++
++static const struct qca8k_match_data qca8327 = {
++ .id = QCA8K_ID_QCA8327,
++ .reduced_package = true,
++ .mib_count = QCA8K_QCA832X_MIB_COUNT,
++};
++
++static const struct qca8k_match_data qca8328 = {
++ .id = QCA8K_ID_QCA8327,
++ .mib_count = QCA8K_QCA832X_MIB_COUNT,
++};
++
++static const struct qca8k_match_data qca833x = {
++ .id = QCA8K_ID_QCA8337,
++ .mib_count = QCA8K_QCA833X_MIB_COUNT,
++};
++
++static const struct of_device_id qca8k_of_match[] = {
++ { .compatible = "qca,qca8327", .data = &qca8327 },
++ { .compatible = "qca,qca8328", .data = &qca8328 },
++ { .compatible = "qca,qca8334", .data = &qca833x },
++ { .compatible = "qca,qca8337", .data = &qca833x },
++ { /* sentinel */ },
++};
++
++static struct mdio_driver qca8kmdio_driver = {
++ .probe = qca8k_sw_probe,
++ .remove = qca8k_sw_remove,
++ .shutdown = qca8k_sw_shutdown,
++ .mdiodrv.driver = {
++ .name = "qca8k",
++ .of_match_table = qca8k_of_match,
++ .pm = &qca8k_pm_ops,
++ },
++};
++
++mdio_module_driver(qca8kmdio_driver);
++
++MODULE_AUTHOR("Mathieu Olivari, John Crispin <john@phrozen.org>");
++MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qca8k");
+--- /dev/null
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -0,0 +1,411 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
++ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ */
++
++#ifndef __QCA8K_H
++#define __QCA8K_H
++
++#include <linux/delay.h>
++#include <linux/regmap.h>
++#include <linux/gpio.h>
++#include <linux/dsa/tag_qca.h>
++
++#define QCA8K_ETHERNET_MDIO_PRIORITY 7
++#define QCA8K_ETHERNET_PHY_PRIORITY 6
++#define QCA8K_ETHERNET_TIMEOUT 100
++
++#define QCA8K_NUM_PORTS 7
++#define QCA8K_NUM_CPU_PORTS 2
++#define QCA8K_MAX_MTU 9000
++#define QCA8K_NUM_LAGS 4
++#define QCA8K_NUM_PORTS_FOR_LAG 4
++
++#define PHY_ID_QCA8327 0x004dd034
++#define QCA8K_ID_QCA8327 0x12
++#define PHY_ID_QCA8337 0x004dd036
++#define QCA8K_ID_QCA8337 0x13
++
++#define QCA8K_QCA832X_MIB_COUNT 39
++#define QCA8K_QCA833X_MIB_COUNT 41
++
++#define QCA8K_BUSY_WAIT_TIMEOUT 2000
++
++#define QCA8K_NUM_FDB_RECORDS 2048
++
++#define QCA8K_PORT_VID_DEF 1
++
++/* Global control registers */
++#define QCA8K_REG_MASK_CTRL 0x000
++#define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0)
++#define QCA8K_MASK_CTRL_REV_ID(x) FIELD_GET(QCA8K_MASK_CTRL_REV_ID_MASK, x)
++#define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8)
++#define QCA8K_MASK_CTRL_DEVICE_ID(x) FIELD_GET(QCA8K_MASK_CTRL_DEVICE_ID_MASK, x)
++#define QCA8K_REG_PORT0_PAD_CTRL 0x004
++#define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31)
++#define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19)
++#define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18)
++#define QCA8K_REG_PORT5_PAD_CTRL 0x008
++#define QCA8K_REG_PORT6_PAD_CTRL 0x00c
++#define QCA8K_PORT_PAD_RGMII_EN BIT(26)
++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22)
++#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, x)
++#define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20)
++#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, x)
++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
++#define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
++#define QCA8K_PORT_PAD_SGMII_EN BIT(7)
++#define QCA8K_REG_PWS 0x010
++#define QCA8K_PWS_POWER_ON_SEL BIT(31)
++/* This reg is only valid for QCA832x and toggle the package
++ * type from 176 pin (by default) to 148 pin used on QCA8327
++ */
++#define QCA8327_PWS_PACKAGE148_EN BIT(30)
++#define QCA8K_PWS_LED_OPEN_EN_CSR BIT(24)
++#define QCA8K_PWS_SERDES_AEN_DIS BIT(7)
++#define QCA8K_REG_MODULE_EN 0x030
++#define QCA8K_MODULE_EN_MIB BIT(0)
++#define QCA8K_REG_MIB 0x034
++#define QCA8K_MIB_FUNC GENMASK(26, 24)
++#define QCA8K_MIB_CPU_KEEP BIT(20)
++#define QCA8K_MIB_BUSY BIT(17)
++#define QCA8K_MDIO_MASTER_CTRL 0x3c
++#define QCA8K_MDIO_MASTER_BUSY BIT(31)
++#define QCA8K_MDIO_MASTER_EN BIT(30)
++#define QCA8K_MDIO_MASTER_READ BIT(27)
++#define QCA8K_MDIO_MASTER_WRITE 0
++#define QCA8K_MDIO_MASTER_SUP_PRE BIT(26)
++#define QCA8K_MDIO_MASTER_PHY_ADDR_MASK GENMASK(25, 21)
++#define QCA8K_MDIO_MASTER_PHY_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_PHY_ADDR_MASK, x)
++#define QCA8K_MDIO_MASTER_REG_ADDR_MASK GENMASK(20, 16)
++#define QCA8K_MDIO_MASTER_REG_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_REG_ADDR_MASK, x)
++#define QCA8K_MDIO_MASTER_DATA_MASK GENMASK(15, 0)
++#define QCA8K_MDIO_MASTER_DATA(x) FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)
++#define QCA8K_MDIO_MASTER_MAX_PORTS 5
++#define QCA8K_MDIO_MASTER_MAX_REG 32
++#define QCA8K_GOL_MAC_ADDR0 0x60
++#define QCA8K_GOL_MAC_ADDR1 0x64
++#define QCA8K_MAX_FRAME_SIZE 0x78
++#define QCA8K_REG_PORT_STATUS(_i) (0x07c + (_i) * 4)
++#define QCA8K_PORT_STATUS_SPEED GENMASK(1, 0)
++#define QCA8K_PORT_STATUS_SPEED_10 0
++#define QCA8K_PORT_STATUS_SPEED_100 0x1
++#define QCA8K_PORT_STATUS_SPEED_1000 0x2
++#define QCA8K_PORT_STATUS_TXMAC BIT(2)
++#define QCA8K_PORT_STATUS_RXMAC BIT(3)
++#define QCA8K_PORT_STATUS_TXFLOW BIT(4)
++#define QCA8K_PORT_STATUS_RXFLOW BIT(5)
++#define QCA8K_PORT_STATUS_DUPLEX BIT(6)
++#define QCA8K_PORT_STATUS_LINK_UP BIT(8)
++#define QCA8K_PORT_STATUS_LINK_AUTO BIT(9)
++#define QCA8K_PORT_STATUS_LINK_PAUSE BIT(10)
++#define QCA8K_PORT_STATUS_FLOW_AUTO BIT(12)
++#define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4))
++#define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2)
++#define QCA8K_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0)
++#define QCA8K_PORT_HDR_CTRL_ALL 2
++#define QCA8K_PORT_HDR_CTRL_MGMT 1
++#define QCA8K_PORT_HDR_CTRL_NONE 0
++#define QCA8K_REG_SGMII_CTRL 0x0e0
++#define QCA8K_SGMII_EN_PLL BIT(1)
++#define QCA8K_SGMII_EN_RX BIT(2)
++#define QCA8K_SGMII_EN_TX BIT(3)
++#define QCA8K_SGMII_EN_SD BIT(4)
++#define QCA8K_SGMII_CLK125M_DELAY BIT(7)
++#define QCA8K_SGMII_MODE_CTRL_MASK GENMASK(23, 22)
++#define QCA8K_SGMII_MODE_CTRL(x) FIELD_PREP(QCA8K_SGMII_MODE_CTRL_MASK, x)
++#define QCA8K_SGMII_MODE_CTRL_BASEX QCA8K_SGMII_MODE_CTRL(0x0)
++#define QCA8K_SGMII_MODE_CTRL_PHY QCA8K_SGMII_MODE_CTRL(0x1)
++#define QCA8K_SGMII_MODE_CTRL_MAC QCA8K_SGMII_MODE_CTRL(0x2)
++
++/* MAC_PWR_SEL registers */
++#define QCA8K_REG_MAC_PWR_SEL 0x0e4
++#define QCA8K_MAC_PWR_RGMII1_1_8V BIT(18)
++#define QCA8K_MAC_PWR_RGMII0_1_8V BIT(19)
++
++/* EEE control registers */
++#define QCA8K_REG_EEE_CTRL 0x100
++#define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2)
++
++/* TRUNK_HASH_EN registers */
++#define QCA8K_TRUNK_HASH_EN_CTRL 0x270
++#define QCA8K_TRUNK_HASH_SIP_EN BIT(3)
++#define QCA8K_TRUNK_HASH_DIP_EN BIT(2)
++#define QCA8K_TRUNK_HASH_SA_EN BIT(1)
++#define QCA8K_TRUNK_HASH_DA_EN BIT(0)
++#define QCA8K_TRUNK_HASH_MASK GENMASK(3, 0)
++
++/* ACL registers */
++#define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8))
++#define QCA8K_PORT_VLAN_CVID_MASK GENMASK(27, 16)
++#define QCA8K_PORT_VLAN_CVID(x) FIELD_PREP(QCA8K_PORT_VLAN_CVID_MASK, x)
++#define QCA8K_PORT_VLAN_SVID_MASK GENMASK(11, 0)
++#define QCA8K_PORT_VLAN_SVID(x) FIELD_PREP(QCA8K_PORT_VLAN_SVID_MASK, x)
++#define QCA8K_REG_PORT_VLAN_CTRL1(_i) (0x424 + (_i * 8))
++#define QCA8K_REG_IPV4_PRI_BASE_ADDR 0x470
++#define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474
++
++/* Lookup registers */
++#define QCA8K_REG_ATU_DATA0 0x600
++#define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24)
++#define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16)
++#define QCA8K_ATU_ADDR4_MASK GENMASK(15, 8)
++#define QCA8K_ATU_ADDR5_MASK GENMASK(7, 0)
++#define QCA8K_REG_ATU_DATA1 0x604
++#define QCA8K_ATU_PORT_MASK GENMASK(22, 16)
++#define QCA8K_ATU_ADDR0_MASK GENMASK(15, 8)
++#define QCA8K_ATU_ADDR1_MASK GENMASK(7, 0)
++#define QCA8K_REG_ATU_DATA2 0x608
++#define QCA8K_ATU_VID_MASK GENMASK(19, 8)
++#define QCA8K_ATU_STATUS_MASK GENMASK(3, 0)
++#define QCA8K_ATU_STATUS_STATIC 0xf
++#define QCA8K_REG_ATU_FUNC 0x60c
++#define QCA8K_ATU_FUNC_BUSY BIT(31)
++#define QCA8K_ATU_FUNC_PORT_EN BIT(14)
++#define QCA8K_ATU_FUNC_MULTI_EN BIT(13)
++#define QCA8K_ATU_FUNC_FULL BIT(12)
++#define QCA8K_ATU_FUNC_PORT_MASK GENMASK(11, 8)
++#define QCA8K_REG_VTU_FUNC0 0x610
++#define QCA8K_VTU_FUNC0_VALID BIT(20)
++#define QCA8K_VTU_FUNC0_IVL_EN BIT(19)
++/* QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(17, 4)
++ * It does contain VLAN_MODE for each port [5:4] for port0,
++ * [7:6] for port1 ... [17:16] for port6. Use virtual port
++ * define to handle this.
++ */
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i) (4 + (_i) * 2)
++#define QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(1, 0)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(_i) (GENMASK(1, 0) << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x0)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNMOD(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNMOD << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x1)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNTAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_TAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x2)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_TAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_VTU_FUNC0_EG_MODE_NOT FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x3)
++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(_i) (QCA8K_VTU_FUNC0_EG_MODE_NOT << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
++#define QCA8K_REG_VTU_FUNC1 0x614
++#define QCA8K_VTU_FUNC1_BUSY BIT(31)
++#define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16)
++#define QCA8K_VTU_FUNC1_FULL BIT(4)
++#define QCA8K_REG_ATU_CTRL 0x618
++#define QCA8K_ATU_AGE_TIME_MASK GENMASK(15, 0)
++#define QCA8K_ATU_AGE_TIME(x) FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x))
++#define QCA8K_REG_GLOBAL_FW_CTRL0 0x620
++#define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10)
++#define QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM GENMASK(7, 4)
++#define QCA8K_REG_GLOBAL_FW_CTRL1 0x624
++#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24)
++#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16)
++#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK GENMASK(14, 8)
++#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK GENMASK(6, 0)
++#define QCA8K_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc)
++#define QCA8K_PORT_LOOKUP_MEMBER GENMASK(6, 0)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_MASK GENMASK(9, 8)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE QCA8K_PORT_LOOKUP_VLAN_MODE(0x0)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK QCA8K_PORT_LOOKUP_VLAN_MODE(0x1)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK QCA8K_PORT_LOOKUP_VLAN_MODE(0x2)
++#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE QCA8K_PORT_LOOKUP_VLAN_MODE(0x3)
++#define QCA8K_PORT_LOOKUP_STATE_MASK GENMASK(18, 16)
++#define QCA8K_PORT_LOOKUP_STATE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_STATE_MASK, x)
++#define QCA8K_PORT_LOOKUP_STATE_DISABLED QCA8K_PORT_LOOKUP_STATE(0x0)
++#define QCA8K_PORT_LOOKUP_STATE_BLOCKING QCA8K_PORT_LOOKUP_STATE(0x1)
++#define QCA8K_PORT_LOOKUP_STATE_LISTENING QCA8K_PORT_LOOKUP_STATE(0x2)
++#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
++#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
++#define QCA8K_PORT_LOOKUP_LEARN BIT(20)
++#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
++
++#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700
++/* 4 max trunk first
++ * first 6 bit for member bitmap
++ * 7th bit is to enable trunk port
++ */
++#define QCA8K_REG_GOL_TRUNK_SHIFT(_i) ((_i) * 8)
++#define QCA8K_REG_GOL_TRUNK_EN_MASK BIT(7)
++#define QCA8K_REG_GOL_TRUNK_EN(_i) (QCA8K_REG_GOL_TRUNK_EN_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
++#define QCA8K_REG_GOL_TRUNK_MEMBER_MASK GENMASK(6, 0)
++#define QCA8K_REG_GOL_TRUNK_MEMBER(_i) (QCA8K_REG_GOL_TRUNK_MEMBER_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
++/* 0x704 for TRUNK 0-1 --- 0x708 for TRUNK 2-3 */
++#define QCA8K_REG_GOL_TRUNK_CTRL(_i) (0x704 + (((_i) / 2) * 4))
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK GENMASK(3, 0)
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK BIT(3)
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK GENMASK(2, 0)
++#define QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i) (((_i) / 2) * 16)
++#define QCA8K_REG_GOL_MEM_ID_SHIFT(_i) ((_i) * 4)
++/* Complex shift: FIRST shift for port THEN shift for trunk */
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j) (QCA8K_REG_GOL_MEM_ID_SHIFT(_j) + QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i))
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
++
++#define QCA8K_REG_GLOBAL_FC_THRESH 0x800
++#define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16)
++#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x)
++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK GENMASK(8, 0)
++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, x)
++
++#define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK GENMASK(3, 0)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK GENMASK(7, 4)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK GENMASK(11, 8)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK GENMASK(15, 12)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK GENMASK(19, 16)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK GENMASK(23, 20)
++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK GENMASK(29, 24)
++#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK, x)
++
++#define QCA8K_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8)
++#define QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK GENMASK(3, 0)
++#define QCA8K_PORT_HOL_CTRL1_ING(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK, x)
++#define QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN BIT(6)
++#define QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN BIT(7)
++#define QCA8K_PORT_HOL_CTRL1_WRED_EN BIT(8)
++#define QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16)
++
++/* Pkt edit registers */
++#define QCA8K_EGREES_VLAN_PORT_SHIFT(_i) (16 * ((_i) % 2))
++#define QCA8K_EGREES_VLAN_PORT_MASK(_i) (GENMASK(11, 0) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
++#define QCA8K_EGREES_VLAN_PORT(_i, x) ((x) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
++#define QCA8K_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2)))
++
++/* L3 registers */
++#define QCA8K_HROUTER_CONTROL 0xe00
++#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_M GENMASK(17, 16)
++#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_S 16
++#define QCA8K_HROUTER_CONTROL_ARP_AGE_MODE 1
++#define QCA8K_HROUTER_PBASED_CONTROL1 0xe08
++#define QCA8K_HROUTER_PBASED_CONTROL2 0xe0c
++#define QCA8K_HNAT_CONTROL 0xe38
++
++/* MIB registers */
++#define QCA8K_PORT_MIB_COUNTER(_i) (0x1000 + (_i) * 0x100)
++
++/* QCA specific MII registers */
++#define MII_ATH_MMD_ADDR 0x0d
++#define MII_ATH_MMD_DATA 0x0e
++
++enum {
++ QCA8K_PORT_SPEED_10M = 0,
++ QCA8K_PORT_SPEED_100M = 1,
++ QCA8K_PORT_SPEED_1000M = 2,
++ QCA8K_PORT_SPEED_ERR = 3,
++};
++
++enum qca8k_fdb_cmd {
++ QCA8K_FDB_FLUSH = 1,
++ QCA8K_FDB_LOAD = 2,
++ QCA8K_FDB_PURGE = 3,
++ QCA8K_FDB_FLUSH_PORT = 5,
++ QCA8K_FDB_NEXT = 6,
++ QCA8K_FDB_SEARCH = 7,
++};
++
++enum qca8k_vlan_cmd {
++ QCA8K_VLAN_FLUSH = 1,
++ QCA8K_VLAN_LOAD = 2,
++ QCA8K_VLAN_PURGE = 3,
++ QCA8K_VLAN_REMOVE_PORT = 4,
++ QCA8K_VLAN_NEXT = 5,
++ QCA8K_VLAN_READ = 6,
++};
++
++enum qca8k_mid_cmd {
++ QCA8K_MIB_FLUSH = 1,
++ QCA8K_MIB_FLUSH_PORT = 2,
++ QCA8K_MIB_CAST = 3,
++};
++
++struct qca8k_match_data {
++ u8 id;
++ bool reduced_package;
++ u8 mib_count;
++};
++
++enum {
++ QCA8K_CPU_PORT0,
++ QCA8K_CPU_PORT6,
++};
++
++struct qca8k_mgmt_eth_data {
++ struct completion rw_done;
++ struct mutex mutex; /* Enforce one mdio read/write at time */
++ bool ack;
++ u32 seq;
++ u32 data[4];
++};
++
++struct qca8k_mib_eth_data {
++ struct completion rw_done;
++ struct mutex mutex; /* Process one command at time */
++ refcount_t port_parsed; /* Counter to track parsed port */
++ u8 req_port;
++ u64 *data; /* pointer to ethtool data */
++};
++
++struct qca8k_ports_config {
++ bool sgmii_rx_clk_falling_edge;
++ bool sgmii_tx_clk_falling_edge;
++ bool sgmii_enable_pll;
++ u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
++ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
++};
++
++struct qca8k_mdio_cache {
++/* The 32bit switch registers are accessed indirectly. To achieve this we need
++ * to set the page of the register. Track the last page that was set to reduce
++ * mdio writes
++ */
++ u16 page;
++/* lo and hi can also be cached and from Documentation we can skip one
++ * extra mdio write if lo or hi is didn't change.
++ */
++ u16 lo;
++ u16 hi;
++};
++
++struct qca8k_priv {
++ u8 switch_id;
++ u8 switch_revision;
++ u8 mirror_rx;
++ u8 mirror_tx;
++ u8 lag_hash_mode;
++ /* Each bit correspond to a port. This switch can support a max of 7 port.
++ * Bit 1: port enabled. Bit 0: port disabled.
++ */
++ u8 port_enabled_map;
++ struct qca8k_ports_config ports_config;
++ struct regmap *regmap;
++ struct mii_bus *bus;
++ struct dsa_switch *ds;
++ struct mutex reg_mutex;
++ struct device *dev;
++ struct gpio_desc *reset_gpio;
++ struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
++ struct qca8k_mgmt_eth_data mgmt_eth_data;
++ struct qca8k_mib_eth_data mib_eth_data;
++ struct qca8k_mdio_cache mdio_cache;
++};
++
++struct qca8k_mib_desc {
++ unsigned int size;
++ unsigned int offset;
++ const char *name;
++};
++
++struct qca8k_fdb {
++ u16 vid;
++ u8 port_mask;
++ u8 aging;
++ u8 mac[6];
++};
++
++#endif /* __QCA8K_H */
+--- a/drivers/net/dsa/qca8k.c
++++ /dev/null
+@@ -1,3243 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+- * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+- * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved.
+- * Copyright (c) 2016 John Crispin <john@phrozen.org>
+- */
+-
+-#include <linux/module.h>
+-#include <linux/phy.h>
+-#include <linux/netdevice.h>
+-#include <linux/bitfield.h>
+-#include <linux/regmap.h>
+-#include <net/dsa.h>
+-#include <linux/of_net.h>
+-#include <linux/of_mdio.h>
+-#include <linux/of_platform.h>
+-#include <linux/if_bridge.h>
+-#include <linux/mdio.h>
+-#include <linux/phylink.h>
+-#include <linux/gpio/consumer.h>
+-#include <linux/etherdevice.h>
+-#include <linux/dsa/tag_qca.h>
+-
+-#include "qca8k.h"
+-
+-#define MIB_DESC(_s, _o, _n) \
+- { \
+- .size = (_s), \
+- .offset = (_o), \
+- .name = (_n), \
+- }
+-
+-static const struct qca8k_mib_desc ar8327_mib[] = {
+- MIB_DESC(1, 0x00, "RxBroad"),
+- MIB_DESC(1, 0x04, "RxPause"),
+- MIB_DESC(1, 0x08, "RxMulti"),
+- MIB_DESC(1, 0x0c, "RxFcsErr"),
+- MIB_DESC(1, 0x10, "RxAlignErr"),
+- MIB_DESC(1, 0x14, "RxRunt"),
+- MIB_DESC(1, 0x18, "RxFragment"),
+- MIB_DESC(1, 0x1c, "Rx64Byte"),
+- MIB_DESC(1, 0x20, "Rx128Byte"),
+- MIB_DESC(1, 0x24, "Rx256Byte"),
+- MIB_DESC(1, 0x28, "Rx512Byte"),
+- MIB_DESC(1, 0x2c, "Rx1024Byte"),
+- MIB_DESC(1, 0x30, "Rx1518Byte"),
+- MIB_DESC(1, 0x34, "RxMaxByte"),
+- MIB_DESC(1, 0x38, "RxTooLong"),
+- MIB_DESC(2, 0x3c, "RxGoodByte"),
+- MIB_DESC(2, 0x44, "RxBadByte"),
+- MIB_DESC(1, 0x4c, "RxOverFlow"),
+- MIB_DESC(1, 0x50, "Filtered"),
+- MIB_DESC(1, 0x54, "TxBroad"),
+- MIB_DESC(1, 0x58, "TxPause"),
+- MIB_DESC(1, 0x5c, "TxMulti"),
+- MIB_DESC(1, 0x60, "TxUnderRun"),
+- MIB_DESC(1, 0x64, "Tx64Byte"),
+- MIB_DESC(1, 0x68, "Tx128Byte"),
+- MIB_DESC(1, 0x6c, "Tx256Byte"),
+- MIB_DESC(1, 0x70, "Tx512Byte"),
+- MIB_DESC(1, 0x74, "Tx1024Byte"),
+- MIB_DESC(1, 0x78, "Tx1518Byte"),
+- MIB_DESC(1, 0x7c, "TxMaxByte"),
+- MIB_DESC(1, 0x80, "TxOverSize"),
+- MIB_DESC(2, 0x84, "TxByte"),
+- MIB_DESC(1, 0x8c, "TxCollision"),
+- MIB_DESC(1, 0x90, "TxAbortCol"),
+- MIB_DESC(1, 0x94, "TxMultiCol"),
+- MIB_DESC(1, 0x98, "TxSingleCol"),
+- MIB_DESC(1, 0x9c, "TxExcDefer"),
+- MIB_DESC(1, 0xa0, "TxDefer"),
+- MIB_DESC(1, 0xa4, "TxLateCol"),
+- MIB_DESC(1, 0xa8, "RXUnicast"),
+- MIB_DESC(1, 0xac, "TXUnicast"),
+-};
+-
+-static void
+-qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
+-{
+- regaddr >>= 1;
+- *r1 = regaddr & 0x1e;
+-
+- regaddr >>= 5;
+- *r2 = regaddr & 0x7;
+-
+- regaddr >>= 3;
+- *page = regaddr & 0x3ff;
+-}
+-
+-static int
+-qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo)
+-{
+- u16 *cached_lo = &priv->mdio_cache.lo;
+- struct mii_bus *bus = priv->bus;
+- int ret;
+-
+- if (lo == *cached_lo)
+- return 0;
+-
+- ret = bus->write(bus, phy_id, regnum, lo);
+- if (ret < 0)
+- dev_err_ratelimited(&bus->dev,
+- "failed to write qca8k 32bit lo register\n");
+-
+- *cached_lo = lo;
+- return 0;
+-}
+-
+-static int
+-qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi)
+-{
+- u16 *cached_hi = &priv->mdio_cache.hi;
+- struct mii_bus *bus = priv->bus;
+- int ret;
+-
+- if (hi == *cached_hi)
+- return 0;
+-
+- ret = bus->write(bus, phy_id, regnum, hi);
+- if (ret < 0)
+- dev_err_ratelimited(&bus->dev,
+- "failed to write qca8k 32bit hi register\n");
+-
+- *cached_hi = hi;
+- return 0;
+-}
+-
+-static int
+-qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
+-{
+- int ret;
+-
+- ret = bus->read(bus, phy_id, regnum);
+- if (ret >= 0) {
+- *val = ret;
+- ret = bus->read(bus, phy_id, regnum + 1);
+- *val |= ret << 16;
+- }
+-
+- if (ret < 0) {
+- dev_err_ratelimited(&bus->dev,
+- "failed to read qca8k 32bit register\n");
+- *val = 0;
+- return ret;
+- }
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val)
+-{
+- u16 lo, hi;
+- int ret;
+-
+- lo = val & 0xffff;
+- hi = (u16)(val >> 16);
+-
+- ret = qca8k_set_lo(priv, phy_id, regnum, lo);
+- if (ret >= 0)
+- ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi);
+-}
+-
+-static int
+-qca8k_set_page(struct qca8k_priv *priv, u16 page)
+-{
+- u16 *cached_page = &priv->mdio_cache.page;
+- struct mii_bus *bus = priv->bus;
+- int ret;
+-
+- if (page == *cached_page)
+- return 0;
+-
+- ret = bus->write(bus, 0x18, 0, page);
+- if (ret < 0) {
+- dev_err_ratelimited(&bus->dev,
+- "failed to set qca8k page\n");
+- return ret;
+- }
+-
+- *cached_page = page;
+- usleep_range(1000, 2000);
+- return 0;
+-}
+-
+-static int
+-qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
+-{
+- return regmap_read(priv->regmap, reg, val);
+-}
+-
+-static int
+-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
+-{
+- return regmap_write(priv->regmap, reg, val);
+-}
+-
+-static int
+-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
+-{
+- return regmap_update_bits(priv->regmap, reg, mask, write_val);
+-}
+-
+-static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
+-{
+- struct qca8k_mgmt_eth_data *mgmt_eth_data;
+- struct qca8k_priv *priv = ds->priv;
+- struct qca_mgmt_ethhdr *mgmt_ethhdr;
+- u8 len, cmd;
+-
+- mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
+- mgmt_eth_data = &priv->mgmt_eth_data;
+-
+- cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
+- len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
+-
+- /* Make sure the seq match the requested packet */
+- if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
+- mgmt_eth_data->ack = true;
+-
+- if (cmd == MDIO_READ) {
+- mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
+-
+- /* Get the rest of the 12 byte of data.
+- * The read/write function will extract the requested data.
+- */
+- if (len > QCA_HDR_MGMT_DATA1_LEN)
+- memcpy(mgmt_eth_data->data + 1, skb->data,
+- QCA_HDR_MGMT_DATA2_LEN);
+- }
+-
+- complete(&mgmt_eth_data->rw_done);
+-}
+-
+-static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val,
+- int priority, unsigned int len)
+-{
+- struct qca_mgmt_ethhdr *mgmt_ethhdr;
+- unsigned int real_len;
+- struct sk_buff *skb;
+- u32 *data2;
+- u16 hdr;
+-
+- skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
+- if (!skb)
+- return NULL;
+-
+- /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte
+- * Actually for some reason the steps are:
+- * 0: nothing
+- * 1-4: first 4 byte
+- * 5-6: first 12 byte
+- * 7-15: all 16 byte
+- */
+- if (len == 16)
+- real_len = 15;
+- else
+- real_len = len;
+-
+- skb_reset_mac_header(skb);
+- skb_set_network_header(skb, skb->len);
+-
+- mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN);
+-
+- hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION);
+- hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority);
+- hdr |= QCA_HDR_XMIT_FROM_CPU;
+- hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
+- hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
+-
+- mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
+- QCA_HDR_MGMT_CHECK_CODE_VAL);
+-
+- if (cmd == MDIO_WRITE)
+- mgmt_ethhdr->mdio_data = *val;
+-
+- mgmt_ethhdr->hdr = htons(hdr);
+-
+- data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
+- if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN)
+- memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
+-
+- return skb;
+-}
+-
+-static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
+-{
+- struct qca_mgmt_ethhdr *mgmt_ethhdr;
+-
+- mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
+- mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
+-}
+-
+-static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
+- struct sk_buff *skb;
+- bool ack;
+- int ret;
+-
+- skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL,
+- QCA8K_ETHERNET_MDIO_PRIORITY, len);
+- if (!skb)
+- return -ENOMEM;
+-
+- mutex_lock(&mgmt_eth_data->mutex);
+-
+- /* Check mgmt_master if is operational */
+- if (!priv->mgmt_master) {
+- kfree_skb(skb);
+- mutex_unlock(&mgmt_eth_data->mutex);
+- return -EINVAL;
+- }
+-
+- skb->dev = priv->mgmt_master;
+-
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the mdio pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
+-
+- *val = mgmt_eth_data->data[0];
+- if (len > QCA_HDR_MGMT_DATA1_LEN)
+- memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN);
+-
+- ack = mgmt_eth_data->ack;
+-
+- mutex_unlock(&mgmt_eth_data->mutex);
+-
+- if (ret <= 0)
+- return -ETIMEDOUT;
+-
+- if (!ack)
+- return -EINVAL;
+-
+- return 0;
+-}
+-
+-static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
+- struct sk_buff *skb;
+- bool ack;
+- int ret;
+-
+- skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val,
+- QCA8K_ETHERNET_MDIO_PRIORITY, len);
+- if (!skb)
+- return -ENOMEM;
+-
+- mutex_lock(&mgmt_eth_data->mutex);
+-
+- /* Check mgmt_master if is operational */
+- if (!priv->mgmt_master) {
+- kfree_skb(skb);
+- mutex_unlock(&mgmt_eth_data->mutex);
+- return -EINVAL;
+- }
+-
+- skb->dev = priv->mgmt_master;
+-
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the mdio pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
+-
+- ack = mgmt_eth_data->ack;
+-
+- mutex_unlock(&mgmt_eth_data->mutex);
+-
+- if (ret <= 0)
+- return -ETIMEDOUT;
+-
+- if (!ack)
+- return -EINVAL;
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
+-{
+- u32 val = 0;
+- int ret;
+-
+- ret = qca8k_read_eth(priv, reg, &val, sizeof(val));
+- if (ret)
+- return ret;
+-
+- val &= ~mask;
+- val |= write_val;
+-
+- return qca8k_write_eth(priv, reg, &val, sizeof(val));
+-}
+-
+-static int
+-qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- int i, count = len / sizeof(u32), ret;
+-
+- if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len))
+- return 0;
+-
+- for (i = 0; i < count; i++) {
+- ret = regmap_read(priv->regmap, reg + (i * 4), val + i);
+- if (ret < 0)
+- return ret;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- int i, count = len / sizeof(u32), ret;
+- u32 tmp;
+-
+- if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len))
+- return 0;
+-
+- for (i = 0; i < count; i++) {
+- tmp = val[i];
+-
+- ret = regmap_write(priv->regmap, reg + (i * 4), tmp);
+- if (ret < 0)
+- return ret;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- int ret;
+-
+- if (!qca8k_read_eth(priv, reg, val, sizeof(*val)))
+- return 0;
+-
+- qca8k_split_addr(reg, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret < 0)
+- goto exit;
+-
+- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val);
+-
+-exit:
+- mutex_unlock(&bus->mdio_lock);
+- return ret;
+-}
+-
+-static int
+-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- int ret;
+-
+- if (!qca8k_write_eth(priv, reg, &val, sizeof(val)))
+- return 0;
+-
+- qca8k_split_addr(reg, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret < 0)
+- goto exit;
+-
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+-
+-exit:
+- mutex_unlock(&bus->mdio_lock);
+- return ret;
+-}
+-
+-static int
+-qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- u32 val;
+- int ret;
+-
+- if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
+- return 0;
+-
+- qca8k_split_addr(reg, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret < 0)
+- goto exit;
+-
+- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
+- if (ret < 0)
+- goto exit;
+-
+- val &= ~mask;
+- val |= write_val;
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+-
+-exit:
+- mutex_unlock(&bus->mdio_lock);
+-
+- return ret;
+-}
+-
+-static const struct regmap_range qca8k_readable_ranges[] = {
+- regmap_reg_range(0x0000, 0x00e4), /* Global control */
+- regmap_reg_range(0x0100, 0x0168), /* EEE control */
+- regmap_reg_range(0x0200, 0x0270), /* Parser control */
+- regmap_reg_range(0x0400, 0x0454), /* ACL */
+- regmap_reg_range(0x0600, 0x0718), /* Lookup */
+- regmap_reg_range(0x0800, 0x0b70), /* QM */
+- regmap_reg_range(0x0c00, 0x0c80), /* PKT */
+- regmap_reg_range(0x0e00, 0x0e98), /* L3 */
+- regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */
+- regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */
+- regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */
+- regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */
+- regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */
+- regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */
+- regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */
+-
+-};
+-
+-static const struct regmap_access_table qca8k_readable_table = {
+- .yes_ranges = qca8k_readable_ranges,
+- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
+-};
+-
+-static struct regmap_config qca8k_regmap_config = {
+- .reg_bits = 16,
+- .val_bits = 32,
+- .reg_stride = 4,
+- .max_register = 0x16ac, /* end MIB - Port6 range */
+- .reg_read = qca8k_regmap_read,
+- .reg_write = qca8k_regmap_write,
+- .reg_update_bits = qca8k_regmap_update_bits,
+- .rd_table = &qca8k_readable_table,
+- .disable_locking = true, /* Locking is handled by qca8k read/write */
+- .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
+-};
+-
+-static int
+-qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+-{
+- u32 val;
+-
+- return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
+- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
+-}
+-
+-static int
+-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+-{
+- u32 reg[3];
+- int ret;
+-
+- /* load the ARL table into an array */
+- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
+- if (ret)
+- return ret;
+-
+- /* vid - 83:72 */
+- fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
+- /* aging - 67:64 */
+- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
+- /* portmask - 54:48 */
+- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
+- /* mac - 47:0 */
+- fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
+- fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
+- fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
+- fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
+- fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
+- fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
+- u8 aging)
+-{
+- u32 reg[3] = { 0 };
+-
+- /* vid - 83:72 */
+- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
+- /* aging - 67:64 */
+- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
+- /* portmask - 54:48 */
+- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
+- /* mac - 47:0 */
+- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
+- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+-
+- /* load the array into the ARL table */
+- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
+-}
+-
+-static int
+-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
+-{
+- u32 reg;
+- int ret;
+-
+- /* Set the command and FDB index */
+- reg = QCA8K_ATU_FUNC_BUSY;
+- reg |= cmd;
+- if (port >= 0) {
+- reg |= QCA8K_ATU_FUNC_PORT_EN;
+- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
+- }
+-
+- /* Write the function register triggering the table access */
+- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
+- if (ret)
+- return ret;
+-
+- /* wait for completion */
+- ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY);
+- if (ret)
+- return ret;
+-
+- /* Check for table full violation when adding an entry */
+- if (cmd == QCA8K_FDB_LOAD) {
+- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, &reg);
+- if (ret < 0)
+- return ret;
+- if (reg & QCA8K_ATU_FUNC_FULL)
+- return -1;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
+-{
+- int ret;
+-
+- qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
+- if (ret < 0)
+- return ret;
+-
+- return qca8k_fdb_read(priv, fdb);
+-}
+-
+-static int
+-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask,
+- u16 vid, u8 aging)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_write(priv, vid, port_mask, mac, aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_write(priv, vid, port_mask, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static void
+-qca8k_fdb_flush(struct qca8k_priv *priv)
+-{
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
+- mutex_unlock(&priv->reg_mutex);
+-}
+-
+-static int
+-qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
+- const u8 *mac, u16 vid)
+-{
+- struct qca8k_fdb fdb = { 0 };
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- qca8k_fdb_write(priv, vid, 0, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+- if (ret < 0)
+- goto exit;
+-
+- ret = qca8k_fdb_read(priv, &fdb);
+- if (ret < 0)
+- goto exit;
+-
+- /* Rule exist. Delete first */
+- if (!fdb.aging) {
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- if (ret)
+- goto exit;
+- }
+-
+- /* Add port to fdb portmask */
+- fdb.port_mask |= port_mask;
+-
+- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+-qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
+- const u8 *mac, u16 vid)
+-{
+- struct qca8k_fdb fdb = { 0 };
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- qca8k_fdb_write(priv, vid, 0, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+- if (ret < 0)
+- goto exit;
+-
+- /* Rule doesn't exist. Why delete? */
+- if (!fdb.aging) {
+- ret = -EINVAL;
+- goto exit;
+- }
+-
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- if (ret)
+- goto exit;
+-
+- /* Only port in the rule is this port. Don't re insert */
+- if (fdb.port_mask == port_mask)
+- goto exit;
+-
+- /* Remove port from port mask */
+- fdb.port_mask &= ~port_mask;
+-
+- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+-qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+-{
+- u32 reg;
+- int ret;
+-
+- /* Set the command and VLAN index */
+- reg = QCA8K_VTU_FUNC1_BUSY;
+- reg |= cmd;
+- reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
+-
+- /* Write the function register triggering the table access */
+- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
+- if (ret)
+- return ret;
+-
+- /* wait for completion */
+- ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
+- if (ret)
+- return ret;
+-
+- /* Check for table full violation when adding an entry */
+- if (cmd == QCA8K_VLAN_LOAD) {
+- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, &reg);
+- if (ret < 0)
+- return ret;
+- if (reg & QCA8K_VTU_FUNC1_FULL)
+- return -ENOMEM;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
+-{
+- u32 reg;
+- int ret;
+-
+- /*
+- We do the right thing with VLAN 0 and treat it as untagged while
+- preserving the tag on egress.
+- */
+- if (vid == 0)
+- return 0;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+- if (ret < 0)
+- goto out;
+-
+- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+- if (ret < 0)
+- goto out;
+- reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+- if (untagged)
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
+- else
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
+-
+- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+- if (ret)
+- goto out;
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+-
+-out:
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
+-{
+- u32 reg, mask;
+- int ret, i;
+- bool del;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+- if (ret < 0)
+- goto out;
+-
+- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+- if (ret < 0)
+- goto out;
+- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
+-
+- /* Check if we're the last member to be removed */
+- del = true;
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
+-
+- if ((reg & mask) != mask) {
+- del = false;
+- break;
+- }
+- }
+-
+- if (del) {
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
+- } else {
+- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+- if (ret)
+- goto out;
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+- }
+-
+-out:
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_mib_init(struct qca8k_priv *priv)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
+- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
+- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) |
+- QCA8K_MIB_BUSY);
+- if (ret)
+- goto exit;
+-
+- ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
+- if (ret)
+- goto exit;
+-
+- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+- if (ret)
+- goto exit;
+-
+- ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static void
+-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
+-{
+- u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
+-
+- /* Port 0 and 6 have no internal PHY */
+- if (port > 0 && port < 6)
+- mask |= QCA8K_PORT_STATUS_LINK_AUTO;
+-
+- if (enable)
+- regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+- else
+- regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+-}
+-
+-static int
+-qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
+- struct sk_buff *read_skb, u32 *val)
+-{
+- struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL);
+- bool ack;
+- int ret;
+-
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the copy pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- QCA8K_ETHERNET_TIMEOUT);
+-
+- ack = mgmt_eth_data->ack;
+-
+- if (ret <= 0)
+- return -ETIMEDOUT;
+-
+- if (!ack)
+- return -EINVAL;
+-
+- *val = mgmt_eth_data->data[0];
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
+- int regnum, u16 data)
+-{
+- struct sk_buff *write_skb, *clear_skb, *read_skb;
+- struct qca8k_mgmt_eth_data *mgmt_eth_data;
+- u32 write_val, clear_val = 0, val;
+- struct net_device *mgmt_master;
+- int ret, ret1;
+- bool ack;
+-
+- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+- return -EINVAL;
+-
+- mgmt_eth_data = &priv->mgmt_eth_data;
+-
+- write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+- QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+- QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+-
+- if (read) {
+- write_val |= QCA8K_MDIO_MASTER_READ;
+- } else {
+- write_val |= QCA8K_MDIO_MASTER_WRITE;
+- write_val |= QCA8K_MDIO_MASTER_DATA(data);
+- }
+-
+- /* Prealloc all the needed skb before the lock */
+- write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val,
+- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val));
+- if (!write_skb)
+- return -ENOMEM;
+-
+- clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val,
+- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
+- if (!clear_skb) {
+- ret = -ENOMEM;
+- goto err_clear_skb;
+- }
+-
+- read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val,
+- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
+- if (!read_skb) {
+- ret = -ENOMEM;
+- goto err_read_skb;
+- }
+-
+- /* Actually start the request:
+- * 1. Send mdio master packet
+- * 2. Busy Wait for mdio master command
+- * 3. Get the data if we are reading
+- * 4. Reset the mdio master (even with error)
+- */
+- mutex_lock(&mgmt_eth_data->mutex);
+-
+- /* Check if mgmt_master is operational */
+- mgmt_master = priv->mgmt_master;
+- if (!mgmt_master) {
+- mutex_unlock(&mgmt_eth_data->mutex);
+- ret = -EINVAL;
+- goto err_mgmt_master;
+- }
+-
+- read_skb->dev = mgmt_master;
+- clear_skb->dev = mgmt_master;
+- write_skb->dev = mgmt_master;
+-
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the write pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(write_skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- QCA8K_ETHERNET_TIMEOUT);
+-
+- ack = mgmt_eth_data->ack;
+-
+- if (ret <= 0) {
+- ret = -ETIMEDOUT;
+- kfree_skb(read_skb);
+- goto exit;
+- }
+-
+- if (!ack) {
+- ret = -EINVAL;
+- kfree_skb(read_skb);
+- goto exit;
+- }
+-
+- ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1,
+- !(val & QCA8K_MDIO_MASTER_BUSY), 0,
+- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- mgmt_eth_data, read_skb, &val);
+-
+- if (ret < 0 && ret1 < 0) {
+- ret = ret1;
+- goto exit;
+- }
+-
+- if (read) {
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the read pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(read_skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- QCA8K_ETHERNET_TIMEOUT);
+-
+- ack = mgmt_eth_data->ack;
+-
+- if (ret <= 0) {
+- ret = -ETIMEDOUT;
+- goto exit;
+- }
+-
+- if (!ack) {
+- ret = -EINVAL;
+- goto exit;
+- }
+-
+- ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK;
+- } else {
+- kfree_skb(read_skb);
+- }
+-exit:
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the clear pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(clear_skb);
+-
+- wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- QCA8K_ETHERNET_TIMEOUT);
+-
+- mutex_unlock(&mgmt_eth_data->mutex);
+-
+- return ret;
+-
+- /* Error handling before lock */
+-err_mgmt_master:
+- kfree_skb(read_skb);
+-err_read_skb:
+- kfree_skb(clear_skb);
+-err_clear_skb:
+- kfree_skb(write_skb);
+-
+- return ret;
+-}
+-
+-static u32
+-qca8k_port_to_phy(int port)
+-{
+- /* From Andrew Lunn:
+- * Port 0 has no internal phy.
+- * Port 1 has an internal PHY at MDIO address 0.
+- * Port 2 has an internal PHY at MDIO address 1.
+- * ...
+- * Port 5 has an internal PHY at MDIO address 4.
+- * Port 6 has no internal PHY.
+- */
+-
+- return port - 1;
+-}
+-
+-static int
+-qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)
+-{
+- u16 r1, r2, page;
+- u32 val;
+- int ret, ret1;
+-
+- qca8k_split_addr(reg, &r1, &r2, &page);
+-
+- ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0,
+- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- bus, 0x10 | r2, r1, &val);
+-
+- /* Check if qca8k_read has failed for a different reason
+- * before returnting -ETIMEDOUT
+- */
+- if (ret < 0 && ret1 < 0)
+- return ret1;
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data)
+-{
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- u32 val;
+- int ret;
+-
+- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+- return -EINVAL;
+-
+- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+- QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+- QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+- QCA8K_MDIO_MASTER_DATA(data);
+-
+- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret)
+- goto exit;
+-
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+-
+- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_BUSY);
+-
+-exit:
+- /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
+-
+- mutex_unlock(&bus->mdio_lock);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum)
+-{
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- u32 val;
+- int ret;
+-
+- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+- return -EINVAL;
+-
+- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+- QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+- QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+-
+- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret)
+- goto exit;
+-
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+-
+- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_BUSY);
+- if (ret)
+- goto exit;
+-
+- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
+-
+-exit:
+- /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
+-
+- mutex_unlock(&bus->mdio_lock);
+-
+- if (ret >= 0)
+- ret = val & QCA8K_MDIO_MASTER_DATA_MASK;
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data)
+-{
+- struct qca8k_priv *priv = slave_bus->priv;
+- int ret;
+-
+- /* Use mdio Ethernet when available, fallback to legacy one on error */
+- ret = qca8k_phy_eth_command(priv, false, phy, regnum, data);
+- if (!ret)
+- return 0;
+-
+- return qca8k_mdio_write(priv, phy, regnum, data);
+-}
+-
+-static int
+-qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum)
+-{
+- struct qca8k_priv *priv = slave_bus->priv;
+- int ret;
+-
+- /* Use mdio Ethernet when available, fallback to legacy one on error */
+- ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0);
+- if (ret >= 0)
+- return ret;
+-
+- ret = qca8k_mdio_read(priv, phy, regnum);
+-
+- if (ret < 0)
+- return 0xffff;
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data)
+-{
+- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+-
+- return qca8k_internal_mdio_write(slave_bus, port, regnum, data);
+-}
+-
+-static int
+-qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum)
+-{
+- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+-
+- return qca8k_internal_mdio_read(slave_bus, port, regnum);
+-}
+-
+-static int
+-qca8k_mdio_register(struct qca8k_priv *priv)
+-{
+- struct dsa_switch *ds = priv->ds;
+- struct device_node *mdio;
+- struct mii_bus *bus;
+-
+- bus = devm_mdiobus_alloc(ds->dev);
+- if (!bus)
+- return -ENOMEM;
+-
+- bus->priv = (void *)priv;
+- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d",
+- ds->dst->index, ds->index);
+- bus->parent = ds->dev;
+- bus->phy_mask = ~ds->phys_mii_mask;
+- ds->slave_mii_bus = bus;
+-
+- /* Check if the devicetree declare the port:phy mapping */
+- mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
+- if (of_device_is_available(mdio)) {
+- bus->name = "qca8k slave mii";
+- bus->read = qca8k_internal_mdio_read;
+- bus->write = qca8k_internal_mdio_write;
+- return devm_of_mdiobus_register(priv->dev, bus, mdio);
+- }
+-
+- /* If a mapping can't be found the legacy mapping is used,
+- * using the qca8k_port_to_phy function
+- */
+- bus->name = "qca8k-legacy slave mii";
+- bus->read = qca8k_legacy_mdio_read;
+- bus->write = qca8k_legacy_mdio_write;
+- return devm_mdiobus_register(priv->dev, bus);
+-}
+-
+-static int
+-qca8k_setup_mdio_bus(struct qca8k_priv *priv)
+-{
+- u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg;
+- struct device_node *ports, *port;
+- phy_interface_t mode;
+- int err;
+-
+- ports = of_get_child_by_name(priv->dev->of_node, "ports");
+- if (!ports)
+- ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports");
+-
+- if (!ports)
+- return -EINVAL;
+-
+- for_each_available_child_of_node(ports, port) {
+- err = of_property_read_u32(port, "reg", &reg);
+- if (err) {
+- of_node_put(port);
+- of_node_put(ports);
+- return err;
+- }
+-
+- if (!dsa_is_user_port(priv->ds, reg))
+- continue;
+-
+- of_get_phy_mode(port, &mode);
+-
+- if (of_property_read_bool(port, "phy-handle") &&
+- mode != PHY_INTERFACE_MODE_INTERNAL)
+- external_mdio_mask |= BIT(reg);
+- else
+- internal_mdio_mask |= BIT(reg);
+- }
+-
+- of_node_put(ports);
+- if (!external_mdio_mask && !internal_mdio_mask) {
+- dev_err(priv->dev, "no PHYs are defined.\n");
+- return -EINVAL;
+- }
+-
+- /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
+- * the MDIO_MASTER register also _disconnects_ the external MDC
+- * passthrough to the internal PHYs. It's not possible to use both
+- * configurations at the same time!
+- *
+- * Because this came up during the review process:
+- * If the external mdio-bus driver is capable magically disabling
+- * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's
+- * accessors for the time being, it would be possible to pull this
+- * off.
+- */
+- if (!!external_mdio_mask && !!internal_mdio_mask) {
+- dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
+- return -EINVAL;
+- }
+-
+- if (external_mdio_mask) {
+- /* Make sure to disable the internal mdio bus in cases
+- * a dt-overlay and driver reload changed the configuration
+- */
+-
+- return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_EN);
+- }
+-
+- return qca8k_mdio_register(priv);
+-}
+-
+-static int
+-qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
+-{
+- u32 mask = 0;
+- int ret = 0;
+-
+- /* SoC specific settings for ipq8064.
+- * If more device require this consider adding
+- * a dedicated binding.
+- */
+- if (of_machine_is_compatible("qcom,ipq8064"))
+- mask |= QCA8K_MAC_PWR_RGMII0_1_8V;
+-
+- /* SoC specific settings for ipq8065 */
+- if (of_machine_is_compatible("qcom,ipq8065"))
+- mask |= QCA8K_MAC_PWR_RGMII1_1_8V;
+-
+- if (mask) {
+- ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL,
+- QCA8K_MAC_PWR_RGMII0_1_8V |
+- QCA8K_MAC_PWR_RGMII1_1_8V,
+- mask);
+- }
+-
+- return ret;
+-}
+-
+-static int qca8k_find_cpu_port(struct dsa_switch *ds)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- /* Find the connected cpu port. Valid port are 0 or 6 */
+- if (dsa_is_cpu_port(ds, 0))
+- return 0;
+-
+- dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
+-
+- if (dsa_is_cpu_port(ds, 6))
+- return 6;
+-
+- return -EINVAL;
+-}
+-
+-static int
+-qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
+-{
+- struct device_node *node = priv->dev->of_node;
+- const struct qca8k_match_data *data;
+- u32 val = 0;
+- int ret;
+-
+- /* QCA8327 require to set to the correct mode.
+- * His bigger brother QCA8328 have the 172 pin layout.
+- * Should be applied by default but we set this just to make sure.
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8327) {
+- data = of_device_get_match_data(priv->dev);
+-
+- /* Set the correct package of 148 pin for QCA8327 */
+- if (data->reduced_package)
+- val |= QCA8327_PWS_PACKAGE148_EN;
+-
+- ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
+- val);
+- if (ret)
+- return ret;
+- }
+-
+- if (of_property_read_bool(node, "qca,ignore-power-on-sel"))
+- val |= QCA8K_PWS_POWER_ON_SEL;
+-
+- if (of_property_read_bool(node, "qca,led-open-drain")) {
+- if (!(val & QCA8K_PWS_POWER_ON_SEL)) {
+- dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set.");
+- return -EINVAL;
+- }
+-
+- val |= QCA8K_PWS_LED_OPEN_EN_CSR;
+- }
+-
+- return qca8k_rmw(priv, QCA8K_REG_PWS,
+- QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL,
+- val);
+-}
+-
+-static int
+-qca8k_parse_port_config(struct qca8k_priv *priv)
+-{
+- int port, cpu_port_index = -1, ret;
+- struct device_node *port_dn;
+- phy_interface_t mode;
+- struct dsa_port *dp;
+- u32 delay;
+-
+- /* We have 2 CPU port. Check them */
+- for (port = 0; port < QCA8K_NUM_PORTS; port++) {
+- /* Skip every other port */
+- if (port != 0 && port != 6)
+- continue;
+-
+- dp = dsa_to_port(priv->ds, port);
+- port_dn = dp->dn;
+- cpu_port_index++;
+-
+- if (!of_device_is_available(port_dn))
+- continue;
+-
+- ret = of_get_phy_mode(port_dn, &mode);
+- if (ret)
+- continue;
+-
+- switch (mode) {
+- case PHY_INTERFACE_MODE_RGMII:
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- case PHY_INTERFACE_MODE_SGMII:
+- delay = 0;
+-
+- if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay))
+- /* Switch regs accept value in ns, convert ps to ns */
+- delay = delay / 1000;
+- else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
+- mode == PHY_INTERFACE_MODE_RGMII_TXID)
+- delay = 1;
+-
+- if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) {
+- dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
+- delay = 3;
+- }
+-
+- priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay;
+-
+- delay = 0;
+-
+- if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay))
+- /* Switch regs accept value in ns, convert ps to ns */
+- delay = delay / 1000;
+- else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
+- mode == PHY_INTERFACE_MODE_RGMII_RXID)
+- delay = 2;
+-
+- if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) {
+- dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
+- delay = 3;
+- }
+-
+- priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay;
+-
+- /* Skip sgmii parsing for rgmii* mode */
+- if (mode == PHY_INTERFACE_MODE_RGMII ||
+- mode == PHY_INTERFACE_MODE_RGMII_ID ||
+- mode == PHY_INTERFACE_MODE_RGMII_TXID ||
+- mode == PHY_INTERFACE_MODE_RGMII_RXID)
+- break;
+-
+- if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
+- priv->ports_config.sgmii_tx_clk_falling_edge = true;
+-
+- if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
+- priv->ports_config.sgmii_rx_clk_falling_edge = true;
+-
+- if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) {
+- priv->ports_config.sgmii_enable_pll = true;
+-
+- if (priv->switch_id == QCA8K_ID_QCA8327) {
+- dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling");
+- priv->ports_config.sgmii_enable_pll = false;
+- }
+-
+- if (priv->switch_revision < 2)
+- dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more.");
+- }
+-
+- break;
+- default:
+- continue;
+- }
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_setup(struct dsa_switch *ds)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int cpu_port, ret, i;
+- u32 mask;
+-
+- cpu_port = qca8k_find_cpu_port(ds);
+- if (cpu_port < 0) {
+- dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
+- return cpu_port;
+- }
+-
+- /* Parse CPU port config to be later used in phy_link mac_config */
+- ret = qca8k_parse_port_config(priv);
+- if (ret)
+- return ret;
+-
+- ret = qca8k_setup_mdio_bus(priv);
+- if (ret)
+- return ret;
+-
+- ret = qca8k_setup_of_pws_reg(priv);
+- if (ret)
+- return ret;
+-
+- ret = qca8k_setup_mac_pwr_sel(priv);
+- if (ret)
+- return ret;
+-
+- /* Make sure MAC06 is disabled */
+- ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL,
+- QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
+- if (ret) {
+- dev_err(priv->dev, "failed disabling MAC06 exchange");
+- return ret;
+- }
+-
+- /* Enable CPU Port */
+- ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+- if (ret) {
+- dev_err(priv->dev, "failed enabling CPU port");
+- return ret;
+- }
+-
+- /* Enable MIB counters */
+- ret = qca8k_mib_init(priv);
+- if (ret)
+- dev_warn(priv->dev, "mib init failed");
+-
+- /* Initial setup of all ports */
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- /* Disable forwarding by default on all ports */
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_MEMBER, 0);
+- if (ret)
+- return ret;
+-
+- /* Enable QCA header mode on all cpu ports */
+- if (dsa_is_cpu_port(ds, i)) {
+- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
+- FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
+- FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
+- if (ret) {
+- dev_err(priv->dev, "failed enabling QCA header mode");
+- return ret;
+- }
+- }
+-
+- /* Disable MAC by default on all user ports */
+- if (dsa_is_user_port(ds, i))
+- qca8k_port_set_status(priv, i, 0);
+- }
+-
+- /* Forward all unknown frames to CPU port for Linux processing
+- * Notice that in multi-cpu config only one port should be set
+- * for igmp, unknown, multicast and broadcast packet
+- */
+- ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) |
+- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) |
+- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) |
+- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));
+- if (ret)
+- return ret;
+-
+- /* Setup connection between CPU port & user ports
+- * Configure specific switch configuration for ports
+- */
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- /* CPU port gets connected to all user ports of the switch */
+- if (dsa_is_cpu_port(ds, i)) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
+- if (ret)
+- return ret;
+- }
+-
+- /* Individual user ports get connected to CPU port only */
+- if (dsa_is_user_port(ds, i)) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_MEMBER,
+- BIT(cpu_port));
+- if (ret)
+- return ret;
+-
+- /* Enable ARP Auto-learning by default */
+- ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_LEARN);
+- if (ret)
+- return ret;
+-
+- /* For port based vlans to work we need to set the
+- * default egress vid
+- */
+- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
+- QCA8K_EGREES_VLAN_PORT_MASK(i),
+- QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));
+- if (ret)
+- return ret;
+-
+- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
+- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
+- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
+- if (ret)
+- return ret;
+- }
+-
+- /* The port 5 of the qca8337 have some problem in flood condition. The
+- * original legacy driver had some specific buffer and priority settings
+- * for the different port suggested by the QCA switch team. Add this
+- * missing settings to improve switch stability under load condition.
+- * This problem is limited to qca8337 and other qca8k switch are not affected.
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8337) {
+- switch (i) {
+- /* The 2 CPU port and port 5 requires some different
+- * priority than any other ports.
+- */
+- case 0:
+- case 5:
+- case 6:
+- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
+- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
+- break;
+- default:
+- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
+- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
+- }
+- qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
+-
+- mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
+- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
+- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+- QCA8K_PORT_HOL_CTRL1_WRED_EN;
+- qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
+- QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
+- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
+- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+- QCA8K_PORT_HOL_CTRL1_WRED_EN,
+- mask);
+- }
+- }
+-
+- /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
+- if (priv->switch_id == QCA8K_ID_QCA8327) {
+- mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
+- QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
+- qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
+- QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK |
+- QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,
+- mask);
+- }
+-
+- /* Setup our port MTUs to match power on defaults */
+- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
+- if (ret)
+- dev_warn(priv->dev, "failed setting MTU settings");
+-
+- /* Flush the FDB table */
+- qca8k_fdb_flush(priv);
+-
+- /* We don't have interrupts for link changes, so we need to poll */
+- ds->pcs_poll = true;
+-
+- /* Set min a max ageing value supported */
+- ds->ageing_time_min = 7000;
+- ds->ageing_time_max = 458745000;
+-
+- /* Set max number of LAGs supported */
+- ds->num_lag_ids = QCA8K_NUM_LAGS;
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index,
+- u32 reg)
+-{
+- u32 delay, val = 0;
+- int ret;
+-
+- /* Delay can be declared in 3 different way.
+- * Mode to rgmii and internal-delay standard binding defined
+- * rgmii-id or rgmii-tx/rx phy mode set.
+- * The parse logic set a delay different than 0 only when one
+- * of the 3 different way is used. In all other case delay is
+- * not enabled. With ID or TX/RXID delay is enabled and set
+- * to the default and recommended value.
+- */
+- if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) {
+- delay = priv->ports_config.rgmii_tx_delay[cpu_port_index];
+-
+- val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
+- }
+-
+- if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) {
+- delay = priv->ports_config.rgmii_rx_delay[cpu_port_index];
+-
+- val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
+- }
+-
+- /* Set RGMII delay based on the selected values */
+- ret = qca8k_rmw(priv, reg,
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN,
+- val);
+- if (ret)
+- dev_err(priv->dev, "Failed to set internal delay for CPU port%d",
+- cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6);
+-}
+-
+-static void
+-qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+- const struct phylink_link_state *state)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int cpu_port_index, ret;
+- u32 reg, val;
+-
+- switch (port) {
+- case 0: /* 1st CPU port */
+- if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+- state->interface != PHY_INTERFACE_MODE_SGMII)
+- return;
+-
+- reg = QCA8K_REG_PORT0_PAD_CTRL;
+- cpu_port_index = QCA8K_CPU_PORT0;
+- break;
+- case 1:
+- case 2:
+- case 3:
+- case 4:
+- case 5:
+- /* Internal PHY, nothing to do */
+- return;
+- case 6: /* 2nd CPU port / external PHY */
+- if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+- state->interface != PHY_INTERFACE_MODE_SGMII &&
+- state->interface != PHY_INTERFACE_MODE_1000BASEX)
+- return;
+-
+- reg = QCA8K_REG_PORT6_PAD_CTRL;
+- cpu_port_index = QCA8K_CPU_PORT6;
+- break;
+- default:
+- dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
+- return;
+- }
+-
+- if (port != 6 && phylink_autoneg_inband(mode)) {
+- dev_err(ds->dev, "%s: in-band negotiation unsupported\n",
+- __func__);
+- return;
+- }
+-
+- switch (state->interface) {
+- case PHY_INTERFACE_MODE_RGMII:
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
+-
+- /* Configure rgmii delay */
+- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
+-
+- /* QCA8337 requires to set rgmii rx delay for all ports.
+- * This is enabled through PORT5_PAD_CTRL for all ports,
+- * rather than individual port registers.
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8337)
+- qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+- break;
+- case PHY_INTERFACE_MODE_SGMII:
+- case PHY_INTERFACE_MODE_1000BASEX:
+- /* Enable SGMII on the port */
+- qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
+-
+- /* Enable/disable SerDes auto-negotiation as necessary */
+- ret = qca8k_read(priv, QCA8K_REG_PWS, &val);
+- if (ret)
+- return;
+- if (phylink_autoneg_inband(mode))
+- val &= ~QCA8K_PWS_SERDES_AEN_DIS;
+- else
+- val |= QCA8K_PWS_SERDES_AEN_DIS;
+- qca8k_write(priv, QCA8K_REG_PWS, val);
+-
+- /* Configure the SGMII parameters */
+- ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
+- if (ret)
+- return;
+-
+- val |= QCA8K_SGMII_EN_SD;
+-
+- if (priv->ports_config.sgmii_enable_pll)
+- val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
+- QCA8K_SGMII_EN_TX;
+-
+- if (dsa_is_cpu_port(ds, port)) {
+- /* CPU port, we're talking to the CPU MAC, be a PHY */
+- val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
+- val |= QCA8K_SGMII_MODE_CTRL_PHY;
+- } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+- val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
+- val |= QCA8K_SGMII_MODE_CTRL_MAC;
+- } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
+- val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
+- val |= QCA8K_SGMII_MODE_CTRL_BASEX;
+- }
+-
+- qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
+-
+- /* From original code is reported port instability as SGMII also
+- * require delay set. Apply advised values here or take them from DT.
+- */
+- if (state->interface == PHY_INTERFACE_MODE_SGMII)
+- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
+-
+- /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
+- * falling edge is set writing in the PORT0 PAD reg
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8327 ||
+- priv->switch_id == QCA8K_ID_QCA8337)
+- reg = QCA8K_REG_PORT0_PAD_CTRL;
+-
+- val = 0;
+-
+- /* SGMII Clock phase configuration */
+- if (priv->ports_config.sgmii_rx_clk_falling_edge)
+- val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE;
+-
+- if (priv->ports_config.sgmii_tx_clk_falling_edge)
+- val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
+-
+- if (val)
+- ret = qca8k_rmw(priv, reg,
+- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
+- val);
+-
+- break;
+- default:
+- dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
+- phy_modes(state->interface), port);
+- return;
+- }
+-}
+-
+-static void
+-qca8k_phylink_validate(struct dsa_switch *ds, int port,
+- unsigned long *supported,
+- struct phylink_link_state *state)
+-{
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+-
+- switch (port) {
+- case 0: /* 1st CPU port */
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+- state->interface != PHY_INTERFACE_MODE_SGMII)
+- goto unsupported;
+- break;
+- case 1:
+- case 2:
+- case 3:
+- case 4:
+- case 5:
+- /* Internal PHY */
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_GMII &&
+- state->interface != PHY_INTERFACE_MODE_INTERNAL)
+- goto unsupported;
+- break;
+- case 6: /* 2nd CPU port / external PHY */
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+- state->interface != PHY_INTERFACE_MODE_SGMII &&
+- state->interface != PHY_INTERFACE_MODE_1000BASEX)
+- goto unsupported;
+- break;
+- default:
+-unsupported:
+- linkmode_zero(supported);
+- return;
+- }
+-
+- phylink_set_port_modes(mask);
+- phylink_set(mask, Autoneg);
+-
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 10baseT_Half);
+- phylink_set(mask, 10baseT_Full);
+- phylink_set(mask, 100baseT_Half);
+- phylink_set(mask, 100baseT_Full);
+-
+- if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
+- phylink_set(mask, 1000baseX_Full);
+-
+- phylink_set(mask, Pause);
+- phylink_set(mask, Asym_Pause);
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
+-}
+-
+-static int
+-qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- u32 reg;
+- int ret;
+-
+- ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), &reg);
+- if (ret < 0)
+- return ret;
+-
+- state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
+- state->an_complete = state->link;
+- state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO);
+- state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL :
+- DUPLEX_HALF;
+-
+- switch (reg & QCA8K_PORT_STATUS_SPEED) {
+- case QCA8K_PORT_STATUS_SPEED_10:
+- state->speed = SPEED_10;
+- break;
+- case QCA8K_PORT_STATUS_SPEED_100:
+- state->speed = SPEED_100;
+- break;
+- case QCA8K_PORT_STATUS_SPEED_1000:
+- state->speed = SPEED_1000;
+- break;
+- default:
+- state->speed = SPEED_UNKNOWN;
+- break;
+- }
+-
+- state->pause = MLO_PAUSE_NONE;
+- if (reg & QCA8K_PORT_STATUS_RXFLOW)
+- state->pause |= MLO_PAUSE_RX;
+- if (reg & QCA8K_PORT_STATUS_TXFLOW)
+- state->pause |= MLO_PAUSE_TX;
+-
+- return 1;
+-}
+-
+-static void
+-qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
+- phy_interface_t interface)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- qca8k_port_set_status(priv, port, 0);
+-}
+-
+-static void
+-qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
+- phy_interface_t interface, struct phy_device *phydev,
+- int speed, int duplex, bool tx_pause, bool rx_pause)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- u32 reg;
+-
+- if (phylink_autoneg_inband(mode)) {
+- reg = QCA8K_PORT_STATUS_LINK_AUTO;
+- } else {
+- switch (speed) {
+- case SPEED_10:
+- reg = QCA8K_PORT_STATUS_SPEED_10;
+- break;
+- case SPEED_100:
+- reg = QCA8K_PORT_STATUS_SPEED_100;
+- break;
+- case SPEED_1000:
+- reg = QCA8K_PORT_STATUS_SPEED_1000;
+- break;
+- default:
+- reg = QCA8K_PORT_STATUS_LINK_AUTO;
+- break;
+- }
+-
+- if (duplex == DUPLEX_FULL)
+- reg |= QCA8K_PORT_STATUS_DUPLEX;
+-
+- if (rx_pause || dsa_is_cpu_port(ds, port))
+- reg |= QCA8K_PORT_STATUS_RXFLOW;
+-
+- if (tx_pause || dsa_is_cpu_port(ds, port))
+- reg |= QCA8K_PORT_STATUS_TXFLOW;
+- }
+-
+- reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
+-
+- qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
+-}
+-
+-static void
+-qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
+-{
+- const struct qca8k_match_data *match_data;
+- struct qca8k_priv *priv = ds->priv;
+- int i;
+-
+- if (stringset != ETH_SS_STATS)
+- return;
+-
+- match_data = of_device_get_match_data(priv->dev);
+-
+- for (i = 0; i < match_data->mib_count; i++)
+- strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
+- ETH_GSTRING_LEN);
+-}
+-
+-static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb)
+-{
+- const struct qca8k_match_data *match_data;
+- struct qca8k_mib_eth_data *mib_eth_data;
+- struct qca8k_priv *priv = ds->priv;
+- const struct qca8k_mib_desc *mib;
+- struct mib_ethhdr *mib_ethhdr;
+- int i, mib_len, offset = 0;
+- u64 *data;
+- u8 port;
+-
+- mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb);
+- mib_eth_data = &priv->mib_eth_data;
+-
+- /* The switch autocast every port. Ignore other packet and
+- * parse only the requested one.
+- */
+- port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr));
+- if (port != mib_eth_data->req_port)
+- goto exit;
+-
+- match_data = device_get_match_data(priv->dev);
+- data = mib_eth_data->data;
+-
+- for (i = 0; i < match_data->mib_count; i++) {
+- mib = &ar8327_mib[i];
+-
+- /* First 3 mib are present in the skb head */
+- if (i < 3) {
+- data[i] = mib_ethhdr->data[i];
+- continue;
+- }
+-
+- mib_len = sizeof(uint32_t);
+-
+- /* Some mib are 64 bit wide */
+- if (mib->size == 2)
+- mib_len = sizeof(uint64_t);
+-
+- /* Copy the mib value from packet to the */
+- memcpy(data + i, skb->data + offset, mib_len);
+-
+- /* Set the offset for the next mib */
+- offset += mib_len;
+- }
+-
+-exit:
+- /* Complete on receiving all the mib packet */
+- if (refcount_dec_and_test(&mib_eth_data->port_parsed))
+- complete(&mib_eth_data->rw_done);
+-}
+-
+-static int
+-qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data)
+-{
+- struct dsa_port *dp = dsa_to_port(ds, port);
+- struct qca8k_mib_eth_data *mib_eth_data;
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- mib_eth_data = &priv->mib_eth_data;
+-
+- mutex_lock(&mib_eth_data->mutex);
+-
+- reinit_completion(&mib_eth_data->rw_done);
+-
+- mib_eth_data->req_port = dp->index;
+- mib_eth_data->data = data;
+- refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS);
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- /* Send mib autocast request */
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
+- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
+- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) |
+- QCA8K_MIB_BUSY);
+-
+- mutex_unlock(&priv->reg_mutex);
+-
+- if (ret)
+- goto exit;
+-
+- ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT);
+-
+-exit:
+- mutex_unlock(&mib_eth_data->mutex);
+-
+- return ret;
+-}
+-
+-static void
+-qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
+- uint64_t *data)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- const struct qca8k_match_data *match_data;
+- const struct qca8k_mib_desc *mib;
+- u32 reg, i, val;
+- u32 hi = 0;
+- int ret;
+-
+- if (priv->mgmt_master &&
+- qca8k_get_ethtool_stats_eth(ds, port, data) > 0)
+- return;
+-
+- match_data = of_device_get_match_data(priv->dev);
+-
+- for (i = 0; i < match_data->mib_count; i++) {
+- mib = &ar8327_mib[i];
+- reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+-
+- ret = qca8k_read(priv, reg, &val);
+- if (ret < 0)
+- continue;
+-
+- if (mib->size == 2) {
+- ret = qca8k_read(priv, reg + 4, &hi);
+- if (ret < 0)
+- continue;
+- }
+-
+- data[i] = val;
+- if (mib->size == 2)
+- data[i] |= (u64)hi << 32;
+- }
+-}
+-
+-static int
+-qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
+-{
+- const struct qca8k_match_data *match_data;
+- struct qca8k_priv *priv = ds->priv;
+-
+- if (sset != ETH_SS_STATS)
+- return 0;
+-
+- match_data = of_device_get_match_data(priv->dev);
+-
+- return match_data->mib_count;
+-}
+-
+-static int
+-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
+- u32 reg;
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, &reg);
+- if (ret < 0)
+- goto exit;
+-
+- if (eee->eee_enabled)
+- reg |= lpi_en;
+- else
+- reg &= ~lpi_en;
+- ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
+-{
+- /* Nothing to do on the port's MAC */
+- return 0;
+-}
+-
+-static void
+-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u32 stp_state;
+-
+- switch (state) {
+- case BR_STATE_DISABLED:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED;
+- break;
+- case BR_STATE_BLOCKING:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING;
+- break;
+- case BR_STATE_LISTENING:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING;
+- break;
+- case BR_STATE_LEARNING:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
+- break;
+- case BR_STATE_FORWARDING:
+- default:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
+- break;
+- }
+-
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
+-}
+-
+-static int
+-qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int port_mask, cpu_port;
+- int i, ret;
+-
+- cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+- port_mask = BIT(cpu_port);
+-
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- if (dsa_is_cpu_port(ds, i))
+- continue;
+- if (dsa_to_port(ds, i)->bridge_dev != br)
+- continue;
+- /* Add this port to the portvlan mask of the other ports
+- * in the bridge
+- */
+- ret = regmap_set_bits(priv->regmap,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
+- if (ret)
+- return ret;
+- if (i != port)
+- port_mask |= BIT(i);
+- }
+-
+- /* Add all other ports to this ports portvlan mask */
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_MEMBER, port_mask);
+-
+- return ret;
+-}
+-
+-static void
+-qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int cpu_port, i;
+-
+- cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+-
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- if (dsa_is_cpu_port(ds, i))
+- continue;
+- if (dsa_to_port(ds, i)->bridge_dev != br)
+- continue;
+- /* Remove this port to the portvlan mask of the other ports
+- * in the bridge
+- */
+- regmap_clear_bits(priv->regmap,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
+- }
+-
+- /* Set the cpu port to be the only one in the portvlan mask of
+- * this port
+- */
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+-}
+-
+-static void
+-qca8k_port_fast_age(struct dsa_switch *ds, int port)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
+- mutex_unlock(&priv->reg_mutex);
+-}
+-
+-static int
+-qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- unsigned int secs = msecs / 1000;
+- u32 val;
+-
+- /* AGE_TIME reg is set in 7s step */
+- val = secs / 7;
+-
+- /* Handle case with 0 as val to NOT disable
+- * learning
+- */
+- if (!val)
+- val = 1;
+-
+- return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
+- QCA8K_ATU_AGE_TIME(val));
+-}
+-
+-static int
+-qca8k_port_enable(struct dsa_switch *ds, int port,
+- struct phy_device *phy)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+-
+- qca8k_port_set_status(priv, port, 1);
+- priv->port_enabled_map |= BIT(port);
+-
+- if (dsa_is_user_port(ds, port))
+- phy_support_asym_pause(phy);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_port_disable(struct dsa_switch *ds, int port)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+-
+- qca8k_port_set_status(priv, port, 0);
+- priv->port_enabled_map &= ~BIT(port);
+-}
+-
+-static int
+-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- /* We have only have a general MTU setting.
+- * DSA always set the CPU port's MTU to the largest MTU of the slave
+- * ports.
+- * Setting MTU just for the CPU port is sufficient to correctly set a
+- * value for every port.
+- */
+- if (!dsa_is_cpu_port(ds, port))
+- return 0;
+-
+- /* To change the MAX_FRAME_SIZE the cpu ports must be off or
+- * the switch panics.
+- * Turn off both cpu ports before applying the new value to prevent
+- * this.
+- */
+- if (priv->port_enabled_map & BIT(0))
+- qca8k_port_set_status(priv, 0, 0);
+-
+- if (priv->port_enabled_map & BIT(6))
+- qca8k_port_set_status(priv, 6, 0);
+-
+- /* Include L2 header / FCS length */
+- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN);
+-
+- if (priv->port_enabled_map & BIT(0))
+- qca8k_port_set_status(priv, 0, 1);
+-
+- if (priv->port_enabled_map & BIT(6))
+- qca8k_port_set_status(priv, 6, 1);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_port_max_mtu(struct dsa_switch *ds, int port)
+-{
+- return QCA8K_MAX_MTU;
+-}
+-
+-static int
+-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
+- u16 port_mask, u16 vid)
+-{
+- /* Set the vid to the port vlan id if no vid is set */
+- if (!vid)
+- vid = QCA8K_PORT_VID_DEF;
+-
+- return qca8k_fdb_add(priv, addr, port_mask, vid,
+- QCA8K_ATU_STATUS_STATIC);
+-}
+-
+-static int
+-qca8k_port_fdb_add(struct dsa_switch *ds, int port,
+- const unsigned char *addr, u16 vid)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u16 port_mask = BIT(port);
+-
+- return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
+-}
+-
+-static int
+-qca8k_port_fdb_del(struct dsa_switch *ds, int port,
+- const unsigned char *addr, u16 vid)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u16 port_mask = BIT(port);
+-
+- if (!vid)
+- vid = QCA8K_PORT_VID_DEF;
+-
+- return qca8k_fdb_del(priv, addr, port_mask, vid);
+-}
+-
+-static int
+-qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
+- dsa_fdb_dump_cb_t *cb, void *data)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- struct qca8k_fdb _fdb = { 0 };
+- int cnt = QCA8K_NUM_FDB_RECORDS;
+- bool is_static;
+- int ret = 0;
+-
+- mutex_lock(&priv->reg_mutex);
+- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
+- if (!_fdb.aging)
+- break;
+- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
+- ret = cb(_fdb.mac, _fdb.vid, is_static, data);
+- if (ret)
+- break;
+- }
+- mutex_unlock(&priv->reg_mutex);
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_port_mdb_add(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_mdb *mdb)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- const u8 *addr = mdb->addr;
+- u16 vid = mdb->vid;
+-
+- return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
+-}
+-
+-static int
+-qca8k_port_mdb_del(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_mdb *mdb)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- const u8 *addr = mdb->addr;
+- u16 vid = mdb->vid;
+-
+- return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
+-}
+-
+-static int
+-qca8k_port_mirror_add(struct dsa_switch *ds, int port,
+- struct dsa_mall_mirror_tc_entry *mirror,
+- bool ingress)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int monitor_port, ret;
+- u32 reg, val;
+-
+- /* Check for existent entry */
+- if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
+- return -EEXIST;
+-
+- ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
+- if (ret)
+- return ret;
+-
+- /* QCA83xx can have only one port set to mirror mode.
+- * Check that the correct port is requested and return error otherwise.
+- * When no mirror port is set, the values is set to 0xF
+- */
+- monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+- if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
+- return -EEXIST;
+-
+- /* Set the monitor port */
+- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
+- mirror->to_local_port);
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+- if (ret)
+- return ret;
+-
+- if (ingress) {
+- reg = QCA8K_PORT_LOOKUP_CTRL(port);
+- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+- } else {
+- reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+- }
+-
+- ret = regmap_update_bits(priv->regmap, reg, val, val);
+- if (ret)
+- return ret;
+-
+- /* Track mirror port for tx and rx to decide when the
+- * mirror port has to be disabled.
+- */
+- if (ingress)
+- priv->mirror_rx |= BIT(port);
+- else
+- priv->mirror_tx |= BIT(port);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_port_mirror_del(struct dsa_switch *ds, int port,
+- struct dsa_mall_mirror_tc_entry *mirror)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- u32 reg, val;
+- int ret;
+-
+- if (mirror->ingress) {
+- reg = QCA8K_PORT_LOOKUP_CTRL(port);
+- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+- } else {
+- reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+- }
+-
+- ret = regmap_clear_bits(priv->regmap, reg, val);
+- if (ret)
+- goto err;
+-
+- if (mirror->ingress)
+- priv->mirror_rx &= ~BIT(port);
+- else
+- priv->mirror_tx &= ~BIT(port);
+-
+- /* No port set to send packet to mirror port. Disable mirror port */
+- if (!priv->mirror_rx && !priv->mirror_tx) {
+- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+- if (ret)
+- goto err;
+- }
+-err:
+- dev_err(priv->dev, "Failed to del mirror port from %d", port);
+-}
+-
+-static int
+-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+- struct netlink_ext_ack *extack)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- if (vlan_filtering) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+- QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
+- } else {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+- QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
+- }
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_port_vlan_add(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_vlan *vlan,
+- struct netlink_ext_ack *extack)
+-{
+- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
+- if (ret) {
+- dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
+- return ret;
+- }
+-
+- if (pvid) {
+- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
+- QCA8K_EGREES_VLAN_PORT_MASK(port),
+- QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
+- if (ret)
+- return ret;
+-
+- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
+- QCA8K_PORT_VLAN_CVID(vlan->vid) |
+- QCA8K_PORT_VLAN_SVID(vlan->vid));
+- }
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_port_vlan_del(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_vlan *vlan)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- ret = qca8k_vlan_del(priv, port, vlan->vid);
+- if (ret)
+- dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
+-
+- return ret;
+-}
+-
+-static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- /* Communicate to the phy internal driver the switch revision.
+- * Based on the switch revision different values needs to be
+- * set to the dbg and mmd reg on the phy.
+- * The first 2 bit are used to communicate the switch revision
+- * to the phy driver.
+- */
+- if (port > 0 && port < 6)
+- return priv->switch_revision;
+-
+- return 0;
+-}
+-
+-static enum dsa_tag_protocol
+-qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
+- enum dsa_tag_protocol mp)
+-{
+- return DSA_TAG_PROTO_QCA;
+-}
+-
+-static bool
+-qca8k_lag_can_offload(struct dsa_switch *ds,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- struct dsa_port *dp;
+- int id, members = 0;
+-
+- id = dsa_lag_id(ds->dst, lag);
+- if (id < 0 || id >= ds->num_lag_ids)
+- return false;
+-
+- dsa_lag_foreach_port(dp, ds->dst, lag)
+- /* Includes the port joining the LAG */
+- members++;
+-
+- if (members > QCA8K_NUM_PORTS_FOR_LAG)
+- return false;
+-
+- if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+- return false;
+-
+- if (info->hash_type != NETDEV_LAG_HASH_L2 &&
+- info->hash_type != NETDEV_LAG_HASH_L23)
+- return false;
+-
+- return true;
+-}
+-
+-static int
+-qca8k_lag_setup_hash(struct dsa_switch *ds,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- bool unique_lag = true;
+- u32 hash = 0;
+- int i, id;
+-
+- id = dsa_lag_id(ds->dst, lag);
+-
+- switch (info->hash_type) {
+- case NETDEV_LAG_HASH_L23:
+- hash |= QCA8K_TRUNK_HASH_SIP_EN;
+- hash |= QCA8K_TRUNK_HASH_DIP_EN;
+- fallthrough;
+- case NETDEV_LAG_HASH_L2:
+- hash |= QCA8K_TRUNK_HASH_SA_EN;
+- hash |= QCA8K_TRUNK_HASH_DA_EN;
+- break;
+- default: /* We should NEVER reach this */
+- return -EOPNOTSUPP;
+- }
+-
+- /* Check if we are the unique configured LAG */
+- dsa_lags_foreach_id(i, ds->dst)
+- if (i != id && dsa_lag_dev(ds->dst, i)) {
+- unique_lag = false;
+- break;
+- }
+-
+- /* Hash Mode is global. Make sure the same Hash Mode
+- * is set to all the 4 possible lag.
+- * If we are the unique LAG we can set whatever hash
+- * mode we want.
+- * To change hash mode it's needed to remove all LAG
+- * and change the mode with the latest.
+- */
+- if (unique_lag) {
+- priv->lag_hash_mode = hash;
+- } else if (priv->lag_hash_mode != hash) {
+- netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n");
+- return -EOPNOTSUPP;
+- }
+-
+- return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
+- QCA8K_TRUNK_HASH_MASK, hash);
+-}
+-
+-static int
+-qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
+- struct net_device *lag, bool delete)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret, id, i;
+- u32 val;
+-
+- id = dsa_lag_id(ds->dst, lag);
+-
+- /* Read current port member */
+- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
+- if (ret)
+- return ret;
+-
+- /* Shift val to the correct trunk */
+- val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
+- val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
+- if (delete)
+- val &= ~BIT(port);
+- else
+- val |= BIT(port);
+-
+- /* Update port member. With empty portmap disable trunk */
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
+- QCA8K_REG_GOL_TRUNK_MEMBER(id) |
+- QCA8K_REG_GOL_TRUNK_EN(id),
+- !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
+- val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
+-
+- /* Search empty member if adding or port on deleting */
+- for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
+- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
+- if (ret)
+- return ret;
+-
+- val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
+- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
+-
+- if (delete) {
+- /* If port flagged to be disabled assume this member is
+- * empty
+- */
+- if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+- continue;
+-
+- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
+- if (val != port)
+- continue;
+- } else {
+- /* If port flagged to be enabled assume this member is
+- * already set
+- */
+- if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+- continue;
+- }
+-
+- /* We have found the member to add/remove */
+- break;
+- }
+-
+- /* Set port in the correct port mask or disable port if in delete mode */
+- return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
+- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
+- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
+- !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
+- port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
+-}
+-
+-static int
+-qca8k_port_lag_join(struct dsa_switch *ds, int port,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- int ret;
+-
+- if (!qca8k_lag_can_offload(ds, lag, info))
+- return -EOPNOTSUPP;
+-
+- ret = qca8k_lag_setup_hash(ds, lag, info);
+- if (ret)
+- return ret;
+-
+- return qca8k_lag_refresh_portmap(ds, port, lag, false);
+-}
+-
+-static int
+-qca8k_port_lag_leave(struct dsa_switch *ds, int port,
+- struct net_device *lag)
+-{
+- return qca8k_lag_refresh_portmap(ds, port, lag, true);
+-}
+-
+-static void
+-qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
+- bool operational)
+-{
+- struct dsa_port *dp = master->dsa_ptr;
+- struct qca8k_priv *priv = ds->priv;
+-
+- /* Ethernet MIB/MDIO is only supported for CPU port 0 */
+- if (dp->index != 0)
+- return;
+-
+- mutex_lock(&priv->mgmt_eth_data.mutex);
+- mutex_lock(&priv->mib_eth_data.mutex);
+-
+- priv->mgmt_master = operational ? (struct net_device *)master : NULL;
+-
+- mutex_unlock(&priv->mib_eth_data.mutex);
+- mutex_unlock(&priv->mgmt_eth_data.mutex);
+-}
+-
+-static int qca8k_connect_tag_protocol(struct dsa_switch *ds,
+- enum dsa_tag_protocol proto)
+-{
+- struct qca_tagger_data *tagger_data;
+-
+- switch (proto) {
+- case DSA_TAG_PROTO_QCA:
+- tagger_data = ds->tagger_data;
+-
+- tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler;
+- tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler;
+-
+- break;
+- default:
+- return -EOPNOTSUPP;
+- }
+-
+- return 0;
+-}
+-
+-static const struct dsa_switch_ops qca8k_switch_ops = {
+- .get_tag_protocol = qca8k_get_tag_protocol,
+- .setup = qca8k_setup,
+- .get_strings = qca8k_get_strings,
+- .get_ethtool_stats = qca8k_get_ethtool_stats,
+- .get_sset_count = qca8k_get_sset_count,
+- .set_ageing_time = qca8k_set_ageing_time,
+- .get_mac_eee = qca8k_get_mac_eee,
+- .set_mac_eee = qca8k_set_mac_eee,
+- .port_enable = qca8k_port_enable,
+- .port_disable = qca8k_port_disable,
+- .port_change_mtu = qca8k_port_change_mtu,
+- .port_max_mtu = qca8k_port_max_mtu,
+- .port_stp_state_set = qca8k_port_stp_state_set,
+- .port_bridge_join = qca8k_port_bridge_join,
+- .port_bridge_leave = qca8k_port_bridge_leave,
+- .port_fast_age = qca8k_port_fast_age,
+- .port_fdb_add = qca8k_port_fdb_add,
+- .port_fdb_del = qca8k_port_fdb_del,
+- .port_fdb_dump = qca8k_port_fdb_dump,
+- .port_mdb_add = qca8k_port_mdb_add,
+- .port_mdb_del = qca8k_port_mdb_del,
+- .port_mirror_add = qca8k_port_mirror_add,
+- .port_mirror_del = qca8k_port_mirror_del,
+- .port_vlan_filtering = qca8k_port_vlan_filtering,
+- .port_vlan_add = qca8k_port_vlan_add,
+- .port_vlan_del = qca8k_port_vlan_del,
+- .phylink_validate = qca8k_phylink_validate,
+- .phylink_mac_link_state = qca8k_phylink_mac_link_state,
+- .phylink_mac_config = qca8k_phylink_mac_config,
+- .phylink_mac_link_down = qca8k_phylink_mac_link_down,
+- .phylink_mac_link_up = qca8k_phylink_mac_link_up,
+- .get_phy_flags = qca8k_get_phy_flags,
+- .port_lag_join = qca8k_port_lag_join,
+- .port_lag_leave = qca8k_port_lag_leave,
+- .master_state_change = qca8k_master_change,
+- .connect_tag_protocol = qca8k_connect_tag_protocol,
+-};
+-
+-static int qca8k_read_switch_id(struct qca8k_priv *priv)
+-{
+- const struct qca8k_match_data *data;
+- u32 val;
+- u8 id;
+- int ret;
+-
+- /* get the switches ID from the compatible */
+- data = of_device_get_match_data(priv->dev);
+- if (!data)
+- return -ENODEV;
+-
+- ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
+- if (ret < 0)
+- return -ENODEV;
+-
+- id = QCA8K_MASK_CTRL_DEVICE_ID(val);
+- if (id != data->id) {
+- dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
+- return -ENODEV;
+- }
+-
+- priv->switch_id = id;
+-
+- /* Save revision to communicate to the internal PHY driver */
+- priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_sw_probe(struct mdio_device *mdiodev)
+-{
+- struct qca8k_priv *priv;
+- int ret;
+-
+- /* allocate the private data struct so that we can probe the switches
+- * ID register
+- */
+- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- priv->bus = mdiodev->bus;
+- priv->dev = &mdiodev->dev;
+-
+- priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset",
+- GPIOD_ASIS);
+- if (IS_ERR(priv->reset_gpio))
+- return PTR_ERR(priv->reset_gpio);
+-
+- if (priv->reset_gpio) {
+- gpiod_set_value_cansleep(priv->reset_gpio, 1);
+- /* The active low duration must be greater than 10 ms
+- * and checkpatch.pl wants 20 ms.
+- */
+- msleep(20);
+- gpiod_set_value_cansleep(priv->reset_gpio, 0);
+- }
+-
+- /* Start by setting up the register mapping */
+- priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv,
+- &qca8k_regmap_config);
+- if (IS_ERR(priv->regmap)) {
+- dev_err(priv->dev, "regmap initialization failed");
+- return PTR_ERR(priv->regmap);
+- }
+-
+- priv->mdio_cache.page = 0xffff;
+- priv->mdio_cache.lo = 0xffff;
+- priv->mdio_cache.hi = 0xffff;
+-
+- /* Check the detected switch id */
+- ret = qca8k_read_switch_id(priv);
+- if (ret)
+- return ret;
+-
+- priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+- if (!priv->ds)
+- return -ENOMEM;
+-
+- mutex_init(&priv->mgmt_eth_data.mutex);
+- init_completion(&priv->mgmt_eth_data.rw_done);
+-
+- mutex_init(&priv->mib_eth_data.mutex);
+- init_completion(&priv->mib_eth_data.rw_done);
+-
+- priv->ds->dev = &mdiodev->dev;
+- priv->ds->num_ports = QCA8K_NUM_PORTS;
+- priv->ds->priv = priv;
+- priv->ds->ops = &qca8k_switch_ops;
+- mutex_init(&priv->reg_mutex);
+- dev_set_drvdata(&mdiodev->dev, priv);
+-
+- return dsa_register_switch(priv->ds);
+-}
+-
+-static void
+-qca8k_sw_remove(struct mdio_device *mdiodev)
+-{
+- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
+- int i;
+-
+- if (!priv)
+- return;
+-
+- for (i = 0; i < QCA8K_NUM_PORTS; i++)
+- qca8k_port_set_status(priv, i, 0);
+-
+- dsa_unregister_switch(priv->ds);
+-
+- dev_set_drvdata(&mdiodev->dev, NULL);
+-}
+-
+-static void qca8k_sw_shutdown(struct mdio_device *mdiodev)
+-{
+- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
+-
+- if (!priv)
+- return;
+-
+- dsa_switch_shutdown(priv->ds);
+-
+- dev_set_drvdata(&mdiodev->dev, NULL);
+-}
+-
+-#ifdef CONFIG_PM_SLEEP
+-static void
+-qca8k_set_pm(struct qca8k_priv *priv, int enable)
+-{
+- int port;
+-
+- for (port = 0; port < QCA8K_NUM_PORTS; port++) {
+- /* Do not enable on resume if the port was
+- * disabled before.
+- */
+- if (!(priv->port_enabled_map & BIT(port)))
+- continue;
+-
+- qca8k_port_set_status(priv, port, enable);
+- }
+-}
+-
+-static int qca8k_suspend(struct device *dev)
+-{
+- struct qca8k_priv *priv = dev_get_drvdata(dev);
+-
+- qca8k_set_pm(priv, 0);
+-
+- return dsa_switch_suspend(priv->ds);
+-}
+-
+-static int qca8k_resume(struct device *dev)
+-{
+- struct qca8k_priv *priv = dev_get_drvdata(dev);
+-
+- qca8k_set_pm(priv, 1);
+-
+- return dsa_switch_resume(priv->ds);
+-}
+-#endif /* CONFIG_PM_SLEEP */
+-
+-static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+- qca8k_suspend, qca8k_resume);
+-
+-static const struct qca8k_match_data qca8327 = {
+- .id = QCA8K_ID_QCA8327,
+- .reduced_package = true,
+- .mib_count = QCA8K_QCA832X_MIB_COUNT,
+-};
+-
+-static const struct qca8k_match_data qca8328 = {
+- .id = QCA8K_ID_QCA8327,
+- .mib_count = QCA8K_QCA832X_MIB_COUNT,
+-};
+-
+-static const struct qca8k_match_data qca833x = {
+- .id = QCA8K_ID_QCA8337,
+- .mib_count = QCA8K_QCA833X_MIB_COUNT,
+-};
+-
+-static const struct of_device_id qca8k_of_match[] = {
+- { .compatible = "qca,qca8327", .data = &qca8327 },
+- { .compatible = "qca,qca8328", .data = &qca8328 },
+- { .compatible = "qca,qca8334", .data = &qca833x },
+- { .compatible = "qca,qca8337", .data = &qca833x },
+- { /* sentinel */ },
+-};
+-
+-static struct mdio_driver qca8kmdio_driver = {
+- .probe = qca8k_sw_probe,
+- .remove = qca8k_sw_remove,
+- .shutdown = qca8k_sw_shutdown,
+- .mdiodrv.driver = {
+- .name = "qca8k",
+- .of_match_table = qca8k_of_match,
+- .pm = &qca8k_pm_ops,
+- },
+-};
+-
+-mdio_module_driver(qca8kmdio_driver);
+-
+-MODULE_AUTHOR("Mathieu Olivari, John Crispin <john@phrozen.org>");
+-MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family");
+-MODULE_LICENSE("GPL v2");
+-MODULE_ALIAS("platform:qca8k");
+--- a/drivers/net/dsa/qca8k.h
++++ /dev/null
+@@ -1,411 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-only */
+-/*
+- * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+- * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+- */
+-
+-#ifndef __QCA8K_H
+-#define __QCA8K_H
+-
+-#include <linux/delay.h>
+-#include <linux/regmap.h>
+-#include <linux/gpio.h>
+-#include <linux/dsa/tag_qca.h>
+-
+-#define QCA8K_ETHERNET_MDIO_PRIORITY 7
+-#define QCA8K_ETHERNET_PHY_PRIORITY 6
+-#define QCA8K_ETHERNET_TIMEOUT 100
+-
+-#define QCA8K_NUM_PORTS 7
+-#define QCA8K_NUM_CPU_PORTS 2
+-#define QCA8K_MAX_MTU 9000
+-#define QCA8K_NUM_LAGS 4
+-#define QCA8K_NUM_PORTS_FOR_LAG 4
+-
+-#define PHY_ID_QCA8327 0x004dd034
+-#define QCA8K_ID_QCA8327 0x12
+-#define PHY_ID_QCA8337 0x004dd036
+-#define QCA8K_ID_QCA8337 0x13
+-
+-#define QCA8K_QCA832X_MIB_COUNT 39
+-#define QCA8K_QCA833X_MIB_COUNT 41
+-
+-#define QCA8K_BUSY_WAIT_TIMEOUT 2000
+-
+-#define QCA8K_NUM_FDB_RECORDS 2048
+-
+-#define QCA8K_PORT_VID_DEF 1
+-
+-/* Global control registers */
+-#define QCA8K_REG_MASK_CTRL 0x000
+-#define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0)
+-#define QCA8K_MASK_CTRL_REV_ID(x) FIELD_GET(QCA8K_MASK_CTRL_REV_ID_MASK, x)
+-#define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8)
+-#define QCA8K_MASK_CTRL_DEVICE_ID(x) FIELD_GET(QCA8K_MASK_CTRL_DEVICE_ID_MASK, x)
+-#define QCA8K_REG_PORT0_PAD_CTRL 0x004
+-#define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31)
+-#define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19)
+-#define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18)
+-#define QCA8K_REG_PORT5_PAD_CTRL 0x008
+-#define QCA8K_REG_PORT6_PAD_CTRL 0x00c
+-#define QCA8K_PORT_PAD_RGMII_EN BIT(26)
+-#define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22)
+-#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, x)
+-#define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20)
+-#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, x)
+-#define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
+-#define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
+-#define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+-#define QCA8K_REG_PWS 0x010
+-#define QCA8K_PWS_POWER_ON_SEL BIT(31)
+-/* This reg is only valid for QCA832x and toggle the package
+- * type from 176 pin (by default) to 148 pin used on QCA8327
+- */
+-#define QCA8327_PWS_PACKAGE148_EN BIT(30)
+-#define QCA8K_PWS_LED_OPEN_EN_CSR BIT(24)
+-#define QCA8K_PWS_SERDES_AEN_DIS BIT(7)
+-#define QCA8K_REG_MODULE_EN 0x030
+-#define QCA8K_MODULE_EN_MIB BIT(0)
+-#define QCA8K_REG_MIB 0x034
+-#define QCA8K_MIB_FUNC GENMASK(26, 24)
+-#define QCA8K_MIB_CPU_KEEP BIT(20)
+-#define QCA8K_MIB_BUSY BIT(17)
+-#define QCA8K_MDIO_MASTER_CTRL 0x3c
+-#define QCA8K_MDIO_MASTER_BUSY BIT(31)
+-#define QCA8K_MDIO_MASTER_EN BIT(30)
+-#define QCA8K_MDIO_MASTER_READ BIT(27)
+-#define QCA8K_MDIO_MASTER_WRITE 0
+-#define QCA8K_MDIO_MASTER_SUP_PRE BIT(26)
+-#define QCA8K_MDIO_MASTER_PHY_ADDR_MASK GENMASK(25, 21)
+-#define QCA8K_MDIO_MASTER_PHY_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_PHY_ADDR_MASK, x)
+-#define QCA8K_MDIO_MASTER_REG_ADDR_MASK GENMASK(20, 16)
+-#define QCA8K_MDIO_MASTER_REG_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_REG_ADDR_MASK, x)
+-#define QCA8K_MDIO_MASTER_DATA_MASK GENMASK(15, 0)
+-#define QCA8K_MDIO_MASTER_DATA(x) FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)
+-#define QCA8K_MDIO_MASTER_MAX_PORTS 5
+-#define QCA8K_MDIO_MASTER_MAX_REG 32
+-#define QCA8K_GOL_MAC_ADDR0 0x60
+-#define QCA8K_GOL_MAC_ADDR1 0x64
+-#define QCA8K_MAX_FRAME_SIZE 0x78
+-#define QCA8K_REG_PORT_STATUS(_i) (0x07c + (_i) * 4)
+-#define QCA8K_PORT_STATUS_SPEED GENMASK(1, 0)
+-#define QCA8K_PORT_STATUS_SPEED_10 0
+-#define QCA8K_PORT_STATUS_SPEED_100 0x1
+-#define QCA8K_PORT_STATUS_SPEED_1000 0x2
+-#define QCA8K_PORT_STATUS_TXMAC BIT(2)
+-#define QCA8K_PORT_STATUS_RXMAC BIT(3)
+-#define QCA8K_PORT_STATUS_TXFLOW BIT(4)
+-#define QCA8K_PORT_STATUS_RXFLOW BIT(5)
+-#define QCA8K_PORT_STATUS_DUPLEX BIT(6)
+-#define QCA8K_PORT_STATUS_LINK_UP BIT(8)
+-#define QCA8K_PORT_STATUS_LINK_AUTO BIT(9)
+-#define QCA8K_PORT_STATUS_LINK_PAUSE BIT(10)
+-#define QCA8K_PORT_STATUS_FLOW_AUTO BIT(12)
+-#define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4))
+-#define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2)
+-#define QCA8K_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0)
+-#define QCA8K_PORT_HDR_CTRL_ALL 2
+-#define QCA8K_PORT_HDR_CTRL_MGMT 1
+-#define QCA8K_PORT_HDR_CTRL_NONE 0
+-#define QCA8K_REG_SGMII_CTRL 0x0e0
+-#define QCA8K_SGMII_EN_PLL BIT(1)
+-#define QCA8K_SGMII_EN_RX BIT(2)
+-#define QCA8K_SGMII_EN_TX BIT(3)
+-#define QCA8K_SGMII_EN_SD BIT(4)
+-#define QCA8K_SGMII_CLK125M_DELAY BIT(7)
+-#define QCA8K_SGMII_MODE_CTRL_MASK GENMASK(23, 22)
+-#define QCA8K_SGMII_MODE_CTRL(x) FIELD_PREP(QCA8K_SGMII_MODE_CTRL_MASK, x)
+-#define QCA8K_SGMII_MODE_CTRL_BASEX QCA8K_SGMII_MODE_CTRL(0x0)
+-#define QCA8K_SGMII_MODE_CTRL_PHY QCA8K_SGMII_MODE_CTRL(0x1)
+-#define QCA8K_SGMII_MODE_CTRL_MAC QCA8K_SGMII_MODE_CTRL(0x2)
+-
+-/* MAC_PWR_SEL registers */
+-#define QCA8K_REG_MAC_PWR_SEL 0x0e4
+-#define QCA8K_MAC_PWR_RGMII1_1_8V BIT(18)
+-#define QCA8K_MAC_PWR_RGMII0_1_8V BIT(19)
+-
+-/* EEE control registers */
+-#define QCA8K_REG_EEE_CTRL 0x100
+-#define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2)
+-
+-/* TRUNK_HASH_EN registers */
+-#define QCA8K_TRUNK_HASH_EN_CTRL 0x270
+-#define QCA8K_TRUNK_HASH_SIP_EN BIT(3)
+-#define QCA8K_TRUNK_HASH_DIP_EN BIT(2)
+-#define QCA8K_TRUNK_HASH_SA_EN BIT(1)
+-#define QCA8K_TRUNK_HASH_DA_EN BIT(0)
+-#define QCA8K_TRUNK_HASH_MASK GENMASK(3, 0)
+-
+-/* ACL registers */
+-#define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8))
+-#define QCA8K_PORT_VLAN_CVID_MASK GENMASK(27, 16)
+-#define QCA8K_PORT_VLAN_CVID(x) FIELD_PREP(QCA8K_PORT_VLAN_CVID_MASK, x)
+-#define QCA8K_PORT_VLAN_SVID_MASK GENMASK(11, 0)
+-#define QCA8K_PORT_VLAN_SVID(x) FIELD_PREP(QCA8K_PORT_VLAN_SVID_MASK, x)
+-#define QCA8K_REG_PORT_VLAN_CTRL1(_i) (0x424 + (_i * 8))
+-#define QCA8K_REG_IPV4_PRI_BASE_ADDR 0x470
+-#define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474
+-
+-/* Lookup registers */
+-#define QCA8K_REG_ATU_DATA0 0x600
+-#define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24)
+-#define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16)
+-#define QCA8K_ATU_ADDR4_MASK GENMASK(15, 8)
+-#define QCA8K_ATU_ADDR5_MASK GENMASK(7, 0)
+-#define QCA8K_REG_ATU_DATA1 0x604
+-#define QCA8K_ATU_PORT_MASK GENMASK(22, 16)
+-#define QCA8K_ATU_ADDR0_MASK GENMASK(15, 8)
+-#define QCA8K_ATU_ADDR1_MASK GENMASK(7, 0)
+-#define QCA8K_REG_ATU_DATA2 0x608
+-#define QCA8K_ATU_VID_MASK GENMASK(19, 8)
+-#define QCA8K_ATU_STATUS_MASK GENMASK(3, 0)
+-#define QCA8K_ATU_STATUS_STATIC 0xf
+-#define QCA8K_REG_ATU_FUNC 0x60c
+-#define QCA8K_ATU_FUNC_BUSY BIT(31)
+-#define QCA8K_ATU_FUNC_PORT_EN BIT(14)
+-#define QCA8K_ATU_FUNC_MULTI_EN BIT(13)
+-#define QCA8K_ATU_FUNC_FULL BIT(12)
+-#define QCA8K_ATU_FUNC_PORT_MASK GENMASK(11, 8)
+-#define QCA8K_REG_VTU_FUNC0 0x610
+-#define QCA8K_VTU_FUNC0_VALID BIT(20)
+-#define QCA8K_VTU_FUNC0_IVL_EN BIT(19)
+-/* QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(17, 4)
+- * It does contain VLAN_MODE for each port [5:4] for port0,
+- * [7:6] for port1 ... [17:16] for port6. Use virtual port
+- * define to handle this.
+- */
+-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i) (4 + (_i) * 2)
+-#define QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(1, 0)
+-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(_i) (GENMASK(1, 0) << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+-#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x0)
+-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNMOD(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNMOD << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+-#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x1)
+-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNTAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+-#define QCA8K_VTU_FUNC0_EG_MODE_TAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x2)
+-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_TAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+-#define QCA8K_VTU_FUNC0_EG_MODE_NOT FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x3)
+-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(_i) (QCA8K_VTU_FUNC0_EG_MODE_NOT << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))
+-#define QCA8K_REG_VTU_FUNC1 0x614
+-#define QCA8K_VTU_FUNC1_BUSY BIT(31)
+-#define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16)
+-#define QCA8K_VTU_FUNC1_FULL BIT(4)
+-#define QCA8K_REG_ATU_CTRL 0x618
+-#define QCA8K_ATU_AGE_TIME_MASK GENMASK(15, 0)
+-#define QCA8K_ATU_AGE_TIME(x) FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x))
+-#define QCA8K_REG_GLOBAL_FW_CTRL0 0x620
+-#define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10)
+-#define QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM GENMASK(7, 4)
+-#define QCA8K_REG_GLOBAL_FW_CTRL1 0x624
+-#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24)
+-#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16)
+-#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK GENMASK(14, 8)
+-#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK GENMASK(6, 0)
+-#define QCA8K_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc)
+-#define QCA8K_PORT_LOOKUP_MEMBER GENMASK(6, 0)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_MASK GENMASK(9, 8)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE QCA8K_PORT_LOOKUP_VLAN_MODE(0x0)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK QCA8K_PORT_LOOKUP_VLAN_MODE(0x1)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK QCA8K_PORT_LOOKUP_VLAN_MODE(0x2)
+-#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE QCA8K_PORT_LOOKUP_VLAN_MODE(0x3)
+-#define QCA8K_PORT_LOOKUP_STATE_MASK GENMASK(18, 16)
+-#define QCA8K_PORT_LOOKUP_STATE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_STATE_MASK, x)
+-#define QCA8K_PORT_LOOKUP_STATE_DISABLED QCA8K_PORT_LOOKUP_STATE(0x0)
+-#define QCA8K_PORT_LOOKUP_STATE_BLOCKING QCA8K_PORT_LOOKUP_STATE(0x1)
+-#define QCA8K_PORT_LOOKUP_STATE_LISTENING QCA8K_PORT_LOOKUP_STATE(0x2)
+-#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
+-#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
+-#define QCA8K_PORT_LOOKUP_LEARN BIT(20)
+-#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
+-
+-#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700
+-/* 4 max trunk first
+- * first 6 bit for member bitmap
+- * 7th bit is to enable trunk port
+- */
+-#define QCA8K_REG_GOL_TRUNK_SHIFT(_i) ((_i) * 8)
+-#define QCA8K_REG_GOL_TRUNK_EN_MASK BIT(7)
+-#define QCA8K_REG_GOL_TRUNK_EN(_i) (QCA8K_REG_GOL_TRUNK_EN_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
+-#define QCA8K_REG_GOL_TRUNK_MEMBER_MASK GENMASK(6, 0)
+-#define QCA8K_REG_GOL_TRUNK_MEMBER(_i) (QCA8K_REG_GOL_TRUNK_MEMBER_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i))
+-/* 0x704 for TRUNK 0-1 --- 0x708 for TRUNK 2-3 */
+-#define QCA8K_REG_GOL_TRUNK_CTRL(_i) (0x704 + (((_i) / 2) * 4))
+-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK GENMASK(3, 0)
+-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK BIT(3)
+-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK GENMASK(2, 0)
+-#define QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i) (((_i) / 2) * 16)
+-#define QCA8K_REG_GOL_MEM_ID_SHIFT(_i) ((_i) * 4)
+-/* Complex shift: FIRST shift for port THEN shift for trunk */
+-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j) (QCA8K_REG_GOL_MEM_ID_SHIFT(_j) + QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i))
+-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
+-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))
+-
+-#define QCA8K_REG_GLOBAL_FC_THRESH 0x800
+-#define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16)
+-#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x)
+-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK GENMASK(8, 0)
+-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, x)
+-
+-#define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK GENMASK(3, 0)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK, x)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK GENMASK(7, 4)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK, x)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK GENMASK(11, 8)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK, x)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK GENMASK(15, 12)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK, x)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK GENMASK(19, 16)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK, x)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK GENMASK(23, 20)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK, x)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK GENMASK(29, 24)
+-#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK, x)
+-
+-#define QCA8K_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8)
+-#define QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK GENMASK(3, 0)
+-#define QCA8K_PORT_HOL_CTRL1_ING(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK, x)
+-#define QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN BIT(6)
+-#define QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN BIT(7)
+-#define QCA8K_PORT_HOL_CTRL1_WRED_EN BIT(8)
+-#define QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16)
+-
+-/* Pkt edit registers */
+-#define QCA8K_EGREES_VLAN_PORT_SHIFT(_i) (16 * ((_i) % 2))
+-#define QCA8K_EGREES_VLAN_PORT_MASK(_i) (GENMASK(11, 0) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
+-#define QCA8K_EGREES_VLAN_PORT(_i, x) ((x) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))
+-#define QCA8K_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2)))
+-
+-/* L3 registers */
+-#define QCA8K_HROUTER_CONTROL 0xe00
+-#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_M GENMASK(17, 16)
+-#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_S 16
+-#define QCA8K_HROUTER_CONTROL_ARP_AGE_MODE 1
+-#define QCA8K_HROUTER_PBASED_CONTROL1 0xe08
+-#define QCA8K_HROUTER_PBASED_CONTROL2 0xe0c
+-#define QCA8K_HNAT_CONTROL 0xe38
+-
+-/* MIB registers */
+-#define QCA8K_PORT_MIB_COUNTER(_i) (0x1000 + (_i) * 0x100)
+-
+-/* QCA specific MII registers */
+-#define MII_ATH_MMD_ADDR 0x0d
+-#define MII_ATH_MMD_DATA 0x0e
+-
+-enum {
+- QCA8K_PORT_SPEED_10M = 0,
+- QCA8K_PORT_SPEED_100M = 1,
+- QCA8K_PORT_SPEED_1000M = 2,
+- QCA8K_PORT_SPEED_ERR = 3,
+-};
+-
+-enum qca8k_fdb_cmd {
+- QCA8K_FDB_FLUSH = 1,
+- QCA8K_FDB_LOAD = 2,
+- QCA8K_FDB_PURGE = 3,
+- QCA8K_FDB_FLUSH_PORT = 5,
+- QCA8K_FDB_NEXT = 6,
+- QCA8K_FDB_SEARCH = 7,
+-};
+-
+-enum qca8k_vlan_cmd {
+- QCA8K_VLAN_FLUSH = 1,
+- QCA8K_VLAN_LOAD = 2,
+- QCA8K_VLAN_PURGE = 3,
+- QCA8K_VLAN_REMOVE_PORT = 4,
+- QCA8K_VLAN_NEXT = 5,
+- QCA8K_VLAN_READ = 6,
+-};
+-
+-enum qca8k_mid_cmd {
+- QCA8K_MIB_FLUSH = 1,
+- QCA8K_MIB_FLUSH_PORT = 2,
+- QCA8K_MIB_CAST = 3,
+-};
+-
+-struct qca8k_match_data {
+- u8 id;
+- bool reduced_package;
+- u8 mib_count;
+-};
+-
+-enum {
+- QCA8K_CPU_PORT0,
+- QCA8K_CPU_PORT6,
+-};
+-
+-struct qca8k_mgmt_eth_data {
+- struct completion rw_done;
+- struct mutex mutex; /* Enforce one mdio read/write at time */
+- bool ack;
+- u32 seq;
+- u32 data[4];
+-};
+-
+-struct qca8k_mib_eth_data {
+- struct completion rw_done;
+- struct mutex mutex; /* Process one command at time */
+- refcount_t port_parsed; /* Counter to track parsed port */
+- u8 req_port;
+- u64 *data; /* pointer to ethtool data */
+-};
+-
+-struct qca8k_ports_config {
+- bool sgmii_rx_clk_falling_edge;
+- bool sgmii_tx_clk_falling_edge;
+- bool sgmii_enable_pll;
+- u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+- u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */
+-};
+-
+-struct qca8k_mdio_cache {
+-/* The 32bit switch registers are accessed indirectly. To achieve this we need
+- * to set the page of the register. Track the last page that was set to reduce
+- * mdio writes
+- */
+- u16 page;
+-/* lo and hi can also be cached and from Documentation we can skip one
+- * extra mdio write if lo or hi is didn't change.
+- */
+- u16 lo;
+- u16 hi;
+-};
+-
+-struct qca8k_priv {
+- u8 switch_id;
+- u8 switch_revision;
+- u8 mirror_rx;
+- u8 mirror_tx;
+- u8 lag_hash_mode;
+- /* Each bit correspond to a port. This switch can support a max of 7 port.
+- * Bit 1: port enabled. Bit 0: port disabled.
+- */
+- u8 port_enabled_map;
+- struct qca8k_ports_config ports_config;
+- struct regmap *regmap;
+- struct mii_bus *bus;
+- struct dsa_switch *ds;
+- struct mutex reg_mutex;
+- struct device *dev;
+- struct gpio_desc *reset_gpio;
+- struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
+- struct qca8k_mgmt_eth_data mgmt_eth_data;
+- struct qca8k_mib_eth_data mib_eth_data;
+- struct qca8k_mdio_cache mdio_cache;
+-};
+-
+-struct qca8k_mib_desc {
+- unsigned int size;
+- unsigned int offset;
+- const char *name;
+-};
+-
+-struct qca8k_fdb {
+- u16 vid;
+- u8 port_mask;
+- u8 aging;
+- u8 mac[6];
+-};
+-
+-#endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch b/target/linux/generic/backport-6.1/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch
new file mode 100644
index 0000000000..77fe64632f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch
@@ -0,0 +1,157 @@
+From 3bb0844e7bcd0fb0bcfab6202b5edd349ef5250a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:10 +0200
+Subject: [PATCH 01/14] net: dsa: qca8k: cache match data to speed up access
+
+Using of_device_get_match_data is expensive. Cache match data to speed
+up access and rework user of match data to use the new cached value.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k.c | 35 +++++++++++------------------------
+ drivers/net/dsa/qca/qca8k.h | 1 +
+ 2 files changed, 12 insertions(+), 24 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k.c
++++ b/drivers/net/dsa/qca/qca8k.c
+@@ -1462,8 +1462,8 @@ static int qca8k_find_cpu_port(struct ds
+ static int
+ qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
+ {
++ const struct qca8k_match_data *data = priv->info;
+ struct device_node *node = priv->dev->of_node;
+- const struct qca8k_match_data *data;
+ u32 val = 0;
+ int ret;
+
+@@ -1472,8 +1472,6 @@ qca8k_setup_of_pws_reg(struct qca8k_priv
+ * Should be applied by default but we set this just to make sure.
+ */
+ if (priv->switch_id == QCA8K_ID_QCA8327) {
+- data = of_device_get_match_data(priv->dev);
+-
+ /* Set the correct package of 148 pin for QCA8327 */
+ if (data->reduced_package)
+ val |= QCA8327_PWS_PACKAGE148_EN;
+@@ -2146,23 +2144,19 @@ qca8k_phylink_mac_link_up(struct dsa_swi
+ static void
+ qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
+ {
+- const struct qca8k_match_data *match_data;
+ struct qca8k_priv *priv = ds->priv;
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+- match_data = of_device_get_match_data(priv->dev);
+-
+- for (i = 0; i < match_data->mib_count; i++)
++ for (i = 0; i < priv->info->mib_count; i++)
+ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
+ ETH_GSTRING_LEN);
+ }
+
+ static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb)
+ {
+- const struct qca8k_match_data *match_data;
+ struct qca8k_mib_eth_data *mib_eth_data;
+ struct qca8k_priv *priv = ds->priv;
+ const struct qca8k_mib_desc *mib;
+@@ -2181,10 +2175,9 @@ static void qca8k_mib_autocast_handler(s
+ if (port != mib_eth_data->req_port)
+ goto exit;
+
+- match_data = device_get_match_data(priv->dev);
+ data = mib_eth_data->data;
+
+- for (i = 0; i < match_data->mib_count; i++) {
++ for (i = 0; i < priv->info->mib_count; i++) {
+ mib = &ar8327_mib[i];
+
+ /* First 3 mib are present in the skb head */
+@@ -2256,7 +2249,6 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ uint64_t *data)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- const struct qca8k_match_data *match_data;
+ const struct qca8k_mib_desc *mib;
+ u32 reg, i, val;
+ u32 hi = 0;
+@@ -2266,9 +2258,7 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ qca8k_get_ethtool_stats_eth(ds, port, data) > 0)
+ return;
+
+- match_data = of_device_get_match_data(priv->dev);
+-
+- for (i = 0; i < match_data->mib_count; i++) {
++ for (i = 0; i < priv->info->mib_count; i++) {
+ mib = &ar8327_mib[i];
+ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+
+@@ -2291,15 +2281,12 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ static int
+ qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
+ {
+- const struct qca8k_match_data *match_data;
+ struct qca8k_priv *priv = ds->priv;
+
+ if (sset != ETH_SS_STATS)
+ return 0;
+
+- match_data = of_device_get_match_data(priv->dev);
+-
+- return match_data->mib_count;
++ return priv->info->mib_count;
+ }
+
+ static int
+@@ -3037,14 +3024,11 @@ static const struct dsa_switch_ops qca8k
+
+ static int qca8k_read_switch_id(struct qca8k_priv *priv)
+ {
+- const struct qca8k_match_data *data;
+ u32 val;
+ u8 id;
+ int ret;
+
+- /* get the switches ID from the compatible */
+- data = of_device_get_match_data(priv->dev);
+- if (!data)
++ if (!priv->info)
+ return -ENODEV;
+
+ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
+@@ -3052,8 +3036,10 @@ static int qca8k_read_switch_id(struct q
+ return -ENODEV;
+
+ id = QCA8K_MASK_CTRL_DEVICE_ID(val);
+- if (id != data->id) {
+- dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);
++ if (id != priv->info->id) {
++ dev_err(priv->dev,
++ "Switch id detected %x but expected %x",
++ id, priv->info->id);
+ return -ENODEV;
+ }
+
+@@ -3078,6 +3064,7 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ if (!priv)
+ return -ENOMEM;
+
++ priv->info = of_device_get_match_data(priv->dev);
+ priv->bus = mdiodev->bus;
+ priv->dev = &mdiodev->dev;
+
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -393,6 +393,7 @@ struct qca8k_priv {
+ struct qca8k_mgmt_eth_data mgmt_eth_data;
+ struct qca8k_mib_eth_data mib_eth_data;
+ struct qca8k_mdio_cache mdio_cache;
++ const struct qca8k_match_data *info;
+ };
+
+ struct qca8k_mib_desc {
diff --git a/target/linux/generic/backport-6.1/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch b/target/linux/generic/backport-6.1/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch
new file mode 100644
index 0000000000..5b2dce4c55
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch
@@ -0,0 +1,77 @@
+From 533c64bca62a8654f00698bc893f639013e38c7b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:11 +0200
+Subject: [PATCH 02/14] net: dsa: qca8k: make mib autocast feature optional
+
+Some switch may not support mib autocast feature and require the legacy
+way of reading the regs directly.
+Make the mib autocast feature optional and permit to declare support for
+it using match_data struct in a dedicated qca8k_info_ops struct.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k.c | 11 +++++++++--
+ drivers/net/dsa/qca/qca8k.h | 5 +++++
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k.c
++++ b/drivers/net/dsa/qca/qca8k.c
+@@ -2254,8 +2254,8 @@ qca8k_get_ethtool_stats(struct dsa_switc
+ u32 hi = 0;
+ int ret;
+
+- if (priv->mgmt_master &&
+- qca8k_get_ethtool_stats_eth(ds, port, data) > 0)
++ if (priv->mgmt_master && priv->info->ops->autocast_mib &&
++ priv->info->ops->autocast_mib(ds, port, data) > 0)
+ return;
+
+ for (i = 0; i < priv->info->mib_count; i++) {
+@@ -3187,20 +3187,27 @@ static int qca8k_resume(struct device *d
+ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+ qca8k_suspend, qca8k_resume);
+
++static const struct qca8k_info_ops qca8xxx_ops = {
++ .autocast_mib = qca8k_get_ethtool_stats_eth,
++};
++
+ static const struct qca8k_match_data qca8327 = {
+ .id = QCA8K_ID_QCA8327,
+ .reduced_package = true,
+ .mib_count = QCA8K_QCA832X_MIB_COUNT,
++ .ops = &qca8xxx_ops,
+ };
+
+ static const struct qca8k_match_data qca8328 = {
+ .id = QCA8K_ID_QCA8327,
+ .mib_count = QCA8K_QCA832X_MIB_COUNT,
++ .ops = &qca8xxx_ops,
+ };
+
+ static const struct qca8k_match_data qca833x = {
+ .id = QCA8K_ID_QCA8337,
+ .mib_count = QCA8K_QCA833X_MIB_COUNT,
++ .ops = &qca8xxx_ops,
+ };
+
+ static const struct of_device_id qca8k_of_match[] = {
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -324,10 +324,15 @@ enum qca8k_mid_cmd {
+ QCA8K_MIB_CAST = 3,
+ };
+
++struct qca8k_info_ops {
++ int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data);
++};
++
+ struct qca8k_match_data {
+ u8 id;
+ bool reduced_package;
+ u8 mib_count;
++ const struct qca8k_info_ops *ops;
+ };
+
+ enum {
diff --git a/target/linux/generic/backport-6.1/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch b/target/linux/generic/backport-6.1/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch
new file mode 100644
index 0000000000..afa466693a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch
@@ -0,0 +1,6532 @@
+From 027152b830434e3632ad5dd678cc5d4740358dbb Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:12 +0200
+Subject: [PATCH 03/14] net: dsa: qca8k: move mib struct to common code
+
+The same MIB struct is used by drivers based on qca8k family switch. Move
+it to common code to make it accessible also by other drivers.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/Makefile | 1 +
+ drivers/net/dsa/qca/{qca8k.c => qca8k-8xxx.c} | 51 ---------------
+ drivers/net/dsa/qca/qca8k-common.c | 63 +++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 3 +
+ 4 files changed, 67 insertions(+), 51 deletions(-)
+ rename drivers/net/dsa/qca/{qca8k.c => qca8k-8xxx.c} (98%)
+ create mode 100644 drivers/net/dsa/qca/qca8k-common.c
+
+--- a/drivers/net/dsa/qca/Makefile
++++ b/drivers/net/dsa/qca/Makefile
+@@ -1,3 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o
+ obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
++qca8k-y += qca8k-common.o qca8k-8xxx.o
+--- a/drivers/net/dsa/qca/qca8k.c
++++ /dev/null
+@@ -1,3237 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+- * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+- * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved.
+- * Copyright (c) 2016 John Crispin <john@phrozen.org>
+- */
+-
+-#include <linux/module.h>
+-#include <linux/phy.h>
+-#include <linux/netdevice.h>
+-#include <linux/bitfield.h>
+-#include <linux/regmap.h>
+-#include <net/dsa.h>
+-#include <linux/of_net.h>
+-#include <linux/of_mdio.h>
+-#include <linux/of_platform.h>
+-#include <linux/if_bridge.h>
+-#include <linux/mdio.h>
+-#include <linux/phylink.h>
+-#include <linux/gpio/consumer.h>
+-#include <linux/etherdevice.h>
+-#include <linux/dsa/tag_qca.h>
+-
+-#include "qca8k.h"
+-
+-#define MIB_DESC(_s, _o, _n) \
+- { \
+- .size = (_s), \
+- .offset = (_o), \
+- .name = (_n), \
+- }
+-
+-static const struct qca8k_mib_desc ar8327_mib[] = {
+- MIB_DESC(1, 0x00, "RxBroad"),
+- MIB_DESC(1, 0x04, "RxPause"),
+- MIB_DESC(1, 0x08, "RxMulti"),
+- MIB_DESC(1, 0x0c, "RxFcsErr"),
+- MIB_DESC(1, 0x10, "RxAlignErr"),
+- MIB_DESC(1, 0x14, "RxRunt"),
+- MIB_DESC(1, 0x18, "RxFragment"),
+- MIB_DESC(1, 0x1c, "Rx64Byte"),
+- MIB_DESC(1, 0x20, "Rx128Byte"),
+- MIB_DESC(1, 0x24, "Rx256Byte"),
+- MIB_DESC(1, 0x28, "Rx512Byte"),
+- MIB_DESC(1, 0x2c, "Rx1024Byte"),
+- MIB_DESC(1, 0x30, "Rx1518Byte"),
+- MIB_DESC(1, 0x34, "RxMaxByte"),
+- MIB_DESC(1, 0x38, "RxTooLong"),
+- MIB_DESC(2, 0x3c, "RxGoodByte"),
+- MIB_DESC(2, 0x44, "RxBadByte"),
+- MIB_DESC(1, 0x4c, "RxOverFlow"),
+- MIB_DESC(1, 0x50, "Filtered"),
+- MIB_DESC(1, 0x54, "TxBroad"),
+- MIB_DESC(1, 0x58, "TxPause"),
+- MIB_DESC(1, 0x5c, "TxMulti"),
+- MIB_DESC(1, 0x60, "TxUnderRun"),
+- MIB_DESC(1, 0x64, "Tx64Byte"),
+- MIB_DESC(1, 0x68, "Tx128Byte"),
+- MIB_DESC(1, 0x6c, "Tx256Byte"),
+- MIB_DESC(1, 0x70, "Tx512Byte"),
+- MIB_DESC(1, 0x74, "Tx1024Byte"),
+- MIB_DESC(1, 0x78, "Tx1518Byte"),
+- MIB_DESC(1, 0x7c, "TxMaxByte"),
+- MIB_DESC(1, 0x80, "TxOverSize"),
+- MIB_DESC(2, 0x84, "TxByte"),
+- MIB_DESC(1, 0x8c, "TxCollision"),
+- MIB_DESC(1, 0x90, "TxAbortCol"),
+- MIB_DESC(1, 0x94, "TxMultiCol"),
+- MIB_DESC(1, 0x98, "TxSingleCol"),
+- MIB_DESC(1, 0x9c, "TxExcDefer"),
+- MIB_DESC(1, 0xa0, "TxDefer"),
+- MIB_DESC(1, 0xa4, "TxLateCol"),
+- MIB_DESC(1, 0xa8, "RXUnicast"),
+- MIB_DESC(1, 0xac, "TXUnicast"),
+-};
+-
+-static void
+-qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
+-{
+- regaddr >>= 1;
+- *r1 = regaddr & 0x1e;
+-
+- regaddr >>= 5;
+- *r2 = regaddr & 0x7;
+-
+- regaddr >>= 3;
+- *page = regaddr & 0x3ff;
+-}
+-
+-static int
+-qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo)
+-{
+- u16 *cached_lo = &priv->mdio_cache.lo;
+- struct mii_bus *bus = priv->bus;
+- int ret;
+-
+- if (lo == *cached_lo)
+- return 0;
+-
+- ret = bus->write(bus, phy_id, regnum, lo);
+- if (ret < 0)
+- dev_err_ratelimited(&bus->dev,
+- "failed to write qca8k 32bit lo register\n");
+-
+- *cached_lo = lo;
+- return 0;
+-}
+-
+-static int
+-qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi)
+-{
+- u16 *cached_hi = &priv->mdio_cache.hi;
+- struct mii_bus *bus = priv->bus;
+- int ret;
+-
+- if (hi == *cached_hi)
+- return 0;
+-
+- ret = bus->write(bus, phy_id, regnum, hi);
+- if (ret < 0)
+- dev_err_ratelimited(&bus->dev,
+- "failed to write qca8k 32bit hi register\n");
+-
+- *cached_hi = hi;
+- return 0;
+-}
+-
+-static int
+-qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
+-{
+- int ret;
+-
+- ret = bus->read(bus, phy_id, regnum);
+- if (ret >= 0) {
+- *val = ret;
+- ret = bus->read(bus, phy_id, regnum + 1);
+- *val |= ret << 16;
+- }
+-
+- if (ret < 0) {
+- dev_err_ratelimited(&bus->dev,
+- "failed to read qca8k 32bit register\n");
+- *val = 0;
+- return ret;
+- }
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val)
+-{
+- u16 lo, hi;
+- int ret;
+-
+- lo = val & 0xffff;
+- hi = (u16)(val >> 16);
+-
+- ret = qca8k_set_lo(priv, phy_id, regnum, lo);
+- if (ret >= 0)
+- ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi);
+-}
+-
+-static int
+-qca8k_set_page(struct qca8k_priv *priv, u16 page)
+-{
+- u16 *cached_page = &priv->mdio_cache.page;
+- struct mii_bus *bus = priv->bus;
+- int ret;
+-
+- if (page == *cached_page)
+- return 0;
+-
+- ret = bus->write(bus, 0x18, 0, page);
+- if (ret < 0) {
+- dev_err_ratelimited(&bus->dev,
+- "failed to set qca8k page\n");
+- return ret;
+- }
+-
+- *cached_page = page;
+- usleep_range(1000, 2000);
+- return 0;
+-}
+-
+-static int
+-qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
+-{
+- return regmap_read(priv->regmap, reg, val);
+-}
+-
+-static int
+-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
+-{
+- return regmap_write(priv->regmap, reg, val);
+-}
+-
+-static int
+-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
+-{
+- return regmap_update_bits(priv->regmap, reg, mask, write_val);
+-}
+-
+-static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
+-{
+- struct qca8k_mgmt_eth_data *mgmt_eth_data;
+- struct qca8k_priv *priv = ds->priv;
+- struct qca_mgmt_ethhdr *mgmt_ethhdr;
+- u8 len, cmd;
+-
+- mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
+- mgmt_eth_data = &priv->mgmt_eth_data;
+-
+- cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
+- len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
+-
+- /* Make sure the seq match the requested packet */
+- if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
+- mgmt_eth_data->ack = true;
+-
+- if (cmd == MDIO_READ) {
+- mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
+-
+- /* Get the rest of the 12 byte of data.
+- * The read/write function will extract the requested data.
+- */
+- if (len > QCA_HDR_MGMT_DATA1_LEN)
+- memcpy(mgmt_eth_data->data + 1, skb->data,
+- QCA_HDR_MGMT_DATA2_LEN);
+- }
+-
+- complete(&mgmt_eth_data->rw_done);
+-}
+-
+-static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val,
+- int priority, unsigned int len)
+-{
+- struct qca_mgmt_ethhdr *mgmt_ethhdr;
+- unsigned int real_len;
+- struct sk_buff *skb;
+- u32 *data2;
+- u16 hdr;
+-
+- skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
+- if (!skb)
+- return NULL;
+-
+- /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte
+- * Actually for some reason the steps are:
+- * 0: nothing
+- * 1-4: first 4 byte
+- * 5-6: first 12 byte
+- * 7-15: all 16 byte
+- */
+- if (len == 16)
+- real_len = 15;
+- else
+- real_len = len;
+-
+- skb_reset_mac_header(skb);
+- skb_set_network_header(skb, skb->len);
+-
+- mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN);
+-
+- hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION);
+- hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority);
+- hdr |= QCA_HDR_XMIT_FROM_CPU;
+- hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
+- hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
+-
+- mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
+- QCA_HDR_MGMT_CHECK_CODE_VAL);
+-
+- if (cmd == MDIO_WRITE)
+- mgmt_ethhdr->mdio_data = *val;
+-
+- mgmt_ethhdr->hdr = htons(hdr);
+-
+- data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
+- if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN)
+- memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
+-
+- return skb;
+-}
+-
+-static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
+-{
+- struct qca_mgmt_ethhdr *mgmt_ethhdr;
+-
+- mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
+- mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
+-}
+-
+-static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
+- struct sk_buff *skb;
+- bool ack;
+- int ret;
+-
+- skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL,
+- QCA8K_ETHERNET_MDIO_PRIORITY, len);
+- if (!skb)
+- return -ENOMEM;
+-
+- mutex_lock(&mgmt_eth_data->mutex);
+-
+- /* Check mgmt_master if is operational */
+- if (!priv->mgmt_master) {
+- kfree_skb(skb);
+- mutex_unlock(&mgmt_eth_data->mutex);
+- return -EINVAL;
+- }
+-
+- skb->dev = priv->mgmt_master;
+-
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the mdio pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
+-
+- *val = mgmt_eth_data->data[0];
+- if (len > QCA_HDR_MGMT_DATA1_LEN)
+- memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN);
+-
+- ack = mgmt_eth_data->ack;
+-
+- mutex_unlock(&mgmt_eth_data->mutex);
+-
+- if (ret <= 0)
+- return -ETIMEDOUT;
+-
+- if (!ack)
+- return -EINVAL;
+-
+- return 0;
+-}
+-
+-static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
+- struct sk_buff *skb;
+- bool ack;
+- int ret;
+-
+- skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val,
+- QCA8K_ETHERNET_MDIO_PRIORITY, len);
+- if (!skb)
+- return -ENOMEM;
+-
+- mutex_lock(&mgmt_eth_data->mutex);
+-
+- /* Check mgmt_master if is operational */
+- if (!priv->mgmt_master) {
+- kfree_skb(skb);
+- mutex_unlock(&mgmt_eth_data->mutex);
+- return -EINVAL;
+- }
+-
+- skb->dev = priv->mgmt_master;
+-
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the mdio pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
+-
+- ack = mgmt_eth_data->ack;
+-
+- mutex_unlock(&mgmt_eth_data->mutex);
+-
+- if (ret <= 0)
+- return -ETIMEDOUT;
+-
+- if (!ack)
+- return -EINVAL;
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
+-{
+- u32 val = 0;
+- int ret;
+-
+- ret = qca8k_read_eth(priv, reg, &val, sizeof(val));
+- if (ret)
+- return ret;
+-
+- val &= ~mask;
+- val |= write_val;
+-
+- return qca8k_write_eth(priv, reg, &val, sizeof(val));
+-}
+-
+-static int
+-qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- int i, count = len / sizeof(u32), ret;
+-
+- if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len))
+- return 0;
+-
+- for (i = 0; i < count; i++) {
+- ret = regmap_read(priv->regmap, reg + (i * 4), val + i);
+- if (ret < 0)
+- return ret;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- int i, count = len / sizeof(u32), ret;
+- u32 tmp;
+-
+- if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len))
+- return 0;
+-
+- for (i = 0; i < count; i++) {
+- tmp = val[i];
+-
+- ret = regmap_write(priv->regmap, reg + (i * 4), tmp);
+- if (ret < 0)
+- return ret;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- int ret;
+-
+- if (!qca8k_read_eth(priv, reg, val, sizeof(*val)))
+- return 0;
+-
+- qca8k_split_addr(reg, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret < 0)
+- goto exit;
+-
+- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val);
+-
+-exit:
+- mutex_unlock(&bus->mdio_lock);
+- return ret;
+-}
+-
+-static int
+-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- int ret;
+-
+- if (!qca8k_write_eth(priv, reg, &val, sizeof(val)))
+- return 0;
+-
+- qca8k_split_addr(reg, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret < 0)
+- goto exit;
+-
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+-
+-exit:
+- mutex_unlock(&bus->mdio_lock);
+- return ret;
+-}
+-
+-static int
+-qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- u32 val;
+- int ret;
+-
+- if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
+- return 0;
+-
+- qca8k_split_addr(reg, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret < 0)
+- goto exit;
+-
+- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
+- if (ret < 0)
+- goto exit;
+-
+- val &= ~mask;
+- val |= write_val;
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+-
+-exit:
+- mutex_unlock(&bus->mdio_lock);
+-
+- return ret;
+-}
+-
+-static const struct regmap_range qca8k_readable_ranges[] = {
+- regmap_reg_range(0x0000, 0x00e4), /* Global control */
+- regmap_reg_range(0x0100, 0x0168), /* EEE control */
+- regmap_reg_range(0x0200, 0x0270), /* Parser control */
+- regmap_reg_range(0x0400, 0x0454), /* ACL */
+- regmap_reg_range(0x0600, 0x0718), /* Lookup */
+- regmap_reg_range(0x0800, 0x0b70), /* QM */
+- regmap_reg_range(0x0c00, 0x0c80), /* PKT */
+- regmap_reg_range(0x0e00, 0x0e98), /* L3 */
+- regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */
+- regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */
+- regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */
+- regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */
+- regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */
+- regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */
+- regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */
+-
+-};
+-
+-static const struct regmap_access_table qca8k_readable_table = {
+- .yes_ranges = qca8k_readable_ranges,
+- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
+-};
+-
+-static struct regmap_config qca8k_regmap_config = {
+- .reg_bits = 16,
+- .val_bits = 32,
+- .reg_stride = 4,
+- .max_register = 0x16ac, /* end MIB - Port6 range */
+- .reg_read = qca8k_regmap_read,
+- .reg_write = qca8k_regmap_write,
+- .reg_update_bits = qca8k_regmap_update_bits,
+- .rd_table = &qca8k_readable_table,
+- .disable_locking = true, /* Locking is handled by qca8k read/write */
+- .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
+-};
+-
+-static int
+-qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+-{
+- u32 val;
+-
+- return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
+- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
+-}
+-
+-static int
+-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+-{
+- u32 reg[3];
+- int ret;
+-
+- /* load the ARL table into an array */
+- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
+- if (ret)
+- return ret;
+-
+- /* vid - 83:72 */
+- fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
+- /* aging - 67:64 */
+- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
+- /* portmask - 54:48 */
+- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
+- /* mac - 47:0 */
+- fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
+- fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
+- fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
+- fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
+- fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
+- fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
+- u8 aging)
+-{
+- u32 reg[3] = { 0 };
+-
+- /* vid - 83:72 */
+- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
+- /* aging - 67:64 */
+- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
+- /* portmask - 54:48 */
+- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
+- /* mac - 47:0 */
+- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
+- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+-
+- /* load the array into the ARL table */
+- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
+-}
+-
+-static int
+-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
+-{
+- u32 reg;
+- int ret;
+-
+- /* Set the command and FDB index */
+- reg = QCA8K_ATU_FUNC_BUSY;
+- reg |= cmd;
+- if (port >= 0) {
+- reg |= QCA8K_ATU_FUNC_PORT_EN;
+- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
+- }
+-
+- /* Write the function register triggering the table access */
+- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
+- if (ret)
+- return ret;
+-
+- /* wait for completion */
+- ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY);
+- if (ret)
+- return ret;
+-
+- /* Check for table full violation when adding an entry */
+- if (cmd == QCA8K_FDB_LOAD) {
+- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, &reg);
+- if (ret < 0)
+- return ret;
+- if (reg & QCA8K_ATU_FUNC_FULL)
+- return -1;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
+-{
+- int ret;
+-
+- qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
+- if (ret < 0)
+- return ret;
+-
+- return qca8k_fdb_read(priv, fdb);
+-}
+-
+-static int
+-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask,
+- u16 vid, u8 aging)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_write(priv, vid, port_mask, mac, aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_write(priv, vid, port_mask, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static void
+-qca8k_fdb_flush(struct qca8k_priv *priv)
+-{
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
+- mutex_unlock(&priv->reg_mutex);
+-}
+-
+-static int
+-qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
+- const u8 *mac, u16 vid)
+-{
+- struct qca8k_fdb fdb = { 0 };
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- qca8k_fdb_write(priv, vid, 0, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+- if (ret < 0)
+- goto exit;
+-
+- ret = qca8k_fdb_read(priv, &fdb);
+- if (ret < 0)
+- goto exit;
+-
+- /* Rule exist. Delete first */
+- if (!fdb.aging) {
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- if (ret)
+- goto exit;
+- }
+-
+- /* Add port to fdb portmask */
+- fdb.port_mask |= port_mask;
+-
+- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+-qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
+- const u8 *mac, u16 vid)
+-{
+- struct qca8k_fdb fdb = { 0 };
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- qca8k_fdb_write(priv, vid, 0, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+- if (ret < 0)
+- goto exit;
+-
+- /* Rule doesn't exist. Why delete? */
+- if (!fdb.aging) {
+- ret = -EINVAL;
+- goto exit;
+- }
+-
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- if (ret)
+- goto exit;
+-
+- /* Only port in the rule is this port. Don't re insert */
+- if (fdb.port_mask == port_mask)
+- goto exit;
+-
+- /* Remove port from port mask */
+- fdb.port_mask &= ~port_mask;
+-
+- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+-qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+-{
+- u32 reg;
+- int ret;
+-
+- /* Set the command and VLAN index */
+- reg = QCA8K_VTU_FUNC1_BUSY;
+- reg |= cmd;
+- reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
+-
+- /* Write the function register triggering the table access */
+- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
+- if (ret)
+- return ret;
+-
+- /* wait for completion */
+- ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
+- if (ret)
+- return ret;
+-
+- /* Check for table full violation when adding an entry */
+- if (cmd == QCA8K_VLAN_LOAD) {
+- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, &reg);
+- if (ret < 0)
+- return ret;
+- if (reg & QCA8K_VTU_FUNC1_FULL)
+- return -ENOMEM;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
+-{
+- u32 reg;
+- int ret;
+-
+- /*
+- We do the right thing with VLAN 0 and treat it as untagged while
+- preserving the tag on egress.
+- */
+- if (vid == 0)
+- return 0;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+- if (ret < 0)
+- goto out;
+-
+- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+- if (ret < 0)
+- goto out;
+- reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+- if (untagged)
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
+- else
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
+-
+- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+- if (ret)
+- goto out;
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+-
+-out:
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
+-{
+- u32 reg, mask;
+- int ret, i;
+- bool del;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+- if (ret < 0)
+- goto out;
+-
+- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+- if (ret < 0)
+- goto out;
+- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
+-
+- /* Check if we're the last member to be removed */
+- del = true;
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
+-
+- if ((reg & mask) != mask) {
+- del = false;
+- break;
+- }
+- }
+-
+- if (del) {
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
+- } else {
+- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+- if (ret)
+- goto out;
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+- }
+-
+-out:
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_mib_init(struct qca8k_priv *priv)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
+- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
+- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) |
+- QCA8K_MIB_BUSY);
+- if (ret)
+- goto exit;
+-
+- ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
+- if (ret)
+- goto exit;
+-
+- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+- if (ret)
+- goto exit;
+-
+- ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static void
+-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
+-{
+- u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
+-
+- /* Port 0 and 6 have no internal PHY */
+- if (port > 0 && port < 6)
+- mask |= QCA8K_PORT_STATUS_LINK_AUTO;
+-
+- if (enable)
+- regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+- else
+- regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+-}
+-
+-static int
+-qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
+- struct sk_buff *read_skb, u32 *val)
+-{
+- struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL);
+- bool ack;
+- int ret;
+-
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the copy pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- QCA8K_ETHERNET_TIMEOUT);
+-
+- ack = mgmt_eth_data->ack;
+-
+- if (ret <= 0)
+- return -ETIMEDOUT;
+-
+- if (!ack)
+- return -EINVAL;
+-
+- *val = mgmt_eth_data->data[0];
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
+- int regnum, u16 data)
+-{
+- struct sk_buff *write_skb, *clear_skb, *read_skb;
+- struct qca8k_mgmt_eth_data *mgmt_eth_data;
+- u32 write_val, clear_val = 0, val;
+- struct net_device *mgmt_master;
+- int ret, ret1;
+- bool ack;
+-
+- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+- return -EINVAL;
+-
+- mgmt_eth_data = &priv->mgmt_eth_data;
+-
+- write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+- QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+- QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+-
+- if (read) {
+- write_val |= QCA8K_MDIO_MASTER_READ;
+- } else {
+- write_val |= QCA8K_MDIO_MASTER_WRITE;
+- write_val |= QCA8K_MDIO_MASTER_DATA(data);
+- }
+-
+- /* Prealloc all the needed skb before the lock */
+- write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val,
+- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val));
+- if (!write_skb)
+- return -ENOMEM;
+-
+- clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val,
+- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
+- if (!clear_skb) {
+- ret = -ENOMEM;
+- goto err_clear_skb;
+- }
+-
+- read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val,
+- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
+- if (!read_skb) {
+- ret = -ENOMEM;
+- goto err_read_skb;
+- }
+-
+- /* Actually start the request:
+- * 1. Send mdio master packet
+- * 2. Busy Wait for mdio master command
+- * 3. Get the data if we are reading
+- * 4. Reset the mdio master (even with error)
+- */
+- mutex_lock(&mgmt_eth_data->mutex);
+-
+- /* Check if mgmt_master is operational */
+- mgmt_master = priv->mgmt_master;
+- if (!mgmt_master) {
+- mutex_unlock(&mgmt_eth_data->mutex);
+- ret = -EINVAL;
+- goto err_mgmt_master;
+- }
+-
+- read_skb->dev = mgmt_master;
+- clear_skb->dev = mgmt_master;
+- write_skb->dev = mgmt_master;
+-
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the write pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(write_skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- QCA8K_ETHERNET_TIMEOUT);
+-
+- ack = mgmt_eth_data->ack;
+-
+- if (ret <= 0) {
+- ret = -ETIMEDOUT;
+- kfree_skb(read_skb);
+- goto exit;
+- }
+-
+- if (!ack) {
+- ret = -EINVAL;
+- kfree_skb(read_skb);
+- goto exit;
+- }
+-
+- ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1,
+- !(val & QCA8K_MDIO_MASTER_BUSY), 0,
+- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- mgmt_eth_data, read_skb, &val);
+-
+- if (ret < 0 && ret1 < 0) {
+- ret = ret1;
+- goto exit;
+- }
+-
+- if (read) {
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the read pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(read_skb);
+-
+- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- QCA8K_ETHERNET_TIMEOUT);
+-
+- ack = mgmt_eth_data->ack;
+-
+- if (ret <= 0) {
+- ret = -ETIMEDOUT;
+- goto exit;
+- }
+-
+- if (!ack) {
+- ret = -EINVAL;
+- goto exit;
+- }
+-
+- ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK;
+- } else {
+- kfree_skb(read_skb);
+- }
+-exit:
+- reinit_completion(&mgmt_eth_data->rw_done);
+-
+- /* Increment seq_num and set it in the clear pkt */
+- mgmt_eth_data->seq++;
+- qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq);
+- mgmt_eth_data->ack = false;
+-
+- dev_queue_xmit(clear_skb);
+-
+- wait_for_completion_timeout(&mgmt_eth_data->rw_done,
+- QCA8K_ETHERNET_TIMEOUT);
+-
+- mutex_unlock(&mgmt_eth_data->mutex);
+-
+- return ret;
+-
+- /* Error handling before lock */
+-err_mgmt_master:
+- kfree_skb(read_skb);
+-err_read_skb:
+- kfree_skb(clear_skb);
+-err_clear_skb:
+- kfree_skb(write_skb);
+-
+- return ret;
+-}
+-
+-static u32
+-qca8k_port_to_phy(int port)
+-{
+- /* From Andrew Lunn:
+- * Port 0 has no internal phy.
+- * Port 1 has an internal PHY at MDIO address 0.
+- * Port 2 has an internal PHY at MDIO address 1.
+- * ...
+- * Port 5 has an internal PHY at MDIO address 4.
+- * Port 6 has no internal PHY.
+- */
+-
+- return port - 1;
+-}
+-
+-static int
+-qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)
+-{
+- u16 r1, r2, page;
+- u32 val;
+- int ret, ret1;
+-
+- qca8k_split_addr(reg, &r1, &r2, &page);
+-
+- ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0,
+- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- bus, 0x10 | r2, r1, &val);
+-
+- /* Check if qca8k_read has failed for a different reason
+- * before returnting -ETIMEDOUT
+- */
+- if (ret < 0 && ret1 < 0)
+- return ret1;
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data)
+-{
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- u32 val;
+- int ret;
+-
+- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+- return -EINVAL;
+-
+- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+- QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+- QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
+- QCA8K_MDIO_MASTER_DATA(data);
+-
+- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret)
+- goto exit;
+-
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+-
+- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_BUSY);
+-
+-exit:
+- /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
+-
+- mutex_unlock(&bus->mdio_lock);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum)
+-{
+- struct mii_bus *bus = priv->bus;
+- u16 r1, r2, page;
+- u32 val;
+- int ret;
+-
+- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
+- return -EINVAL;
+-
+- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
+- QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
+- QCA8K_MDIO_MASTER_REG_ADDR(regnum);
+-
+- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
+-
+- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+-
+- ret = qca8k_set_page(priv, page);
+- if (ret)
+- goto exit;
+-
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+-
+- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_BUSY);
+- if (ret)
+- goto exit;
+-
+- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
+-
+-exit:
+- /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
+-
+- mutex_unlock(&bus->mdio_lock);
+-
+- if (ret >= 0)
+- ret = val & QCA8K_MDIO_MASTER_DATA_MASK;
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data)
+-{
+- struct qca8k_priv *priv = slave_bus->priv;
+- int ret;
+-
+- /* Use mdio Ethernet when available, fallback to legacy one on error */
+- ret = qca8k_phy_eth_command(priv, false, phy, regnum, data);
+- if (!ret)
+- return 0;
+-
+- return qca8k_mdio_write(priv, phy, regnum, data);
+-}
+-
+-static int
+-qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum)
+-{
+- struct qca8k_priv *priv = slave_bus->priv;
+- int ret;
+-
+- /* Use mdio Ethernet when available, fallback to legacy one on error */
+- ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0);
+- if (ret >= 0)
+- return ret;
+-
+- ret = qca8k_mdio_read(priv, phy, regnum);
+-
+- if (ret < 0)
+- return 0xffff;
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data)
+-{
+- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+-
+- return qca8k_internal_mdio_write(slave_bus, port, regnum, data);
+-}
+-
+-static int
+-qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum)
+-{
+- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
+-
+- return qca8k_internal_mdio_read(slave_bus, port, regnum);
+-}
+-
+-static int
+-qca8k_mdio_register(struct qca8k_priv *priv)
+-{
+- struct dsa_switch *ds = priv->ds;
+- struct device_node *mdio;
+- struct mii_bus *bus;
+-
+- bus = devm_mdiobus_alloc(ds->dev);
+- if (!bus)
+- return -ENOMEM;
+-
+- bus->priv = (void *)priv;
+- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d",
+- ds->dst->index, ds->index);
+- bus->parent = ds->dev;
+- bus->phy_mask = ~ds->phys_mii_mask;
+- ds->slave_mii_bus = bus;
+-
+- /* Check if the devicetree declare the port:phy mapping */
+- mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
+- if (of_device_is_available(mdio)) {
+- bus->name = "qca8k slave mii";
+- bus->read = qca8k_internal_mdio_read;
+- bus->write = qca8k_internal_mdio_write;
+- return devm_of_mdiobus_register(priv->dev, bus, mdio);
+- }
+-
+- /* If a mapping can't be found the legacy mapping is used,
+- * using the qca8k_port_to_phy function
+- */
+- bus->name = "qca8k-legacy slave mii";
+- bus->read = qca8k_legacy_mdio_read;
+- bus->write = qca8k_legacy_mdio_write;
+- return devm_mdiobus_register(priv->dev, bus);
+-}
+-
+-static int
+-qca8k_setup_mdio_bus(struct qca8k_priv *priv)
+-{
+- u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg;
+- struct device_node *ports, *port;
+- phy_interface_t mode;
+- int err;
+-
+- ports = of_get_child_by_name(priv->dev->of_node, "ports");
+- if (!ports)
+- ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports");
+-
+- if (!ports)
+- return -EINVAL;
+-
+- for_each_available_child_of_node(ports, port) {
+- err = of_property_read_u32(port, "reg", &reg);
+- if (err) {
+- of_node_put(port);
+- of_node_put(ports);
+- return err;
+- }
+-
+- if (!dsa_is_user_port(priv->ds, reg))
+- continue;
+-
+- of_get_phy_mode(port, &mode);
+-
+- if (of_property_read_bool(port, "phy-handle") &&
+- mode != PHY_INTERFACE_MODE_INTERNAL)
+- external_mdio_mask |= BIT(reg);
+- else
+- internal_mdio_mask |= BIT(reg);
+- }
+-
+- of_node_put(ports);
+- if (!external_mdio_mask && !internal_mdio_mask) {
+- dev_err(priv->dev, "no PHYs are defined.\n");
+- return -EINVAL;
+- }
+-
+- /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
+- * the MDIO_MASTER register also _disconnects_ the external MDC
+- * passthrough to the internal PHYs. It's not possible to use both
+- * configurations at the same time!
+- *
+- * Because this came up during the review process:
+- * If the external mdio-bus driver is capable magically disabling
+- * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's
+- * accessors for the time being, it would be possible to pull this
+- * off.
+- */
+- if (!!external_mdio_mask && !!internal_mdio_mask) {
+- dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
+- return -EINVAL;
+- }
+-
+- if (external_mdio_mask) {
+- /* Make sure to disable the internal mdio bus in cases
+- * a dt-overlay and driver reload changed the configuration
+- */
+-
+- return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL,
+- QCA8K_MDIO_MASTER_EN);
+- }
+-
+- return qca8k_mdio_register(priv);
+-}
+-
+-static int
+-qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
+-{
+- u32 mask = 0;
+- int ret = 0;
+-
+- /* SoC specific settings for ipq8064.
+- * If more device require this consider adding
+- * a dedicated binding.
+- */
+- if (of_machine_is_compatible("qcom,ipq8064"))
+- mask |= QCA8K_MAC_PWR_RGMII0_1_8V;
+-
+- /* SoC specific settings for ipq8065 */
+- if (of_machine_is_compatible("qcom,ipq8065"))
+- mask |= QCA8K_MAC_PWR_RGMII1_1_8V;
+-
+- if (mask) {
+- ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL,
+- QCA8K_MAC_PWR_RGMII0_1_8V |
+- QCA8K_MAC_PWR_RGMII1_1_8V,
+- mask);
+- }
+-
+- return ret;
+-}
+-
+-static int qca8k_find_cpu_port(struct dsa_switch *ds)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- /* Find the connected cpu port. Valid port are 0 or 6 */
+- if (dsa_is_cpu_port(ds, 0))
+- return 0;
+-
+- dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
+-
+- if (dsa_is_cpu_port(ds, 6))
+- return 6;
+-
+- return -EINVAL;
+-}
+-
+-static int
+-qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
+-{
+- const struct qca8k_match_data *data = priv->info;
+- struct device_node *node = priv->dev->of_node;
+- u32 val = 0;
+- int ret;
+-
+- /* QCA8327 require to set to the correct mode.
+- * His bigger brother QCA8328 have the 172 pin layout.
+- * Should be applied by default but we set this just to make sure.
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8327) {
+- /* Set the correct package of 148 pin for QCA8327 */
+- if (data->reduced_package)
+- val |= QCA8327_PWS_PACKAGE148_EN;
+-
+- ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
+- val);
+- if (ret)
+- return ret;
+- }
+-
+- if (of_property_read_bool(node, "qca,ignore-power-on-sel"))
+- val |= QCA8K_PWS_POWER_ON_SEL;
+-
+- if (of_property_read_bool(node, "qca,led-open-drain")) {
+- if (!(val & QCA8K_PWS_POWER_ON_SEL)) {
+- dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set.");
+- return -EINVAL;
+- }
+-
+- val |= QCA8K_PWS_LED_OPEN_EN_CSR;
+- }
+-
+- return qca8k_rmw(priv, QCA8K_REG_PWS,
+- QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL,
+- val);
+-}
+-
+-static int
+-qca8k_parse_port_config(struct qca8k_priv *priv)
+-{
+- int port, cpu_port_index = -1, ret;
+- struct device_node *port_dn;
+- phy_interface_t mode;
+- struct dsa_port *dp;
+- u32 delay;
+-
+- /* We have 2 CPU port. Check them */
+- for (port = 0; port < QCA8K_NUM_PORTS; port++) {
+- /* Skip every other port */
+- if (port != 0 && port != 6)
+- continue;
+-
+- dp = dsa_to_port(priv->ds, port);
+- port_dn = dp->dn;
+- cpu_port_index++;
+-
+- if (!of_device_is_available(port_dn))
+- continue;
+-
+- ret = of_get_phy_mode(port_dn, &mode);
+- if (ret)
+- continue;
+-
+- switch (mode) {
+- case PHY_INTERFACE_MODE_RGMII:
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- case PHY_INTERFACE_MODE_SGMII:
+- delay = 0;
+-
+- if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay))
+- /* Switch regs accept value in ns, convert ps to ns */
+- delay = delay / 1000;
+- else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
+- mode == PHY_INTERFACE_MODE_RGMII_TXID)
+- delay = 1;
+-
+- if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) {
+- dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
+- delay = 3;
+- }
+-
+- priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay;
+-
+- delay = 0;
+-
+- if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay))
+- /* Switch regs accept value in ns, convert ps to ns */
+- delay = delay / 1000;
+- else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
+- mode == PHY_INTERFACE_MODE_RGMII_RXID)
+- delay = 2;
+-
+- if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) {
+- dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
+- delay = 3;
+- }
+-
+- priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay;
+-
+- /* Skip sgmii parsing for rgmii* mode */
+- if (mode == PHY_INTERFACE_MODE_RGMII ||
+- mode == PHY_INTERFACE_MODE_RGMII_ID ||
+- mode == PHY_INTERFACE_MODE_RGMII_TXID ||
+- mode == PHY_INTERFACE_MODE_RGMII_RXID)
+- break;
+-
+- if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
+- priv->ports_config.sgmii_tx_clk_falling_edge = true;
+-
+- if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
+- priv->ports_config.sgmii_rx_clk_falling_edge = true;
+-
+- if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) {
+- priv->ports_config.sgmii_enable_pll = true;
+-
+- if (priv->switch_id == QCA8K_ID_QCA8327) {
+- dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling");
+- priv->ports_config.sgmii_enable_pll = false;
+- }
+-
+- if (priv->switch_revision < 2)
+- dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more.");
+- }
+-
+- break;
+- default:
+- continue;
+- }
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_setup(struct dsa_switch *ds)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int cpu_port, ret, i;
+- u32 mask;
+-
+- cpu_port = qca8k_find_cpu_port(ds);
+- if (cpu_port < 0) {
+- dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
+- return cpu_port;
+- }
+-
+- /* Parse CPU port config to be later used in phy_link mac_config */
+- ret = qca8k_parse_port_config(priv);
+- if (ret)
+- return ret;
+-
+- ret = qca8k_setup_mdio_bus(priv);
+- if (ret)
+- return ret;
+-
+- ret = qca8k_setup_of_pws_reg(priv);
+- if (ret)
+- return ret;
+-
+- ret = qca8k_setup_mac_pwr_sel(priv);
+- if (ret)
+- return ret;
+-
+- /* Make sure MAC06 is disabled */
+- ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL,
+- QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
+- if (ret) {
+- dev_err(priv->dev, "failed disabling MAC06 exchange");
+- return ret;
+- }
+-
+- /* Enable CPU Port */
+- ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
+- if (ret) {
+- dev_err(priv->dev, "failed enabling CPU port");
+- return ret;
+- }
+-
+- /* Enable MIB counters */
+- ret = qca8k_mib_init(priv);
+- if (ret)
+- dev_warn(priv->dev, "mib init failed");
+-
+- /* Initial setup of all ports */
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- /* Disable forwarding by default on all ports */
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_MEMBER, 0);
+- if (ret)
+- return ret;
+-
+- /* Enable QCA header mode on all cpu ports */
+- if (dsa_is_cpu_port(ds, i)) {
+- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
+- FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
+- FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
+- if (ret) {
+- dev_err(priv->dev, "failed enabling QCA header mode");
+- return ret;
+- }
+- }
+-
+- /* Disable MAC by default on all user ports */
+- if (dsa_is_user_port(ds, i))
+- qca8k_port_set_status(priv, i, 0);
+- }
+-
+- /* Forward all unknown frames to CPU port for Linux processing
+- * Notice that in multi-cpu config only one port should be set
+- * for igmp, unknown, multicast and broadcast packet
+- */
+- ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) |
+- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) |
+- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) |
+- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));
+- if (ret)
+- return ret;
+-
+- /* Setup connection between CPU port & user ports
+- * Configure specific switch configuration for ports
+- */
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- /* CPU port gets connected to all user ports of the switch */
+- if (dsa_is_cpu_port(ds, i)) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
+- if (ret)
+- return ret;
+- }
+-
+- /* Individual user ports get connected to CPU port only */
+- if (dsa_is_user_port(ds, i)) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_MEMBER,
+- BIT(cpu_port));
+- if (ret)
+- return ret;
+-
+- /* Enable ARP Auto-learning by default */
+- ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
+- QCA8K_PORT_LOOKUP_LEARN);
+- if (ret)
+- return ret;
+-
+- /* For port based vlans to work we need to set the
+- * default egress vid
+- */
+- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
+- QCA8K_EGREES_VLAN_PORT_MASK(i),
+- QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));
+- if (ret)
+- return ret;
+-
+- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
+- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
+- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
+- if (ret)
+- return ret;
+- }
+-
+- /* The port 5 of the qca8337 have some problem in flood condition. The
+- * original legacy driver had some specific buffer and priority settings
+- * for the different port suggested by the QCA switch team. Add this
+- * missing settings to improve switch stability under load condition.
+- * This problem is limited to qca8337 and other qca8k switch are not affected.
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8337) {
+- switch (i) {
+- /* The 2 CPU port and port 5 requires some different
+- * priority than any other ports.
+- */
+- case 0:
+- case 5:
+- case 6:
+- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
+- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
+- break;
+- default:
+- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
+- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
+- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
+- }
+- qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
+-
+- mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
+- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
+- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+- QCA8K_PORT_HOL_CTRL1_WRED_EN;
+- qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
+- QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
+- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
+- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
+- QCA8K_PORT_HOL_CTRL1_WRED_EN,
+- mask);
+- }
+- }
+-
+- /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
+- if (priv->switch_id == QCA8K_ID_QCA8327) {
+- mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
+- QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
+- qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
+- QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK |
+- QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,
+- mask);
+- }
+-
+- /* Setup our port MTUs to match power on defaults */
+- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
+- if (ret)
+- dev_warn(priv->dev, "failed setting MTU settings");
+-
+- /* Flush the FDB table */
+- qca8k_fdb_flush(priv);
+-
+- /* We don't have interrupts for link changes, so we need to poll */
+- ds->pcs_poll = true;
+-
+- /* Set min a max ageing value supported */
+- ds->ageing_time_min = 7000;
+- ds->ageing_time_max = 458745000;
+-
+- /* Set max number of LAGs supported */
+- ds->num_lag_ids = QCA8K_NUM_LAGS;
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index,
+- u32 reg)
+-{
+- u32 delay, val = 0;
+- int ret;
+-
+- /* Delay can be declared in 3 different way.
+- * Mode to rgmii and internal-delay standard binding defined
+- * rgmii-id or rgmii-tx/rx phy mode set.
+- * The parse logic set a delay different than 0 only when one
+- * of the 3 different way is used. In all other case delay is
+- * not enabled. With ID or TX/RXID delay is enabled and set
+- * to the default and recommended value.
+- */
+- if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) {
+- delay = priv->ports_config.rgmii_tx_delay[cpu_port_index];
+-
+- val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
+- }
+-
+- if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) {
+- delay = priv->ports_config.rgmii_rx_delay[cpu_port_index];
+-
+- val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
+- }
+-
+- /* Set RGMII delay based on the selected values */
+- ret = qca8k_rmw(priv, reg,
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK |
+- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN,
+- val);
+- if (ret)
+- dev_err(priv->dev, "Failed to set internal delay for CPU port%d",
+- cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6);
+-}
+-
+-static void
+-qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+- const struct phylink_link_state *state)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int cpu_port_index, ret;
+- u32 reg, val;
+-
+- switch (port) {
+- case 0: /* 1st CPU port */
+- if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+- state->interface != PHY_INTERFACE_MODE_SGMII)
+- return;
+-
+- reg = QCA8K_REG_PORT0_PAD_CTRL;
+- cpu_port_index = QCA8K_CPU_PORT0;
+- break;
+- case 1:
+- case 2:
+- case 3:
+- case 4:
+- case 5:
+- /* Internal PHY, nothing to do */
+- return;
+- case 6: /* 2nd CPU port / external PHY */
+- if (state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+- state->interface != PHY_INTERFACE_MODE_SGMII &&
+- state->interface != PHY_INTERFACE_MODE_1000BASEX)
+- return;
+-
+- reg = QCA8K_REG_PORT6_PAD_CTRL;
+- cpu_port_index = QCA8K_CPU_PORT6;
+- break;
+- default:
+- dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
+- return;
+- }
+-
+- if (port != 6 && phylink_autoneg_inband(mode)) {
+- dev_err(ds->dev, "%s: in-band negotiation unsupported\n",
+- __func__);
+- return;
+- }
+-
+- switch (state->interface) {
+- case PHY_INTERFACE_MODE_RGMII:
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
+-
+- /* Configure rgmii delay */
+- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
+-
+- /* QCA8337 requires to set rgmii rx delay for all ports.
+- * This is enabled through PORT5_PAD_CTRL for all ports,
+- * rather than individual port registers.
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8337)
+- qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
+- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
+- break;
+- case PHY_INTERFACE_MODE_SGMII:
+- case PHY_INTERFACE_MODE_1000BASEX:
+- /* Enable SGMII on the port */
+- qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
+-
+- /* Enable/disable SerDes auto-negotiation as necessary */
+- ret = qca8k_read(priv, QCA8K_REG_PWS, &val);
+- if (ret)
+- return;
+- if (phylink_autoneg_inband(mode))
+- val &= ~QCA8K_PWS_SERDES_AEN_DIS;
+- else
+- val |= QCA8K_PWS_SERDES_AEN_DIS;
+- qca8k_write(priv, QCA8K_REG_PWS, val);
+-
+- /* Configure the SGMII parameters */
+- ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
+- if (ret)
+- return;
+-
+- val |= QCA8K_SGMII_EN_SD;
+-
+- if (priv->ports_config.sgmii_enable_pll)
+- val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
+- QCA8K_SGMII_EN_TX;
+-
+- if (dsa_is_cpu_port(ds, port)) {
+- /* CPU port, we're talking to the CPU MAC, be a PHY */
+- val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
+- val |= QCA8K_SGMII_MODE_CTRL_PHY;
+- } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+- val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
+- val |= QCA8K_SGMII_MODE_CTRL_MAC;
+- } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
+- val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
+- val |= QCA8K_SGMII_MODE_CTRL_BASEX;
+- }
+-
+- qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
+-
+- /* From original code is reported port instability as SGMII also
+- * require delay set. Apply advised values here or take them from DT.
+- */
+- if (state->interface == PHY_INTERFACE_MODE_SGMII)
+- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
+-
+- /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
+- * falling edge is set writing in the PORT0 PAD reg
+- */
+- if (priv->switch_id == QCA8K_ID_QCA8327 ||
+- priv->switch_id == QCA8K_ID_QCA8337)
+- reg = QCA8K_REG_PORT0_PAD_CTRL;
+-
+- val = 0;
+-
+- /* SGMII Clock phase configuration */
+- if (priv->ports_config.sgmii_rx_clk_falling_edge)
+- val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE;
+-
+- if (priv->ports_config.sgmii_tx_clk_falling_edge)
+- val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
+-
+- if (val)
+- ret = qca8k_rmw(priv, reg,
+- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
+- val);
+-
+- break;
+- default:
+- dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
+- phy_modes(state->interface), port);
+- return;
+- }
+-}
+-
+-static void
+-qca8k_phylink_validate(struct dsa_switch *ds, int port,
+- unsigned long *supported,
+- struct phylink_link_state *state)
+-{
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+-
+- switch (port) {
+- case 0: /* 1st CPU port */
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+- state->interface != PHY_INTERFACE_MODE_SGMII)
+- goto unsupported;
+- break;
+- case 1:
+- case 2:
+- case 3:
+- case 4:
+- case 5:
+- /* Internal PHY */
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_GMII &&
+- state->interface != PHY_INTERFACE_MODE_INTERNAL)
+- goto unsupported;
+- break;
+- case 6: /* 2nd CPU port / external PHY */
+- if (state->interface != PHY_INTERFACE_MODE_NA &&
+- state->interface != PHY_INTERFACE_MODE_RGMII &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
+- state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
+- state->interface != PHY_INTERFACE_MODE_SGMII &&
+- state->interface != PHY_INTERFACE_MODE_1000BASEX)
+- goto unsupported;
+- break;
+- default:
+-unsupported:
+- linkmode_zero(supported);
+- return;
+- }
+-
+- phylink_set_port_modes(mask);
+- phylink_set(mask, Autoneg);
+-
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 10baseT_Half);
+- phylink_set(mask, 10baseT_Full);
+- phylink_set(mask, 100baseT_Half);
+- phylink_set(mask, 100baseT_Full);
+-
+- if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
+- phylink_set(mask, 1000baseX_Full);
+-
+- phylink_set(mask, Pause);
+- phylink_set(mask, Asym_Pause);
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
+-}
+-
+-static int
+-qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port,
+- struct phylink_link_state *state)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- u32 reg;
+- int ret;
+-
+- ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), &reg);
+- if (ret < 0)
+- return ret;
+-
+- state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
+- state->an_complete = state->link;
+- state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO);
+- state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL :
+- DUPLEX_HALF;
+-
+- switch (reg & QCA8K_PORT_STATUS_SPEED) {
+- case QCA8K_PORT_STATUS_SPEED_10:
+- state->speed = SPEED_10;
+- break;
+- case QCA8K_PORT_STATUS_SPEED_100:
+- state->speed = SPEED_100;
+- break;
+- case QCA8K_PORT_STATUS_SPEED_1000:
+- state->speed = SPEED_1000;
+- break;
+- default:
+- state->speed = SPEED_UNKNOWN;
+- break;
+- }
+-
+- state->pause = MLO_PAUSE_NONE;
+- if (reg & QCA8K_PORT_STATUS_RXFLOW)
+- state->pause |= MLO_PAUSE_RX;
+- if (reg & QCA8K_PORT_STATUS_TXFLOW)
+- state->pause |= MLO_PAUSE_TX;
+-
+- return 1;
+-}
+-
+-static void
+-qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
+- phy_interface_t interface)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- qca8k_port_set_status(priv, port, 0);
+-}
+-
+-static void
+-qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
+- phy_interface_t interface, struct phy_device *phydev,
+- int speed, int duplex, bool tx_pause, bool rx_pause)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- u32 reg;
+-
+- if (phylink_autoneg_inband(mode)) {
+- reg = QCA8K_PORT_STATUS_LINK_AUTO;
+- } else {
+- switch (speed) {
+- case SPEED_10:
+- reg = QCA8K_PORT_STATUS_SPEED_10;
+- break;
+- case SPEED_100:
+- reg = QCA8K_PORT_STATUS_SPEED_100;
+- break;
+- case SPEED_1000:
+- reg = QCA8K_PORT_STATUS_SPEED_1000;
+- break;
+- default:
+- reg = QCA8K_PORT_STATUS_LINK_AUTO;
+- break;
+- }
+-
+- if (duplex == DUPLEX_FULL)
+- reg |= QCA8K_PORT_STATUS_DUPLEX;
+-
+- if (rx_pause || dsa_is_cpu_port(ds, port))
+- reg |= QCA8K_PORT_STATUS_RXFLOW;
+-
+- if (tx_pause || dsa_is_cpu_port(ds, port))
+- reg |= QCA8K_PORT_STATUS_TXFLOW;
+- }
+-
+- reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
+-
+- qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
+-}
+-
+-static void
+-qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int i;
+-
+- if (stringset != ETH_SS_STATS)
+- return;
+-
+- for (i = 0; i < priv->info->mib_count; i++)
+- strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
+- ETH_GSTRING_LEN);
+-}
+-
+-static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb)
+-{
+- struct qca8k_mib_eth_data *mib_eth_data;
+- struct qca8k_priv *priv = ds->priv;
+- const struct qca8k_mib_desc *mib;
+- struct mib_ethhdr *mib_ethhdr;
+- int i, mib_len, offset = 0;
+- u64 *data;
+- u8 port;
+-
+- mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb);
+- mib_eth_data = &priv->mib_eth_data;
+-
+- /* The switch autocast every port. Ignore other packet and
+- * parse only the requested one.
+- */
+- port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr));
+- if (port != mib_eth_data->req_port)
+- goto exit;
+-
+- data = mib_eth_data->data;
+-
+- for (i = 0; i < priv->info->mib_count; i++) {
+- mib = &ar8327_mib[i];
+-
+- /* First 3 mib are present in the skb head */
+- if (i < 3) {
+- data[i] = mib_ethhdr->data[i];
+- continue;
+- }
+-
+- mib_len = sizeof(uint32_t);
+-
+- /* Some mib are 64 bit wide */
+- if (mib->size == 2)
+- mib_len = sizeof(uint64_t);
+-
+- /* Copy the mib value from packet to the */
+- memcpy(data + i, skb->data + offset, mib_len);
+-
+- /* Set the offset for the next mib */
+- offset += mib_len;
+- }
+-
+-exit:
+- /* Complete on receiving all the mib packet */
+- if (refcount_dec_and_test(&mib_eth_data->port_parsed))
+- complete(&mib_eth_data->rw_done);
+-}
+-
+-static int
+-qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data)
+-{
+- struct dsa_port *dp = dsa_to_port(ds, port);
+- struct qca8k_mib_eth_data *mib_eth_data;
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- mib_eth_data = &priv->mib_eth_data;
+-
+- mutex_lock(&mib_eth_data->mutex);
+-
+- reinit_completion(&mib_eth_data->rw_done);
+-
+- mib_eth_data->req_port = dp->index;
+- mib_eth_data->data = data;
+- refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS);
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- /* Send mib autocast request */
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
+- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
+- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) |
+- QCA8K_MIB_BUSY);
+-
+- mutex_unlock(&priv->reg_mutex);
+-
+- if (ret)
+- goto exit;
+-
+- ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT);
+-
+-exit:
+- mutex_unlock(&mib_eth_data->mutex);
+-
+- return ret;
+-}
+-
+-static void
+-qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
+- uint64_t *data)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- const struct qca8k_mib_desc *mib;
+- u32 reg, i, val;
+- u32 hi = 0;
+- int ret;
+-
+- if (priv->mgmt_master && priv->info->ops->autocast_mib &&
+- priv->info->ops->autocast_mib(ds, port, data) > 0)
+- return;
+-
+- for (i = 0; i < priv->info->mib_count; i++) {
+- mib = &ar8327_mib[i];
+- reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+-
+- ret = qca8k_read(priv, reg, &val);
+- if (ret < 0)
+- continue;
+-
+- if (mib->size == 2) {
+- ret = qca8k_read(priv, reg + 4, &hi);
+- if (ret < 0)
+- continue;
+- }
+-
+- data[i] = val;
+- if (mib->size == 2)
+- data[i] |= (u64)hi << 32;
+- }
+-}
+-
+-static int
+-qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- if (sset != ETH_SS_STATS)
+- return 0;
+-
+- return priv->info->mib_count;
+-}
+-
+-static int
+-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
+- u32 reg;
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, &reg);
+- if (ret < 0)
+- goto exit;
+-
+- if (eee->eee_enabled)
+- reg |= lpi_en;
+- else
+- reg &= ~lpi_en;
+- ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
+-{
+- /* Nothing to do on the port's MAC */
+- return 0;
+-}
+-
+-static void
+-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u32 stp_state;
+-
+- switch (state) {
+- case BR_STATE_DISABLED:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED;
+- break;
+- case BR_STATE_BLOCKING:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING;
+- break;
+- case BR_STATE_LISTENING:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING;
+- break;
+- case BR_STATE_LEARNING:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
+- break;
+- case BR_STATE_FORWARDING:
+- default:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
+- break;
+- }
+-
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
+-}
+-
+-static int
+-qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int port_mask, cpu_port;
+- int i, ret;
+-
+- cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+- port_mask = BIT(cpu_port);
+-
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- if (dsa_is_cpu_port(ds, i))
+- continue;
+- if (dsa_to_port(ds, i)->bridge_dev != br)
+- continue;
+- /* Add this port to the portvlan mask of the other ports
+- * in the bridge
+- */
+- ret = regmap_set_bits(priv->regmap,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
+- if (ret)
+- return ret;
+- if (i != port)
+- port_mask |= BIT(i);
+- }
+-
+- /* Add all other ports to this ports portvlan mask */
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_MEMBER, port_mask);
+-
+- return ret;
+-}
+-
+-static void
+-qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int cpu_port, i;
+-
+- cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+-
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- if (dsa_is_cpu_port(ds, i))
+- continue;
+- if (dsa_to_port(ds, i)->bridge_dev != br)
+- continue;
+- /* Remove this port to the portvlan mask of the other ports
+- * in the bridge
+- */
+- regmap_clear_bits(priv->regmap,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
+- }
+-
+- /* Set the cpu port to be the only one in the portvlan mask of
+- * this port
+- */
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+-}
+-
+-static void
+-qca8k_port_fast_age(struct dsa_switch *ds, int port)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
+- mutex_unlock(&priv->reg_mutex);
+-}
+-
+-static int
+-qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- unsigned int secs = msecs / 1000;
+- u32 val;
+-
+- /* AGE_TIME reg is set in 7s step */
+- val = secs / 7;
+-
+- /* Handle case with 0 as val to NOT disable
+- * learning
+- */
+- if (!val)
+- val = 1;
+-
+- return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
+- QCA8K_ATU_AGE_TIME(val));
+-}
+-
+-static int
+-qca8k_port_enable(struct dsa_switch *ds, int port,
+- struct phy_device *phy)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+-
+- qca8k_port_set_status(priv, port, 1);
+- priv->port_enabled_map |= BIT(port);
+-
+- if (dsa_is_user_port(ds, port))
+- phy_support_asym_pause(phy);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_port_disable(struct dsa_switch *ds, int port)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+-
+- qca8k_port_set_status(priv, port, 0);
+- priv->port_enabled_map &= ~BIT(port);
+-}
+-
+-static int
+-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- /* We have only have a general MTU setting.
+- * DSA always set the CPU port's MTU to the largest MTU of the slave
+- * ports.
+- * Setting MTU just for the CPU port is sufficient to correctly set a
+- * value for every port.
+- */
+- if (!dsa_is_cpu_port(ds, port))
+- return 0;
+-
+- /* To change the MAX_FRAME_SIZE the cpu ports must be off or
+- * the switch panics.
+- * Turn off both cpu ports before applying the new value to prevent
+- * this.
+- */
+- if (priv->port_enabled_map & BIT(0))
+- qca8k_port_set_status(priv, 0, 0);
+-
+- if (priv->port_enabled_map & BIT(6))
+- qca8k_port_set_status(priv, 6, 0);
+-
+- /* Include L2 header / FCS length */
+- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN);
+-
+- if (priv->port_enabled_map & BIT(0))
+- qca8k_port_set_status(priv, 0, 1);
+-
+- if (priv->port_enabled_map & BIT(6))
+- qca8k_port_set_status(priv, 6, 1);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_port_max_mtu(struct dsa_switch *ds, int port)
+-{
+- return QCA8K_MAX_MTU;
+-}
+-
+-static int
+-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
+- u16 port_mask, u16 vid)
+-{
+- /* Set the vid to the port vlan id if no vid is set */
+- if (!vid)
+- vid = QCA8K_PORT_VID_DEF;
+-
+- return qca8k_fdb_add(priv, addr, port_mask, vid,
+- QCA8K_ATU_STATUS_STATIC);
+-}
+-
+-static int
+-qca8k_port_fdb_add(struct dsa_switch *ds, int port,
+- const unsigned char *addr, u16 vid)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u16 port_mask = BIT(port);
+-
+- return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
+-}
+-
+-static int
+-qca8k_port_fdb_del(struct dsa_switch *ds, int port,
+- const unsigned char *addr, u16 vid)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u16 port_mask = BIT(port);
+-
+- if (!vid)
+- vid = QCA8K_PORT_VID_DEF;
+-
+- return qca8k_fdb_del(priv, addr, port_mask, vid);
+-}
+-
+-static int
+-qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
+- dsa_fdb_dump_cb_t *cb, void *data)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- struct qca8k_fdb _fdb = { 0 };
+- int cnt = QCA8K_NUM_FDB_RECORDS;
+- bool is_static;
+- int ret = 0;
+-
+- mutex_lock(&priv->reg_mutex);
+- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
+- if (!_fdb.aging)
+- break;
+- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
+- ret = cb(_fdb.mac, _fdb.vid, is_static, data);
+- if (ret)
+- break;
+- }
+- mutex_unlock(&priv->reg_mutex);
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_port_mdb_add(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_mdb *mdb)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- const u8 *addr = mdb->addr;
+- u16 vid = mdb->vid;
+-
+- return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
+-}
+-
+-static int
+-qca8k_port_mdb_del(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_mdb *mdb)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- const u8 *addr = mdb->addr;
+- u16 vid = mdb->vid;
+-
+- return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
+-}
+-
+-static int
+-qca8k_port_mirror_add(struct dsa_switch *ds, int port,
+- struct dsa_mall_mirror_tc_entry *mirror,
+- bool ingress)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int monitor_port, ret;
+- u32 reg, val;
+-
+- /* Check for existent entry */
+- if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
+- return -EEXIST;
+-
+- ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
+- if (ret)
+- return ret;
+-
+- /* QCA83xx can have only one port set to mirror mode.
+- * Check that the correct port is requested and return error otherwise.
+- * When no mirror port is set, the values is set to 0xF
+- */
+- monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+- if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
+- return -EEXIST;
+-
+- /* Set the monitor port */
+- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
+- mirror->to_local_port);
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+- if (ret)
+- return ret;
+-
+- if (ingress) {
+- reg = QCA8K_PORT_LOOKUP_CTRL(port);
+- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+- } else {
+- reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+- }
+-
+- ret = regmap_update_bits(priv->regmap, reg, val, val);
+- if (ret)
+- return ret;
+-
+- /* Track mirror port for tx and rx to decide when the
+- * mirror port has to be disabled.
+- */
+- if (ingress)
+- priv->mirror_rx |= BIT(port);
+- else
+- priv->mirror_tx |= BIT(port);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_port_mirror_del(struct dsa_switch *ds, int port,
+- struct dsa_mall_mirror_tc_entry *mirror)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- u32 reg, val;
+- int ret;
+-
+- if (mirror->ingress) {
+- reg = QCA8K_PORT_LOOKUP_CTRL(port);
+- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+- } else {
+- reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+- }
+-
+- ret = regmap_clear_bits(priv->regmap, reg, val);
+- if (ret)
+- goto err;
+-
+- if (mirror->ingress)
+- priv->mirror_rx &= ~BIT(port);
+- else
+- priv->mirror_tx &= ~BIT(port);
+-
+- /* No port set to send packet to mirror port. Disable mirror port */
+- if (!priv->mirror_rx && !priv->mirror_tx) {
+- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+- if (ret)
+- goto err;
+- }
+-err:
+- dev_err(priv->dev, "Failed to del mirror port from %d", port);
+-}
+-
+-static int
+-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+- struct netlink_ext_ack *extack)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- if (vlan_filtering) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+- QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
+- } else {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+- QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
+- }
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_port_vlan_add(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_vlan *vlan,
+- struct netlink_ext_ack *extack)
+-{
+- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
+- if (ret) {
+- dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
+- return ret;
+- }
+-
+- if (pvid) {
+- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
+- QCA8K_EGREES_VLAN_PORT_MASK(port),
+- QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
+- if (ret)
+- return ret;
+-
+- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
+- QCA8K_PORT_VLAN_CVID(vlan->vid) |
+- QCA8K_PORT_VLAN_SVID(vlan->vid));
+- }
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_port_vlan_del(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_vlan *vlan)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- ret = qca8k_vlan_del(priv, port, vlan->vid);
+- if (ret)
+- dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
+-
+- return ret;
+-}
+-
+-static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- /* Communicate to the phy internal driver the switch revision.
+- * Based on the switch revision different values needs to be
+- * set to the dbg and mmd reg on the phy.
+- * The first 2 bit are used to communicate the switch revision
+- * to the phy driver.
+- */
+- if (port > 0 && port < 6)
+- return priv->switch_revision;
+-
+- return 0;
+-}
+-
+-static enum dsa_tag_protocol
+-qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
+- enum dsa_tag_protocol mp)
+-{
+- return DSA_TAG_PROTO_QCA;
+-}
+-
+-static bool
+-qca8k_lag_can_offload(struct dsa_switch *ds,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- struct dsa_port *dp;
+- int id, members = 0;
+-
+- id = dsa_lag_id(ds->dst, lag);
+- if (id < 0 || id >= ds->num_lag_ids)
+- return false;
+-
+- dsa_lag_foreach_port(dp, ds->dst, lag)
+- /* Includes the port joining the LAG */
+- members++;
+-
+- if (members > QCA8K_NUM_PORTS_FOR_LAG)
+- return false;
+-
+- if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+- return false;
+-
+- if (info->hash_type != NETDEV_LAG_HASH_L2 &&
+- info->hash_type != NETDEV_LAG_HASH_L23)
+- return false;
+-
+- return true;
+-}
+-
+-static int
+-qca8k_lag_setup_hash(struct dsa_switch *ds,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- bool unique_lag = true;
+- u32 hash = 0;
+- int i, id;
+-
+- id = dsa_lag_id(ds->dst, lag);
+-
+- switch (info->hash_type) {
+- case NETDEV_LAG_HASH_L23:
+- hash |= QCA8K_TRUNK_HASH_SIP_EN;
+- hash |= QCA8K_TRUNK_HASH_DIP_EN;
+- fallthrough;
+- case NETDEV_LAG_HASH_L2:
+- hash |= QCA8K_TRUNK_HASH_SA_EN;
+- hash |= QCA8K_TRUNK_HASH_DA_EN;
+- break;
+- default: /* We should NEVER reach this */
+- return -EOPNOTSUPP;
+- }
+-
+- /* Check if we are the unique configured LAG */
+- dsa_lags_foreach_id(i, ds->dst)
+- if (i != id && dsa_lag_dev(ds->dst, i)) {
+- unique_lag = false;
+- break;
+- }
+-
+- /* Hash Mode is global. Make sure the same Hash Mode
+- * is set to all the 4 possible lag.
+- * If we are the unique LAG we can set whatever hash
+- * mode we want.
+- * To change hash mode it's needed to remove all LAG
+- * and change the mode with the latest.
+- */
+- if (unique_lag) {
+- priv->lag_hash_mode = hash;
+- } else if (priv->lag_hash_mode != hash) {
+- netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n");
+- return -EOPNOTSUPP;
+- }
+-
+- return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
+- QCA8K_TRUNK_HASH_MASK, hash);
+-}
+-
+-static int
+-qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
+- struct net_device *lag, bool delete)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret, id, i;
+- u32 val;
+-
+- id = dsa_lag_id(ds->dst, lag);
+-
+- /* Read current port member */
+- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
+- if (ret)
+- return ret;
+-
+- /* Shift val to the correct trunk */
+- val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
+- val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
+- if (delete)
+- val &= ~BIT(port);
+- else
+- val |= BIT(port);
+-
+- /* Update port member. With empty portmap disable trunk */
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
+- QCA8K_REG_GOL_TRUNK_MEMBER(id) |
+- QCA8K_REG_GOL_TRUNK_EN(id),
+- !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
+- val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
+-
+- /* Search empty member if adding or port on deleting */
+- for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
+- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
+- if (ret)
+- return ret;
+-
+- val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
+- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
+-
+- if (delete) {
+- /* If port flagged to be disabled assume this member is
+- * empty
+- */
+- if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+- continue;
+-
+- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
+- if (val != port)
+- continue;
+- } else {
+- /* If port flagged to be enabled assume this member is
+- * already set
+- */
+- if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+- continue;
+- }
+-
+- /* We have found the member to add/remove */
+- break;
+- }
+-
+- /* Set port in the correct port mask or disable port if in delete mode */
+- return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
+- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
+- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
+- !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
+- port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
+-}
+-
+-static int
+-qca8k_port_lag_join(struct dsa_switch *ds, int port,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- int ret;
+-
+- if (!qca8k_lag_can_offload(ds, lag, info))
+- return -EOPNOTSUPP;
+-
+- ret = qca8k_lag_setup_hash(ds, lag, info);
+- if (ret)
+- return ret;
+-
+- return qca8k_lag_refresh_portmap(ds, port, lag, false);
+-}
+-
+-static int
+-qca8k_port_lag_leave(struct dsa_switch *ds, int port,
+- struct net_device *lag)
+-{
+- return qca8k_lag_refresh_portmap(ds, port, lag, true);
+-}
+-
+-static void
+-qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
+- bool operational)
+-{
+- struct dsa_port *dp = master->dsa_ptr;
+- struct qca8k_priv *priv = ds->priv;
+-
+- /* Ethernet MIB/MDIO is only supported for CPU port 0 */
+- if (dp->index != 0)
+- return;
+-
+- mutex_lock(&priv->mgmt_eth_data.mutex);
+- mutex_lock(&priv->mib_eth_data.mutex);
+-
+- priv->mgmt_master = operational ? (struct net_device *)master : NULL;
+-
+- mutex_unlock(&priv->mib_eth_data.mutex);
+- mutex_unlock(&priv->mgmt_eth_data.mutex);
+-}
+-
+-static int qca8k_connect_tag_protocol(struct dsa_switch *ds,
+- enum dsa_tag_protocol proto)
+-{
+- struct qca_tagger_data *tagger_data;
+-
+- switch (proto) {
+- case DSA_TAG_PROTO_QCA:
+- tagger_data = ds->tagger_data;
+-
+- tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler;
+- tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler;
+-
+- break;
+- default:
+- return -EOPNOTSUPP;
+- }
+-
+- return 0;
+-}
+-
+-static const struct dsa_switch_ops qca8k_switch_ops = {
+- .get_tag_protocol = qca8k_get_tag_protocol,
+- .setup = qca8k_setup,
+- .get_strings = qca8k_get_strings,
+- .get_ethtool_stats = qca8k_get_ethtool_stats,
+- .get_sset_count = qca8k_get_sset_count,
+- .set_ageing_time = qca8k_set_ageing_time,
+- .get_mac_eee = qca8k_get_mac_eee,
+- .set_mac_eee = qca8k_set_mac_eee,
+- .port_enable = qca8k_port_enable,
+- .port_disable = qca8k_port_disable,
+- .port_change_mtu = qca8k_port_change_mtu,
+- .port_max_mtu = qca8k_port_max_mtu,
+- .port_stp_state_set = qca8k_port_stp_state_set,
+- .port_bridge_join = qca8k_port_bridge_join,
+- .port_bridge_leave = qca8k_port_bridge_leave,
+- .port_fast_age = qca8k_port_fast_age,
+- .port_fdb_add = qca8k_port_fdb_add,
+- .port_fdb_del = qca8k_port_fdb_del,
+- .port_fdb_dump = qca8k_port_fdb_dump,
+- .port_mdb_add = qca8k_port_mdb_add,
+- .port_mdb_del = qca8k_port_mdb_del,
+- .port_mirror_add = qca8k_port_mirror_add,
+- .port_mirror_del = qca8k_port_mirror_del,
+- .port_vlan_filtering = qca8k_port_vlan_filtering,
+- .port_vlan_add = qca8k_port_vlan_add,
+- .port_vlan_del = qca8k_port_vlan_del,
+- .phylink_validate = qca8k_phylink_validate,
+- .phylink_mac_link_state = qca8k_phylink_mac_link_state,
+- .phylink_mac_config = qca8k_phylink_mac_config,
+- .phylink_mac_link_down = qca8k_phylink_mac_link_down,
+- .phylink_mac_link_up = qca8k_phylink_mac_link_up,
+- .get_phy_flags = qca8k_get_phy_flags,
+- .port_lag_join = qca8k_port_lag_join,
+- .port_lag_leave = qca8k_port_lag_leave,
+- .master_state_change = qca8k_master_change,
+- .connect_tag_protocol = qca8k_connect_tag_protocol,
+-};
+-
+-static int qca8k_read_switch_id(struct qca8k_priv *priv)
+-{
+- u32 val;
+- u8 id;
+- int ret;
+-
+- if (!priv->info)
+- return -ENODEV;
+-
+- ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
+- if (ret < 0)
+- return -ENODEV;
+-
+- id = QCA8K_MASK_CTRL_DEVICE_ID(val);
+- if (id != priv->info->id) {
+- dev_err(priv->dev,
+- "Switch id detected %x but expected %x",
+- id, priv->info->id);
+- return -ENODEV;
+- }
+-
+- priv->switch_id = id;
+-
+- /* Save revision to communicate to the internal PHY driver */
+- priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_sw_probe(struct mdio_device *mdiodev)
+-{
+- struct qca8k_priv *priv;
+- int ret;
+-
+- /* allocate the private data struct so that we can probe the switches
+- * ID register
+- */
+- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- priv->info = of_device_get_match_data(priv->dev);
+- priv->bus = mdiodev->bus;
+- priv->dev = &mdiodev->dev;
+-
+- priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset",
+- GPIOD_ASIS);
+- if (IS_ERR(priv->reset_gpio))
+- return PTR_ERR(priv->reset_gpio);
+-
+- if (priv->reset_gpio) {
+- gpiod_set_value_cansleep(priv->reset_gpio, 1);
+- /* The active low duration must be greater than 10 ms
+- * and checkpatch.pl wants 20 ms.
+- */
+- msleep(20);
+- gpiod_set_value_cansleep(priv->reset_gpio, 0);
+- }
+-
+- /* Start by setting up the register mapping */
+- priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv,
+- &qca8k_regmap_config);
+- if (IS_ERR(priv->regmap)) {
+- dev_err(priv->dev, "regmap initialization failed");
+- return PTR_ERR(priv->regmap);
+- }
+-
+- priv->mdio_cache.page = 0xffff;
+- priv->mdio_cache.lo = 0xffff;
+- priv->mdio_cache.hi = 0xffff;
+-
+- /* Check the detected switch id */
+- ret = qca8k_read_switch_id(priv);
+- if (ret)
+- return ret;
+-
+- priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
+- if (!priv->ds)
+- return -ENOMEM;
+-
+- mutex_init(&priv->mgmt_eth_data.mutex);
+- init_completion(&priv->mgmt_eth_data.rw_done);
+-
+- mutex_init(&priv->mib_eth_data.mutex);
+- init_completion(&priv->mib_eth_data.rw_done);
+-
+- priv->ds->dev = &mdiodev->dev;
+- priv->ds->num_ports = QCA8K_NUM_PORTS;
+- priv->ds->priv = priv;
+- priv->ds->ops = &qca8k_switch_ops;
+- mutex_init(&priv->reg_mutex);
+- dev_set_drvdata(&mdiodev->dev, priv);
+-
+- return dsa_register_switch(priv->ds);
+-}
+-
+-static void
+-qca8k_sw_remove(struct mdio_device *mdiodev)
+-{
+- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
+- int i;
+-
+- if (!priv)
+- return;
+-
+- for (i = 0; i < QCA8K_NUM_PORTS; i++)
+- qca8k_port_set_status(priv, i, 0);
+-
+- dsa_unregister_switch(priv->ds);
+-
+- dev_set_drvdata(&mdiodev->dev, NULL);
+-}
+-
+-static void qca8k_sw_shutdown(struct mdio_device *mdiodev)
+-{
+- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
+-
+- if (!priv)
+- return;
+-
+- dsa_switch_shutdown(priv->ds);
+-
+- dev_set_drvdata(&mdiodev->dev, NULL);
+-}
+-
+-#ifdef CONFIG_PM_SLEEP
+-static void
+-qca8k_set_pm(struct qca8k_priv *priv, int enable)
+-{
+- int port;
+-
+- for (port = 0; port < QCA8K_NUM_PORTS; port++) {
+- /* Do not enable on resume if the port was
+- * disabled before.
+- */
+- if (!(priv->port_enabled_map & BIT(port)))
+- continue;
+-
+- qca8k_port_set_status(priv, port, enable);
+- }
+-}
+-
+-static int qca8k_suspend(struct device *dev)
+-{
+- struct qca8k_priv *priv = dev_get_drvdata(dev);
+-
+- qca8k_set_pm(priv, 0);
+-
+- return dsa_switch_suspend(priv->ds);
+-}
+-
+-static int qca8k_resume(struct device *dev)
+-{
+- struct qca8k_priv *priv = dev_get_drvdata(dev);
+-
+- qca8k_set_pm(priv, 1);
+-
+- return dsa_switch_resume(priv->ds);
+-}
+-#endif /* CONFIG_PM_SLEEP */
+-
+-static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+- qca8k_suspend, qca8k_resume);
+-
+-static const struct qca8k_info_ops qca8xxx_ops = {
+- .autocast_mib = qca8k_get_ethtool_stats_eth,
+-};
+-
+-static const struct qca8k_match_data qca8327 = {
+- .id = QCA8K_ID_QCA8327,
+- .reduced_package = true,
+- .mib_count = QCA8K_QCA832X_MIB_COUNT,
+- .ops = &qca8xxx_ops,
+-};
+-
+-static const struct qca8k_match_data qca8328 = {
+- .id = QCA8K_ID_QCA8327,
+- .mib_count = QCA8K_QCA832X_MIB_COUNT,
+- .ops = &qca8xxx_ops,
+-};
+-
+-static const struct qca8k_match_data qca833x = {
+- .id = QCA8K_ID_QCA8337,
+- .mib_count = QCA8K_QCA833X_MIB_COUNT,
+- .ops = &qca8xxx_ops,
+-};
+-
+-static const struct of_device_id qca8k_of_match[] = {
+- { .compatible = "qca,qca8327", .data = &qca8327 },
+- { .compatible = "qca,qca8328", .data = &qca8328 },
+- { .compatible = "qca,qca8334", .data = &qca833x },
+- { .compatible = "qca,qca8337", .data = &qca833x },
+- { /* sentinel */ },
+-};
+-
+-static struct mdio_driver qca8kmdio_driver = {
+- .probe = qca8k_sw_probe,
+- .remove = qca8k_sw_remove,
+- .shutdown = qca8k_sw_shutdown,
+- .mdiodrv.driver = {
+- .name = "qca8k",
+- .of_match_table = qca8k_of_match,
+- .pm = &qca8k_pm_ops,
+- },
+-};
+-
+-mdio_module_driver(qca8kmdio_driver);
+-
+-MODULE_AUTHOR("Mathieu Olivari, John Crispin <john@phrozen.org>");
+-MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family");
+-MODULE_LICENSE("GPL v2");
+-MODULE_ALIAS("platform:qca8k");
+--- /dev/null
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -0,0 +1,3186 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
++ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
++ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2016 John Crispin <john@phrozen.org>
++ */
++
++#include <linux/module.h>
++#include <linux/phy.h>
++#include <linux/netdevice.h>
++#include <linux/bitfield.h>
++#include <linux/regmap.h>
++#include <net/dsa.h>
++#include <linux/of_net.h>
++#include <linux/of_mdio.h>
++#include <linux/of_platform.h>
++#include <linux/if_bridge.h>
++#include <linux/mdio.h>
++#include <linux/phylink.h>
++#include <linux/gpio/consumer.h>
++#include <linux/etherdevice.h>
++#include <linux/dsa/tag_qca.h>
++
++#include "qca8k.h"
++
++static void
++qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
++{
++ regaddr >>= 1;
++ *r1 = regaddr & 0x1e;
++
++ regaddr >>= 5;
++ *r2 = regaddr & 0x7;
++
++ regaddr >>= 3;
++ *page = regaddr & 0x3ff;
++}
++
++static int
++qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo)
++{
++ u16 *cached_lo = &priv->mdio_cache.lo;
++ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ if (lo == *cached_lo)
++ return 0;
++
++ ret = bus->write(bus, phy_id, regnum, lo);
++ if (ret < 0)
++ dev_err_ratelimited(&bus->dev,
++ "failed to write qca8k 32bit lo register\n");
++
++ *cached_lo = lo;
++ return 0;
++}
++
++static int
++qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi)
++{
++ u16 *cached_hi = &priv->mdio_cache.hi;
++ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ if (hi == *cached_hi)
++ return 0;
++
++ ret = bus->write(bus, phy_id, regnum, hi);
++ if (ret < 0)
++ dev_err_ratelimited(&bus->dev,
++ "failed to write qca8k 32bit hi register\n");
++
++ *cached_hi = hi;
++ return 0;
++}
++
++static int
++qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
++{
++ int ret;
++
++ ret = bus->read(bus, phy_id, regnum);
++ if (ret >= 0) {
++ *val = ret;
++ ret = bus->read(bus, phy_id, regnum + 1);
++ *val |= ret << 16;
++ }
++
++ if (ret < 0) {
++ dev_err_ratelimited(&bus->dev,
++ "failed to read qca8k 32bit register\n");
++ *val = 0;
++ return ret;
++ }
++
++ return 0;
++}
++
++static void
++qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val)
++{
++ u16 lo, hi;
++ int ret;
++
++ lo = val & 0xffff;
++ hi = (u16)(val >> 16);
++
++ ret = qca8k_set_lo(priv, phy_id, regnum, lo);
++ if (ret >= 0)
++ ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi);
++}
++
++static int
++qca8k_set_page(struct qca8k_priv *priv, u16 page)
++{
++ u16 *cached_page = &priv->mdio_cache.page;
++ struct mii_bus *bus = priv->bus;
++ int ret;
++
++ if (page == *cached_page)
++ return 0;
++
++ ret = bus->write(bus, 0x18, 0, page);
++ if (ret < 0) {
++ dev_err_ratelimited(&bus->dev,
++ "failed to set qca8k page\n");
++ return ret;
++ }
++
++ *cached_page = page;
++ usleep_range(1000, 2000);
++ return 0;
++}
++
++static int
++qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
++{
++ return regmap_read(priv->regmap, reg, val);
++}
++
++static int
++qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
++{
++ return regmap_write(priv->regmap, reg, val);
++}
++
++static int
++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++{
++ return regmap_update_bits(priv->regmap, reg, mask, write_val);
++}
++
++static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
++{
++ struct qca8k_mgmt_eth_data *mgmt_eth_data;
++ struct qca8k_priv *priv = ds->priv;
++ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++ u8 len, cmd;
++
++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
++ mgmt_eth_data = &priv->mgmt_eth_data;
++
++ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
++ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
++
++ /* Make sure the seq match the requested packet */
++ if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
++ mgmt_eth_data->ack = true;
++
++ if (cmd == MDIO_READ) {
++ mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
++
++ /* Get the rest of the 12 byte of data.
++ * The read/write function will extract the requested data.
++ */
++ if (len > QCA_HDR_MGMT_DATA1_LEN)
++ memcpy(mgmt_eth_data->data + 1, skb->data,
++ QCA_HDR_MGMT_DATA2_LEN);
++ }
++
++ complete(&mgmt_eth_data->rw_done);
++}
++
++static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val,
++ int priority, unsigned int len)
++{
++ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++ unsigned int real_len;
++ struct sk_buff *skb;
++ u32 *data2;
++ u16 hdr;
++
++ skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
++ if (!skb)
++ return NULL;
++
++ /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte
++ * Actually for some reason the steps are:
++ * 0: nothing
++ * 1-4: first 4 byte
++ * 5-6: first 12 byte
++ * 7-15: all 16 byte
++ */
++ if (len == 16)
++ real_len = 15;
++ else
++ real_len = len;
++
++ skb_reset_mac_header(skb);
++ skb_set_network_header(skb, skb->len);
++
++ mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN);
++
++ hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION);
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority);
++ hdr |= QCA_HDR_XMIT_FROM_CPU;
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
++ hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
++
++ mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
++ QCA_HDR_MGMT_CHECK_CODE_VAL);
++
++ if (cmd == MDIO_WRITE)
++ mgmt_ethhdr->mdio_data = *val;
++
++ mgmt_ethhdr->hdr = htons(hdr);
++
++ data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
++ if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN)
++ memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
++
++ return skb;
++}
++
++static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
++{
++ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++
++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
++ mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
++}
++
++static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
++ struct sk_buff *skb;
++ bool ack;
++ int ret;
++
++ skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL,
++ QCA8K_ETHERNET_MDIO_PRIORITY, len);
++ if (!skb)
++ return -ENOMEM;
++
++ mutex_lock(&mgmt_eth_data->mutex);
++
++ /* Check mgmt_master if is operational */
++ if (!priv->mgmt_master) {
++ kfree_skb(skb);
++ mutex_unlock(&mgmt_eth_data->mutex);
++ return -EINVAL;
++ }
++
++ skb->dev = priv->mgmt_master;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the mdio pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
++
++ *val = mgmt_eth_data->data[0];
++ if (len > QCA_HDR_MGMT_DATA1_LEN)
++ memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN);
++
++ ack = mgmt_eth_data->ack;
++
++ mutex_unlock(&mgmt_eth_data->mutex);
++
++ if (ret <= 0)
++ return -ETIMEDOUT;
++
++ if (!ack)
++ return -EINVAL;
++
++ return 0;
++}
++
++static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
++ struct sk_buff *skb;
++ bool ack;
++ int ret;
++
++ skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val,
++ QCA8K_ETHERNET_MDIO_PRIORITY, len);
++ if (!skb)
++ return -ENOMEM;
++
++ mutex_lock(&mgmt_eth_data->mutex);
++
++ /* Check mgmt_master if is operational */
++ if (!priv->mgmt_master) {
++ kfree_skb(skb);
++ mutex_unlock(&mgmt_eth_data->mutex);
++ return -EINVAL;
++ }
++
++ skb->dev = priv->mgmt_master;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the mdio pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
++
++ ack = mgmt_eth_data->ack;
++
++ mutex_unlock(&mgmt_eth_data->mutex);
++
++ if (ret <= 0)
++ return -ETIMEDOUT;
++
++ if (!ack)
++ return -EINVAL;
++
++ return 0;
++}
++
++static int
++qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++{
++ u32 val = 0;
++ int ret;
++
++ ret = qca8k_read_eth(priv, reg, &val, sizeof(val));
++ if (ret)
++ return ret;
++
++ val &= ~mask;
++ val |= write_val;
++
++ return qca8k_write_eth(priv, reg, &val, sizeof(val));
++}
++
++static int
++qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ int i, count = len / sizeof(u32), ret;
++
++ if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len))
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ ret = regmap_read(priv->regmap, reg + (i * 4), val + i);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int
++qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ int i, count = len / sizeof(u32), ret;
++ u32 tmp;
++
++ if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len))
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ tmp = val[i];
++
++ ret = regmap_write(priv->regmap, reg + (i * 4), tmp);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int
++qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ int ret;
++
++ if (!qca8k_read_eth(priv, reg, val, sizeof(*val)))
++ return 0;
++
++ qca8k_split_addr(reg, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val);
++
++exit:
++ mutex_unlock(&bus->mdio_lock);
++ return ret;
++}
++
++static int
++qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ int ret;
++
++ if (!qca8k_write_eth(priv, reg, &val, sizeof(val)))
++ return 0;
++
++ qca8k_split_addr(reg, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret < 0)
++ goto exit;
++
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++
++exit:
++ mutex_unlock(&bus->mdio_lock);
++ return ret;
++}
++
++static int
++qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ u32 val;
++ int ret;
++
++ if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
++ return 0;
++
++ qca8k_split_addr(reg, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
++ if (ret < 0)
++ goto exit;
++
++ val &= ~mask;
++ val |= write_val;
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++
++exit:
++ mutex_unlock(&bus->mdio_lock);
++
++ return ret;
++}
++
++static const struct regmap_range qca8k_readable_ranges[] = {
++ regmap_reg_range(0x0000, 0x00e4), /* Global control */
++ regmap_reg_range(0x0100, 0x0168), /* EEE control */
++ regmap_reg_range(0x0200, 0x0270), /* Parser control */
++ regmap_reg_range(0x0400, 0x0454), /* ACL */
++ regmap_reg_range(0x0600, 0x0718), /* Lookup */
++ regmap_reg_range(0x0800, 0x0b70), /* QM */
++ regmap_reg_range(0x0c00, 0x0c80), /* PKT */
++ regmap_reg_range(0x0e00, 0x0e98), /* L3 */
++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */
++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */
++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */
++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */
++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */
++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */
++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */
++
++};
++
++static const struct regmap_access_table qca8k_readable_table = {
++ .yes_ranges = qca8k_readable_ranges,
++ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
++};
++
++static struct regmap_config qca8k_regmap_config = {
++ .reg_bits = 16,
++ .val_bits = 32,
++ .reg_stride = 4,
++ .max_register = 0x16ac, /* end MIB - Port6 range */
++ .reg_read = qca8k_regmap_read,
++ .reg_write = qca8k_regmap_write,
++ .reg_update_bits = qca8k_regmap_update_bits,
++ .rd_table = &qca8k_readable_table,
++ .disable_locking = true, /* Locking is handled by qca8k read/write */
++ .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */
++};
++
++static int
++qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
++{
++ u32 val;
++
++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
++}
++
++static int
++qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
++{
++ u32 reg[3];
++ int ret;
++
++ /* load the ARL table into an array */
++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++ if (ret)
++ return ret;
++
++ /* vid - 83:72 */
++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
++ /* aging - 67:64 */
++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
++ /* portmask - 54:48 */
++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
++ /* mac - 47:0 */
++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
++
++ return 0;
++}
++
++static void
++qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
++ u8 aging)
++{
++ u32 reg[3] = { 0 };
++
++ /* vid - 83:72 */
++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
++ /* aging - 67:64 */
++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
++ /* portmask - 54:48 */
++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
++ /* mac - 47:0 */
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
++
++ /* load the array into the ARL table */
++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++}
++
++static int
++qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
++{
++ u32 reg;
++ int ret;
++
++ /* Set the command and FDB index */
++ reg = QCA8K_ATU_FUNC_BUSY;
++ reg |= cmd;
++ if (port >= 0) {
++ reg |= QCA8K_ATU_FUNC_PORT_EN;
++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
++ }
++
++ /* Write the function register triggering the table access */
++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
++ if (ret)
++ return ret;
++
++ /* wait for completion */
++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY);
++ if (ret)
++ return ret;
++
++ /* Check for table full violation when adding an entry */
++ if (cmd == QCA8K_FDB_LOAD) {
++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, &reg);
++ if (ret < 0)
++ return ret;
++ if (reg & QCA8K_ATU_FUNC_FULL)
++ return -1;
++ }
++
++ return 0;
++}
++
++static int
++qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
++{
++ int ret;
++
++ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
++ if (ret < 0)
++ return ret;
++
++ return qca8k_fdb_read(priv, fdb);
++}
++
++static int
++qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask,
++ u16 vid, u8 aging)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_write(priv, vid, port_mask, mac, aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int
++qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_write(priv, vid, port_mask, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static void
++qca8k_fdb_flush(struct qca8k_priv *priv)
++{
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
++ mutex_unlock(&priv->reg_mutex);
++}
++
++static int
++qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_fdb_read(priv, &fdb);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule exist. Delete first */
++ if (!fdb.aging) {
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++ }
++
++ /* Add port to fdb portmask */
++ fdb.port_mask |= port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
++qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule doesn't exist. Why delete? */
++ if (!fdb.aging) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++
++ /* Only port in the rule is this port. Don't re insert */
++ if (fdb.port_mask == port_mask)
++ goto exit;
++
++ /* Remove port from port mask */
++ fdb.port_mask &= ~port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
++qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
++{
++ u32 reg;
++ int ret;
++
++ /* Set the command and VLAN index */
++ reg = QCA8K_VTU_FUNC1_BUSY;
++ reg |= cmd;
++ reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
++
++ /* Write the function register triggering the table access */
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
++ if (ret)
++ return ret;
++
++ /* wait for completion */
++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
++ if (ret)
++ return ret;
++
++ /* Check for table full violation when adding an entry */
++ if (cmd == QCA8K_VLAN_LOAD) {
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, &reg);
++ if (ret < 0)
++ return ret;
++ if (reg & QCA8K_VTU_FUNC1_FULL)
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static int
++qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
++{
++ u32 reg;
++ int ret;
++
++ /*
++ We do the right thing with VLAN 0 and treat it as untagged while
++ preserving the tag on egress.
++ */
++ if (vid == 0)
++ return 0;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
++ if (ret < 0)
++ goto out;
++
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
++ if (ret < 0)
++ goto out;
++ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++ if (untagged)
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
++ else
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
++
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ if (ret)
++ goto out;
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
++
++out:
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int
++qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
++{
++ u32 reg, mask;
++ int ret, i;
++ bool del;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
++ if (ret < 0)
++ goto out;
++
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
++ if (ret < 0)
++ goto out;
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
++
++ /* Check if we're the last member to be removed */
++ del = true;
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
++
++ if ((reg & mask) != mask) {
++ del = false;
++ break;
++ }
++ }
++
++ if (del) {
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
++ } else {
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ if (ret)
++ goto out;
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
++ }
++
++out:
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int
++qca8k_mib_init(struct qca8k_priv *priv)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) |
++ QCA8K_MIB_BUSY);
++ if (ret)
++ goto exit;
++
++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
++ if (ret)
++ goto exit;
++
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
++ if (ret)
++ goto exit;
++
++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static void
++qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
++{
++ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
++
++ /* Port 0 and 6 have no internal PHY */
++ if (port > 0 && port < 6)
++ mask |= QCA8K_PORT_STATUS_LINK_AUTO;
++
++ if (enable)
++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
++ else
++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
++}
++
++static int
++qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
++ struct sk_buff *read_skb, u32 *val)
++{
++ struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL);
++ bool ack;
++ int ret;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the copy pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ ack = mgmt_eth_data->ack;
++
++ if (ret <= 0)
++ return -ETIMEDOUT;
++
++ if (!ack)
++ return -EINVAL;
++
++ *val = mgmt_eth_data->data[0];
++
++ return 0;
++}
++
++static int
++qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy,
++ int regnum, u16 data)
++{
++ struct sk_buff *write_skb, *clear_skb, *read_skb;
++ struct qca8k_mgmt_eth_data *mgmt_eth_data;
++ u32 write_val, clear_val = 0, val;
++ struct net_device *mgmt_master;
++ int ret, ret1;
++ bool ack;
++
++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
++ return -EINVAL;
++
++ mgmt_eth_data = &priv->mgmt_eth_data;
++
++ write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
++ QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
++ QCA8K_MDIO_MASTER_REG_ADDR(regnum);
++
++ if (read) {
++ write_val |= QCA8K_MDIO_MASTER_READ;
++ } else {
++ write_val |= QCA8K_MDIO_MASTER_WRITE;
++ write_val |= QCA8K_MDIO_MASTER_DATA(data);
++ }
++
++ /* Prealloc all the needed skb before the lock */
++ write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val,
++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val));
++ if (!write_skb)
++ return -ENOMEM;
++
++ clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val,
++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
++ if (!clear_skb) {
++ ret = -ENOMEM;
++ goto err_clear_skb;
++ }
++
++ read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val,
++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val));
++ if (!read_skb) {
++ ret = -ENOMEM;
++ goto err_read_skb;
++ }
++
++ /* Actually start the request:
++ * 1. Send mdio master packet
++ * 2. Busy Wait for mdio master command
++ * 3. Get the data if we are reading
++ * 4. Reset the mdio master (even with error)
++ */
++ mutex_lock(&mgmt_eth_data->mutex);
++
++ /* Check if mgmt_master is operational */
++ mgmt_master = priv->mgmt_master;
++ if (!mgmt_master) {
++ mutex_unlock(&mgmt_eth_data->mutex);
++ ret = -EINVAL;
++ goto err_mgmt_master;
++ }
++
++ read_skb->dev = mgmt_master;
++ clear_skb->dev = mgmt_master;
++ write_skb->dev = mgmt_master;
++
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the write pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(write_skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ ack = mgmt_eth_data->ack;
++
++ if (ret <= 0) {
++ ret = -ETIMEDOUT;
++ kfree_skb(read_skb);
++ goto exit;
++ }
++
++ if (!ack) {
++ ret = -EINVAL;
++ kfree_skb(read_skb);
++ goto exit;
++ }
++
++ ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1,
++ !(val & QCA8K_MDIO_MASTER_BUSY), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
++ mgmt_eth_data, read_skb, &val);
++
++ if (ret < 0 && ret1 < 0) {
++ ret = ret1;
++ goto exit;
++ }
++
++ if (read) {
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the read pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(read_skb);
++
++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ ack = mgmt_eth_data->ack;
++
++ if (ret <= 0) {
++ ret = -ETIMEDOUT;
++ goto exit;
++ }
++
++ if (!ack) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK;
++ } else {
++ kfree_skb(read_skb);
++ }
++exit:
++ reinit_completion(&mgmt_eth_data->rw_done);
++
++ /* Increment seq_num and set it in the clear pkt */
++ mgmt_eth_data->seq++;
++ qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq);
++ mgmt_eth_data->ack = false;
++
++ dev_queue_xmit(clear_skb);
++
++ wait_for_completion_timeout(&mgmt_eth_data->rw_done,
++ QCA8K_ETHERNET_TIMEOUT);
++
++ mutex_unlock(&mgmt_eth_data->mutex);
++
++ return ret;
++
++ /* Error handling before lock */
++err_mgmt_master:
++ kfree_skb(read_skb);
++err_read_skb:
++ kfree_skb(clear_skb);
++err_clear_skb:
++ kfree_skb(write_skb);
++
++ return ret;
++}
++
++static u32
++qca8k_port_to_phy(int port)
++{
++ /* From Andrew Lunn:
++ * Port 0 has no internal phy.
++ * Port 1 has an internal PHY at MDIO address 0.
++ * Port 2 has an internal PHY at MDIO address 1.
++ * ...
++ * Port 5 has an internal PHY at MDIO address 4.
++ * Port 6 has no internal PHY.
++ */
++
++ return port - 1;
++}
++
++static int
++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)
++{
++ u16 r1, r2, page;
++ u32 val;
++ int ret, ret1;
++
++ qca8k_split_addr(reg, &r1, &r2, &page);
++
++ ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
++ bus, 0x10 | r2, r1, &val);
++
++ /* Check if qca8k_read has failed for a different reason
++ * before returnting -ETIMEDOUT
++ */
++ if (ret < 0 && ret1 < 0)
++ return ret1;
++
++ return ret;
++}
++
++static int
++qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data)
++{
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ u32 val;
++ int ret;
++
++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
++ return -EINVAL;
++
++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
++ QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
++ QCA8K_MDIO_MASTER_REG_ADDR(regnum) |
++ QCA8K_MDIO_MASTER_DATA(data);
++
++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret)
++ goto exit;
++
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++
++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_BUSY);
++
++exit:
++ /* even if the busy_wait timeouts try to clear the MASTER_EN */
++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
++
++ mutex_unlock(&bus->mdio_lock);
++
++ return ret;
++}
++
++static int
++qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum)
++{
++ struct mii_bus *bus = priv->bus;
++ u16 r1, r2, page;
++ u32 val;
++ int ret;
++
++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG)
++ return -EINVAL;
++
++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN |
++ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) |
++ QCA8K_MDIO_MASTER_REG_ADDR(regnum);
++
++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page);
++
++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
++
++ ret = qca8k_set_page(priv, page);
++ if (ret)
++ goto exit;
++
++ qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++
++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_BUSY);
++ if (ret)
++ goto exit;
++
++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
++
++exit:
++ /* even if the busy_wait timeouts try to clear the MASTER_EN */
++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
++
++ mutex_unlock(&bus->mdio_lock);
++
++ if (ret >= 0)
++ ret = val & QCA8K_MDIO_MASTER_DATA_MASK;
++
++ return ret;
++}
++
++static int
++qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data)
++{
++ struct qca8k_priv *priv = slave_bus->priv;
++ int ret;
++
++ /* Use mdio Ethernet when available, fallback to legacy one on error */
++ ret = qca8k_phy_eth_command(priv, false, phy, regnum, data);
++ if (!ret)
++ return 0;
++
++ return qca8k_mdio_write(priv, phy, regnum, data);
++}
++
++static int
++qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum)
++{
++ struct qca8k_priv *priv = slave_bus->priv;
++ int ret;
++
++ /* Use mdio Ethernet when available, fallback to legacy one on error */
++ ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0);
++ if (ret >= 0)
++ return ret;
++
++ ret = qca8k_mdio_read(priv, phy, regnum);
++
++ if (ret < 0)
++ return 0xffff;
++
++ return ret;
++}
++
++static int
++qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data)
++{
++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
++
++ return qca8k_internal_mdio_write(slave_bus, port, regnum, data);
++}
++
++static int
++qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum)
++{
++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR;
++
++ return qca8k_internal_mdio_read(slave_bus, port, regnum);
++}
++
++static int
++qca8k_mdio_register(struct qca8k_priv *priv)
++{
++ struct dsa_switch *ds = priv->ds;
++ struct device_node *mdio;
++ struct mii_bus *bus;
++
++ bus = devm_mdiobus_alloc(ds->dev);
++ if (!bus)
++ return -ENOMEM;
++
++ bus->priv = (void *)priv;
++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d",
++ ds->dst->index, ds->index);
++ bus->parent = ds->dev;
++ bus->phy_mask = ~ds->phys_mii_mask;
++ ds->slave_mii_bus = bus;
++
++ /* Check if the devicetree declare the port:phy mapping */
++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio");
++ if (of_device_is_available(mdio)) {
++ bus->name = "qca8k slave mii";
++ bus->read = qca8k_internal_mdio_read;
++ bus->write = qca8k_internal_mdio_write;
++ return devm_of_mdiobus_register(priv->dev, bus, mdio);
++ }
++
++ /* If a mapping can't be found the legacy mapping is used,
++ * using the qca8k_port_to_phy function
++ */
++ bus->name = "qca8k-legacy slave mii";
++ bus->read = qca8k_legacy_mdio_read;
++ bus->write = qca8k_legacy_mdio_write;
++ return devm_mdiobus_register(priv->dev, bus);
++}
++
++static int
++qca8k_setup_mdio_bus(struct qca8k_priv *priv)
++{
++ u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg;
++ struct device_node *ports, *port;
++ phy_interface_t mode;
++ int err;
++
++ ports = of_get_child_by_name(priv->dev->of_node, "ports");
++ if (!ports)
++ ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports");
++
++ if (!ports)
++ return -EINVAL;
++
++ for_each_available_child_of_node(ports, port) {
++ err = of_property_read_u32(port, "reg", &reg);
++ if (err) {
++ of_node_put(port);
++ of_node_put(ports);
++ return err;
++ }
++
++ if (!dsa_is_user_port(priv->ds, reg))
++ continue;
++
++ of_get_phy_mode(port, &mode);
++
++ if (of_property_read_bool(port, "phy-handle") &&
++ mode != PHY_INTERFACE_MODE_INTERNAL)
++ external_mdio_mask |= BIT(reg);
++ else
++ internal_mdio_mask |= BIT(reg);
++ }
++
++ of_node_put(ports);
++ if (!external_mdio_mask && !internal_mdio_mask) {
++ dev_err(priv->dev, "no PHYs are defined.\n");
++ return -EINVAL;
++ }
++
++ /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through
++ * the MDIO_MASTER register also _disconnects_ the external MDC
++ * passthrough to the internal PHYs. It's not possible to use both
++ * configurations at the same time!
++ *
++ * Because this came up during the review process:
++ * If the external mdio-bus driver is capable magically disabling
++ * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's
++ * accessors for the time being, it would be possible to pull this
++ * off.
++ */
++ if (!!external_mdio_mask && !!internal_mdio_mask) {
++ dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n");
++ return -EINVAL;
++ }
++
++ if (external_mdio_mask) {
++ /* Make sure to disable the internal mdio bus in cases
++ * a dt-overlay and driver reload changed the configuration
++ */
++
++ return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL,
++ QCA8K_MDIO_MASTER_EN);
++ }
++
++ return qca8k_mdio_register(priv);
++}
++
++static int
++qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
++{
++ u32 mask = 0;
++ int ret = 0;
++
++ /* SoC specific settings for ipq8064.
++ * If more device require this consider adding
++ * a dedicated binding.
++ */
++ if (of_machine_is_compatible("qcom,ipq8064"))
++ mask |= QCA8K_MAC_PWR_RGMII0_1_8V;
++
++ /* SoC specific settings for ipq8065 */
++ if (of_machine_is_compatible("qcom,ipq8065"))
++ mask |= QCA8K_MAC_PWR_RGMII1_1_8V;
++
++ if (mask) {
++ ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL,
++ QCA8K_MAC_PWR_RGMII0_1_8V |
++ QCA8K_MAC_PWR_RGMII1_1_8V,
++ mask);
++ }
++
++ return ret;
++}
++
++static int qca8k_find_cpu_port(struct dsa_switch *ds)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Find the connected cpu port. Valid port are 0 or 6 */
++ if (dsa_is_cpu_port(ds, 0))
++ return 0;
++
++ dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
++
++ if (dsa_is_cpu_port(ds, 6))
++ return 6;
++
++ return -EINVAL;
++}
++
++static int
++qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
++{
++ const struct qca8k_match_data *data = priv->info;
++ struct device_node *node = priv->dev->of_node;
++ u32 val = 0;
++ int ret;
++
++ /* QCA8327 require to set to the correct mode.
++ * His bigger brother QCA8328 have the 172 pin layout.
++ * Should be applied by default but we set this just to make sure.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ /* Set the correct package of 148 pin for QCA8327 */
++ if (data->reduced_package)
++ val |= QCA8327_PWS_PACKAGE148_EN;
++
++ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN,
++ val);
++ if (ret)
++ return ret;
++ }
++
++ if (of_property_read_bool(node, "qca,ignore-power-on-sel"))
++ val |= QCA8K_PWS_POWER_ON_SEL;
++
++ if (of_property_read_bool(node, "qca,led-open-drain")) {
++ if (!(val & QCA8K_PWS_POWER_ON_SEL)) {
++ dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set.");
++ return -EINVAL;
++ }
++
++ val |= QCA8K_PWS_LED_OPEN_EN_CSR;
++ }
++
++ return qca8k_rmw(priv, QCA8K_REG_PWS,
++ QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL,
++ val);
++}
++
++static int
++qca8k_parse_port_config(struct qca8k_priv *priv)
++{
++ int port, cpu_port_index = -1, ret;
++ struct device_node *port_dn;
++ phy_interface_t mode;
++ struct dsa_port *dp;
++ u32 delay;
++
++ /* We have 2 CPU port. Check them */
++ for (port = 0; port < QCA8K_NUM_PORTS; port++) {
++ /* Skip every other port */
++ if (port != 0 && port != 6)
++ continue;
++
++ dp = dsa_to_port(priv->ds, port);
++ port_dn = dp->dn;
++ cpu_port_index++;
++
++ if (!of_device_is_available(port_dn))
++ continue;
++
++ ret = of_get_phy_mode(port_dn, &mode);
++ if (ret)
++ continue;
++
++ switch (mode) {
++ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ case PHY_INTERFACE_MODE_SGMII:
++ delay = 0;
++
++ if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay))
++ /* Switch regs accept value in ns, convert ps to ns */
++ delay = delay / 1000;
++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_TXID)
++ delay = 1;
++
++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) {
++ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");
++ delay = 3;
++ }
++
++ priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay;
++
++ delay = 0;
++
++ if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay))
++ /* Switch regs accept value in ns, convert ps to ns */
++ delay = delay / 1000;
++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_RXID)
++ delay = 2;
++
++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) {
++ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");
++ delay = 3;
++ }
++
++ priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay;
++
++ /* Skip sgmii parsing for rgmii* mode */
++ if (mode == PHY_INTERFACE_MODE_RGMII ||
++ mode == PHY_INTERFACE_MODE_RGMII_ID ||
++ mode == PHY_INTERFACE_MODE_RGMII_TXID ||
++ mode == PHY_INTERFACE_MODE_RGMII_RXID)
++ break;
++
++ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge"))
++ priv->ports_config.sgmii_tx_clk_falling_edge = true;
++
++ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge"))
++ priv->ports_config.sgmii_rx_clk_falling_edge = true;
++
++ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) {
++ priv->ports_config.sgmii_enable_pll = true;
++
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling");
++ priv->ports_config.sgmii_enable_pll = false;
++ }
++
++ if (priv->switch_revision < 2)
++ dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more.");
++ }
++
++ break;
++ default:
++ continue;
++ }
++ }
++
++ return 0;
++}
++
++static int
++qca8k_setup(struct dsa_switch *ds)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ int cpu_port, ret, i;
++ u32 mask;
++
++ cpu_port = qca8k_find_cpu_port(ds);
++ if (cpu_port < 0) {
++ dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
++ return cpu_port;
++ }
++
++ /* Parse CPU port config to be later used in phy_link mac_config */
++ ret = qca8k_parse_port_config(priv);
++ if (ret)
++ return ret;
++
++ ret = qca8k_setup_mdio_bus(priv);
++ if (ret)
++ return ret;
++
++ ret = qca8k_setup_of_pws_reg(priv);
++ if (ret)
++ return ret;
++
++ ret = qca8k_setup_mac_pwr_sel(priv);
++ if (ret)
++ return ret;
++
++ /* Make sure MAC06 is disabled */
++ ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL,
++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);
++ if (ret) {
++ dev_err(priv->dev, "failed disabling MAC06 exchange");
++ return ret;
++ }
++
++ /* Enable CPU Port */
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);
++ if (ret) {
++ dev_err(priv->dev, "failed enabling CPU port");
++ return ret;
++ }
++
++ /* Enable MIB counters */
++ ret = qca8k_mib_init(priv);
++ if (ret)
++ dev_warn(priv->dev, "mib init failed");
++
++ /* Initial setup of all ports */
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ /* Disable forwarding by default on all ports */
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_MEMBER, 0);
++ if (ret)
++ return ret;
++
++ /* Enable QCA header mode on all cpu ports */
++ if (dsa_is_cpu_port(ds, i)) {
++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i),
++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) |
++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));
++ if (ret) {
++ dev_err(priv->dev, "failed enabling QCA header mode");
++ return ret;
++ }
++ }
++
++ /* Disable MAC by default on all user ports */
++ if (dsa_is_user_port(ds, i))
++ qca8k_port_set_status(priv, i, 0);
++ }
++
++ /* Forward all unknown frames to CPU port for Linux processing
++ * Notice that in multi-cpu config only one port should be set
++ * for igmp, unknown, multicast and broadcast packet
++ */
++ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) |
++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));
++ if (ret)
++ return ret;
++
++ /* Setup connection between CPU port & user ports
++ * Configure specific switch configuration for ports
++ */
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ /* CPU port gets connected to all user ports of the switch */
++ if (dsa_is_cpu_port(ds, i)) {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
++ if (ret)
++ return ret;
++ }
++
++ /* Individual user ports get connected to CPU port only */
++ if (dsa_is_user_port(ds, i)) {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_MEMBER,
++ BIT(cpu_port));
++ if (ret)
++ return ret;
++
++ /* Enable ARP Auto-learning by default */
++ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
++ QCA8K_PORT_LOOKUP_LEARN);
++ if (ret)
++ return ret;
++
++ /* For port based vlans to work we need to set the
++ * default egress vid
++ */
++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i),
++ QCA8K_EGREES_VLAN_PORT_MASK(i),
++ QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));
++ if (ret)
++ return ret;
++
++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i),
++ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) |
++ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF));
++ if (ret)
++ return ret;
++ }
++
++ /* The port 5 of the qca8337 have some problem in flood condition. The
++ * original legacy driver had some specific buffer and priority settings
++ * for the different port suggested by the QCA switch team. Add this
++ * missing settings to improve switch stability under load condition.
++ * This problem is limited to qca8337 and other qca8k switch are not affected.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8337) {
++ switch (i) {
++ /* The 2 CPU port and port 5 requires some different
++ * priority than any other ports.
++ */
++ case 0:
++ case 5:
++ case 6:
++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) |
++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e);
++ break;
++ default:
++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) |
++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) |
++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19);
++ }
++ qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask);
++
++ mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) |
++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_WRED_EN;
++ qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i),
++ QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |
++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |
++ QCA8K_PORT_HOL_CTRL1_WRED_EN,
++ mask);
++ }
++ }
++
++ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */
++ if (priv->switch_id == QCA8K_ID_QCA8327) {
++ mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |
++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);
++ qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH,
++ QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK |
++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,
++ mask);
++ }
++
++ /* Setup our port MTUs to match power on defaults */
++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN);
++ if (ret)
++ dev_warn(priv->dev, "failed setting MTU settings");
++
++ /* Flush the FDB table */
++ qca8k_fdb_flush(priv);
++
++ /* We don't have interrupts for link changes, so we need to poll */
++ ds->pcs_poll = true;
++
++ /* Set min a max ageing value supported */
++ ds->ageing_time_min = 7000;
++ ds->ageing_time_max = 458745000;
++
++ /* Set max number of LAGs supported */
++ ds->num_lag_ids = QCA8K_NUM_LAGS;
++
++ return 0;
++}
++
++static void
++qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index,
++ u32 reg)
++{
++ u32 delay, val = 0;
++ int ret;
++
++ /* Delay can be declared in 3 different way.
++ * Mode to rgmii and internal-delay standard binding defined
++ * rgmii-id or rgmii-tx/rx phy mode set.
++ * The parse logic set a delay different than 0 only when one
++ * of the 3 different way is used. In all other case delay is
++ * not enabled. With ID or TX/RXID delay is enabled and set
++ * to the default and recommended value.
++ */
++ if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) {
++ delay = priv->ports_config.rgmii_tx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN;
++ }
++
++ if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) {
++ delay = priv->ports_config.rgmii_rx_delay[cpu_port_index];
++
++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN;
++ }
++
++ /* Set RGMII delay based on the selected values */
++ ret = qca8k_rmw(priv, reg,
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK |
++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN |
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN,
++ val);
++ if (ret)
++ dev_err(priv->dev, "Failed to set internal delay for CPU port%d",
++ cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6);
++}
++
++static void
++qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
++ const struct phylink_link_state *state)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int cpu_port_index, ret;
++ u32 reg, val;
++
++ switch (port) {
++ case 0: /* 1st CPU port */
++ if (state->interface != PHY_INTERFACE_MODE_RGMII &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
++ state->interface != PHY_INTERFACE_MODE_SGMII)
++ return;
++
++ reg = QCA8K_REG_PORT0_PAD_CTRL;
++ cpu_port_index = QCA8K_CPU_PORT0;
++ break;
++ case 1:
++ case 2:
++ case 3:
++ case 4:
++ case 5:
++ /* Internal PHY, nothing to do */
++ return;
++ case 6: /* 2nd CPU port / external PHY */
++ if (state->interface != PHY_INTERFACE_MODE_RGMII &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
++ state->interface != PHY_INTERFACE_MODE_SGMII &&
++ state->interface != PHY_INTERFACE_MODE_1000BASEX)
++ return;
++
++ reg = QCA8K_REG_PORT6_PAD_CTRL;
++ cpu_port_index = QCA8K_CPU_PORT6;
++ break;
++ default:
++ dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port);
++ return;
++ }
++
++ if (port != 6 && phylink_autoneg_inband(mode)) {
++ dev_err(ds->dev, "%s: in-band negotiation unsupported\n",
++ __func__);
++ return;
++ }
++
++ switch (state->interface) {
++ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN);
++
++ /* Configure rgmii delay */
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
++
++ /* QCA8337 requires to set rgmii rx delay for all ports.
++ * This is enabled through PORT5_PAD_CTRL for all ports,
++ * rather than individual port registers.
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8337)
++ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL,
++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN);
++ break;
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_1000BASEX:
++ /* Enable SGMII on the port */
++ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN);
++
++ /* Enable/disable SerDes auto-negotiation as necessary */
++ ret = qca8k_read(priv, QCA8K_REG_PWS, &val);
++ if (ret)
++ return;
++ if (phylink_autoneg_inband(mode))
++ val &= ~QCA8K_PWS_SERDES_AEN_DIS;
++ else
++ val |= QCA8K_PWS_SERDES_AEN_DIS;
++ qca8k_write(priv, QCA8K_REG_PWS, val);
++
++ /* Configure the SGMII parameters */
++ ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
++ if (ret)
++ return;
++
++ val |= QCA8K_SGMII_EN_SD;
++
++ if (priv->ports_config.sgmii_enable_pll)
++ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX |
++ QCA8K_SGMII_EN_TX;
++
++ if (dsa_is_cpu_port(ds, port)) {
++ /* CPU port, we're talking to the CPU MAC, be a PHY */
++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
++ val |= QCA8K_SGMII_MODE_CTRL_PHY;
++ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
++ val |= QCA8K_SGMII_MODE_CTRL_MAC;
++ } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK;
++ val |= QCA8K_SGMII_MODE_CTRL_BASEX;
++ }
++
++ qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val);
++
++ /* From original code is reported port instability as SGMII also
++ * require delay set. Apply advised values here or take them from DT.
++ */
++ if (state->interface == PHY_INTERFACE_MODE_SGMII)
++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg);
++
++ /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and
++ * falling edge is set writing in the PORT0 PAD reg
++ */
++ if (priv->switch_id == QCA8K_ID_QCA8327 ||
++ priv->switch_id == QCA8K_ID_QCA8337)
++ reg = QCA8K_REG_PORT0_PAD_CTRL;
++
++ val = 0;
++
++ /* SGMII Clock phase configuration */
++ if (priv->ports_config.sgmii_rx_clk_falling_edge)
++ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE;
++
++ if (priv->ports_config.sgmii_tx_clk_falling_edge)
++ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
++
++ if (val)
++ ret = qca8k_rmw(priv, reg,
++ QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
++ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
++ val);
++
++ break;
++ default:
++ dev_err(ds->dev, "xMII mode %s not supported for port %d\n",
++ phy_modes(state->interface), port);
++ return;
++ }
++}
++
++static void
++qca8k_phylink_validate(struct dsa_switch *ds, int port,
++ unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
++
++ switch (port) {
++ case 0: /* 1st CPU port */
++ if (state->interface != PHY_INTERFACE_MODE_NA &&
++ state->interface != PHY_INTERFACE_MODE_RGMII &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
++ state->interface != PHY_INTERFACE_MODE_SGMII)
++ goto unsupported;
++ break;
++ case 1:
++ case 2:
++ case 3:
++ case 4:
++ case 5:
++ /* Internal PHY */
++ if (state->interface != PHY_INTERFACE_MODE_NA &&
++ state->interface != PHY_INTERFACE_MODE_GMII &&
++ state->interface != PHY_INTERFACE_MODE_INTERNAL)
++ goto unsupported;
++ break;
++ case 6: /* 2nd CPU port / external PHY */
++ if (state->interface != PHY_INTERFACE_MODE_NA &&
++ state->interface != PHY_INTERFACE_MODE_RGMII &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_ID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID &&
++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID &&
++ state->interface != PHY_INTERFACE_MODE_SGMII &&
++ state->interface != PHY_INTERFACE_MODE_1000BASEX)
++ goto unsupported;
++ break;
++ default:
++unsupported:
++ linkmode_zero(supported);
++ return;
++ }
++
++ phylink_set_port_modes(mask);
++ phylink_set(mask, Autoneg);
++
++ phylink_set(mask, 1000baseT_Full);
++ phylink_set(mask, 10baseT_Half);
++ phylink_set(mask, 10baseT_Full);
++ phylink_set(mask, 100baseT_Half);
++ phylink_set(mask, 100baseT_Full);
++
++ if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
++ phylink_set(mask, 1000baseX_Full);
++
++ phylink_set(mask, Pause);
++ phylink_set(mask, Asym_Pause);
++
++ linkmode_and(supported, supported, mask);
++ linkmode_and(state->advertising, state->advertising, mask);
++}
++
++static int
++qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port,
++ struct phylink_link_state *state)
++{
++ struct qca8k_priv *priv = ds->priv;
++ u32 reg;
++ int ret;
++
++ ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), &reg);
++ if (ret < 0)
++ return ret;
++
++ state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP);
++ state->an_complete = state->link;
++ state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO);
++ state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL :
++ DUPLEX_HALF;
++
++ switch (reg & QCA8K_PORT_STATUS_SPEED) {
++ case QCA8K_PORT_STATUS_SPEED_10:
++ state->speed = SPEED_10;
++ break;
++ case QCA8K_PORT_STATUS_SPEED_100:
++ state->speed = SPEED_100;
++ break;
++ case QCA8K_PORT_STATUS_SPEED_1000:
++ state->speed = SPEED_1000;
++ break;
++ default:
++ state->speed = SPEED_UNKNOWN;
++ break;
++ }
++
++ state->pause = MLO_PAUSE_NONE;
++ if (reg & QCA8K_PORT_STATUS_RXFLOW)
++ state->pause |= MLO_PAUSE_RX;
++ if (reg & QCA8K_PORT_STATUS_TXFLOW)
++ state->pause |= MLO_PAUSE_TX;
++
++ return 1;
++}
++
++static void
++qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
++ phy_interface_t interface)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ qca8k_port_set_status(priv, port, 0);
++}
++
++static void
++qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
++ phy_interface_t interface, struct phy_device *phydev,
++ int speed, int duplex, bool tx_pause, bool rx_pause)
++{
++ struct qca8k_priv *priv = ds->priv;
++ u32 reg;
++
++ if (phylink_autoneg_inband(mode)) {
++ reg = QCA8K_PORT_STATUS_LINK_AUTO;
++ } else {
++ switch (speed) {
++ case SPEED_10:
++ reg = QCA8K_PORT_STATUS_SPEED_10;
++ break;
++ case SPEED_100:
++ reg = QCA8K_PORT_STATUS_SPEED_100;
++ break;
++ case SPEED_1000:
++ reg = QCA8K_PORT_STATUS_SPEED_1000;
++ break;
++ default:
++ reg = QCA8K_PORT_STATUS_LINK_AUTO;
++ break;
++ }
++
++ if (duplex == DUPLEX_FULL)
++ reg |= QCA8K_PORT_STATUS_DUPLEX;
++
++ if (rx_pause || dsa_is_cpu_port(ds, port))
++ reg |= QCA8K_PORT_STATUS_RXFLOW;
++
++ if (tx_pause || dsa_is_cpu_port(ds, port))
++ reg |= QCA8K_PORT_STATUS_TXFLOW;
++ }
++
++ reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
++
++ qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
++}
++
++static void
++qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int i;
++
++ if (stringset != ETH_SS_STATS)
++ return;
++
++ for (i = 0; i < priv->info->mib_count; i++)
++ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
++ ETH_GSTRING_LEN);
++}
++
++static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb)
++{
++ struct qca8k_mib_eth_data *mib_eth_data;
++ struct qca8k_priv *priv = ds->priv;
++ const struct qca8k_mib_desc *mib;
++ struct mib_ethhdr *mib_ethhdr;
++ int i, mib_len, offset = 0;
++ u64 *data;
++ u8 port;
++
++ mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb);
++ mib_eth_data = &priv->mib_eth_data;
++
++ /* The switch autocast every port. Ignore other packet and
++ * parse only the requested one.
++ */
++ port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr));
++ if (port != mib_eth_data->req_port)
++ goto exit;
++
++ data = mib_eth_data->data;
++
++ for (i = 0; i < priv->info->mib_count; i++) {
++ mib = &ar8327_mib[i];
++
++ /* First 3 mib are present in the skb head */
++ if (i < 3) {
++ data[i] = mib_ethhdr->data[i];
++ continue;
++ }
++
++ mib_len = sizeof(uint32_t);
++
++ /* Some mib are 64 bit wide */
++ if (mib->size == 2)
++ mib_len = sizeof(uint64_t);
++
++ /* Copy the mib value from packet to the */
++ memcpy(data + i, skb->data + offset, mib_len);
++
++ /* Set the offset for the next mib */
++ offset += mib_len;
++ }
++
++exit:
++ /* Complete on receiving all the mib packet */
++ if (refcount_dec_and_test(&mib_eth_data->port_parsed))
++ complete(&mib_eth_data->rw_done);
++}
++
++static int
++qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data)
++{
++ struct dsa_port *dp = dsa_to_port(ds, port);
++ struct qca8k_mib_eth_data *mib_eth_data;
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ mib_eth_data = &priv->mib_eth_data;
++
++ mutex_lock(&mib_eth_data->mutex);
++
++ reinit_completion(&mib_eth_data->rw_done);
++
++ mib_eth_data->req_port = dp->index;
++ mib_eth_data->data = data;
++ refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS);
++
++ mutex_lock(&priv->reg_mutex);
++
++ /* Send mib autocast request */
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) |
++ QCA8K_MIB_BUSY);
++
++ mutex_unlock(&priv->reg_mutex);
++
++ if (ret)
++ goto exit;
++
++ ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT);
++
++exit:
++ mutex_unlock(&mib_eth_data->mutex);
++
++ return ret;
++}
++
++static void
++qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
++ uint64_t *data)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ const struct qca8k_mib_desc *mib;
++ u32 reg, i, val;
++ u32 hi = 0;
++ int ret;
++
++ if (priv->mgmt_master && priv->info->ops->autocast_mib &&
++ priv->info->ops->autocast_mib(ds, port, data) > 0)
++ return;
++
++ for (i = 0; i < priv->info->mib_count; i++) {
++ mib = &ar8327_mib[i];
++ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
++
++ ret = qca8k_read(priv, reg, &val);
++ if (ret < 0)
++ continue;
++
++ if (mib->size == 2) {
++ ret = qca8k_read(priv, reg + 4, &hi);
++ if (ret < 0)
++ continue;
++ }
++
++ data[i] = val;
++ if (mib->size == 2)
++ data[i] |= (u64)hi << 32;
++ }
++}
++
++static int
++qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ if (sset != ETH_SS_STATS)
++ return 0;
++
++ return priv->info->mib_count;
++}
++
++static int
++qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
++ u32 reg;
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, &reg);
++ if (ret < 0)
++ goto exit;
++
++ if (eee->eee_enabled)
++ reg |= lpi_en;
++ else
++ reg &= ~lpi_en;
++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int
++qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
++{
++ /* Nothing to do on the port's MAC */
++ return 0;
++}
++
++static void
++qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u32 stp_state;
++
++ switch (state) {
++ case BR_STATE_DISABLED:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED;
++ break;
++ case BR_STATE_BLOCKING:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING;
++ break;
++ case BR_STATE_LISTENING:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING;
++ break;
++ case BR_STATE_LEARNING:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
++ break;
++ case BR_STATE_FORWARDING:
++ default:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
++ break;
++ }
++
++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
++}
++
++static int
++qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ int port_mask, cpu_port;
++ int i, ret;
++
++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
++ port_mask = BIT(cpu_port);
++
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
++ if (dsa_to_port(ds, i)->bridge_dev != br)
++ continue;
++ /* Add this port to the portvlan mask of the other ports
++ * in the bridge
++ */
++ ret = regmap_set_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
++ if (ret)
++ return ret;
++ if (i != port)
++ port_mask |= BIT(i);
++ }
++
++ /* Add all other ports to this ports portvlan mask */
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_MEMBER, port_mask);
++
++ return ret;
++}
++
++static void
++qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ int cpu_port, i;
++
++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
++
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
++ if (dsa_to_port(ds, i)->bridge_dev != br)
++ continue;
++ /* Remove this port to the portvlan mask of the other ports
++ * in the bridge
++ */
++ regmap_clear_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
++ }
++
++ /* Set the cpu port to be the only one in the portvlan mask of
++ * this port
++ */
++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
++}
++
++static void
++qca8k_port_fast_age(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
++ mutex_unlock(&priv->reg_mutex);
++}
++
++static int
++qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
++{
++ struct qca8k_priv *priv = ds->priv;
++ unsigned int secs = msecs / 1000;
++ u32 val;
++
++ /* AGE_TIME reg is set in 7s step */
++ val = secs / 7;
++
++ /* Handle case with 0 as val to NOT disable
++ * learning
++ */
++ if (!val)
++ val = 1;
++
++ return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
++ QCA8K_ATU_AGE_TIME(val));
++}
++
++static int
++qca8k_port_enable(struct dsa_switch *ds, int port,
++ struct phy_device *phy)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++
++ qca8k_port_set_status(priv, port, 1);
++ priv->port_enabled_map |= BIT(port);
++
++ if (dsa_is_user_port(ds, port))
++ phy_support_asym_pause(phy);
++
++ return 0;
++}
++
++static void
++qca8k_port_disable(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++
++ qca8k_port_set_status(priv, port, 0);
++ priv->port_enabled_map &= ~BIT(port);
++}
++
++static int
++qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ /* We have only have a general MTU setting.
++ * DSA always set the CPU port's MTU to the largest MTU of the slave
++ * ports.
++ * Setting MTU just for the CPU port is sufficient to correctly set a
++ * value for every port.
++ */
++ if (!dsa_is_cpu_port(ds, port))
++ return 0;
++
++ /* To change the MAX_FRAME_SIZE the cpu ports must be off or
++ * the switch panics.
++ * Turn off both cpu ports before applying the new value to prevent
++ * this.
++ */
++ if (priv->port_enabled_map & BIT(0))
++ qca8k_port_set_status(priv, 0, 0);
++
++ if (priv->port_enabled_map & BIT(6))
++ qca8k_port_set_status(priv, 6, 0);
++
++ /* Include L2 header / FCS length */
++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN);
++
++ if (priv->port_enabled_map & BIT(0))
++ qca8k_port_set_status(priv, 0, 1);
++
++ if (priv->port_enabled_map & BIT(6))
++ qca8k_port_set_status(priv, 6, 1);
++
++ return ret;
++}
++
++static int
++qca8k_port_max_mtu(struct dsa_switch *ds, int port)
++{
++ return QCA8K_MAX_MTU;
++}
++
++static int
++qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
++ u16 port_mask, u16 vid)
++{
++ /* Set the vid to the port vlan id if no vid is set */
++ if (!vid)
++ vid = QCA8K_PORT_VID_DEF;
++
++ return qca8k_fdb_add(priv, addr, port_mask, vid,
++ QCA8K_ATU_STATUS_STATIC);
++}
++
++static int
++qca8k_port_fdb_add(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u16 port_mask = BIT(port);
++
++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
++}
++
++static int
++qca8k_port_fdb_del(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u16 port_mask = BIT(port);
++
++ if (!vid)
++ vid = QCA8K_PORT_VID_DEF;
++
++ return qca8k_fdb_del(priv, addr, port_mask, vid);
++}
++
++static int
++qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
++ dsa_fdb_dump_cb_t *cb, void *data)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ struct qca8k_fdb _fdb = { 0 };
++ int cnt = QCA8K_NUM_FDB_RECORDS;
++ bool is_static;
++ int ret = 0;
++
++ mutex_lock(&priv->reg_mutex);
++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
++ if (!_fdb.aging)
++ break;
++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
++ ret = cb(_fdb.mac, _fdb.vid, is_static, data);
++ if (ret)
++ break;
++ }
++ mutex_unlock(&priv->reg_mutex);
++
++ return 0;
++}
++
++static int
++qca8k_port_mdb_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
++}
++
++static int
++qca8k_port_mdb_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
++}
++
++static int
++qca8k_port_mirror_add(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror,
++ bool ingress)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int monitor_port, ret;
++ u32 reg, val;
++
++ /* Check for existent entry */
++ if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
++ return -EEXIST;
++
++ ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
++ if (ret)
++ return ret;
++
++ /* QCA83xx can have only one port set to mirror mode.
++ * Check that the correct port is requested and return error otherwise.
++ * When no mirror port is set, the values is set to 0xF
++ */
++ monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
++ return -EEXIST;
++
++ /* Set the monitor port */
++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
++ mirror->to_local_port);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (ret)
++ return ret;
++
++ if (ingress) {
++ reg = QCA8K_PORT_LOOKUP_CTRL(port);
++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
++ } else {
++ reg = QCA8K_REG_PORT_HOL_CTRL1(port);
++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
++ }
++
++ ret = regmap_update_bits(priv->regmap, reg, val, val);
++ if (ret)
++ return ret;
++
++ /* Track mirror port for tx and rx to decide when the
++ * mirror port has to be disabled.
++ */
++ if (ingress)
++ priv->mirror_rx |= BIT(port);
++ else
++ priv->mirror_tx |= BIT(port);
++
++ return 0;
++}
++
++static void
++qca8k_port_mirror_del(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror)
++{
++ struct qca8k_priv *priv = ds->priv;
++ u32 reg, val;
++ int ret;
++
++ if (mirror->ingress) {
++ reg = QCA8K_PORT_LOOKUP_CTRL(port);
++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
++ } else {
++ reg = QCA8K_REG_PORT_HOL_CTRL1(port);
++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
++ }
++
++ ret = regmap_clear_bits(priv->regmap, reg, val);
++ if (ret)
++ goto err;
++
++ if (mirror->ingress)
++ priv->mirror_rx &= ~BIT(port);
++ else
++ priv->mirror_tx &= ~BIT(port);
++
++ /* No port set to send packet to mirror port. Disable mirror port */
++ if (!priv->mirror_rx && !priv->mirror_tx) {
++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (ret)
++ goto err;
++ }
++err:
++ dev_err(priv->dev, "Failed to del mirror port from %d", port);
++}
++
++static int
++qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
++ struct netlink_ext_ack *extack)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ if (vlan_filtering) {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
++ } else {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
++ }
++
++ return ret;
++}
++
++static int
++qca8k_port_vlan_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_vlan *vlan,
++ struct netlink_ext_ack *extack)
++{
++ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
++ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
++ if (ret) {
++ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
++ return ret;
++ }
++
++ if (pvid) {
++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
++ QCA8K_EGREES_VLAN_PORT_MASK(port),
++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
++ if (ret)
++ return ret;
++
++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
++ QCA8K_PORT_VLAN_CVID(vlan->vid) |
++ QCA8K_PORT_VLAN_SVID(vlan->vid));
++ }
++
++ return ret;
++}
++
++static int
++qca8k_port_vlan_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_vlan *vlan)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ ret = qca8k_vlan_del(priv, port, vlan->vid);
++ if (ret)
++ dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
++
++ return ret;
++}
++
++static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Communicate to the phy internal driver the switch revision.
++ * Based on the switch revision different values needs to be
++ * set to the dbg and mmd reg on the phy.
++ * The first 2 bit are used to communicate the switch revision
++ * to the phy driver.
++ */
++ if (port > 0 && port < 6)
++ return priv->switch_revision;
++
++ return 0;
++}
++
++static enum dsa_tag_protocol
++qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
++ enum dsa_tag_protocol mp)
++{
++ return DSA_TAG_PROTO_QCA;
++}
++
++static bool
++qca8k_lag_can_offload(struct dsa_switch *ds,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct dsa_port *dp;
++ int id, members = 0;
++
++ id = dsa_lag_id(ds->dst, lag);
++ if (id < 0 || id >= ds->num_lag_ids)
++ return false;
++
++ dsa_lag_foreach_port(dp, ds->dst, lag)
++ /* Includes the port joining the LAG */
++ members++;
++
++ if (members > QCA8K_NUM_PORTS_FOR_LAG)
++ return false;
++
++ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
++ return false;
++
++ if (info->hash_type != NETDEV_LAG_HASH_L2 &&
++ info->hash_type != NETDEV_LAG_HASH_L23)
++ return false;
++
++ return true;
++}
++
++static int
++qca8k_lag_setup_hash(struct dsa_switch *ds,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct qca8k_priv *priv = ds->priv;
++ bool unique_lag = true;
++ u32 hash = 0;
++ int i, id;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ switch (info->hash_type) {
++ case NETDEV_LAG_HASH_L23:
++ hash |= QCA8K_TRUNK_HASH_SIP_EN;
++ hash |= QCA8K_TRUNK_HASH_DIP_EN;
++ fallthrough;
++ case NETDEV_LAG_HASH_L2:
++ hash |= QCA8K_TRUNK_HASH_SA_EN;
++ hash |= QCA8K_TRUNK_HASH_DA_EN;
++ break;
++ default: /* We should NEVER reach this */
++ return -EOPNOTSUPP;
++ }
++
++ /* Check if we are the unique configured LAG */
++ dsa_lags_foreach_id(i, ds->dst)
++ if (i != id && dsa_lag_dev(ds->dst, i)) {
++ unique_lag = false;
++ break;
++ }
++
++ /* Hash Mode is global. Make sure the same Hash Mode
++ * is set to all the 4 possible lag.
++ * If we are the unique LAG we can set whatever hash
++ * mode we want.
++ * To change hash mode it's needed to remove all LAG
++ * and change the mode with the latest.
++ */
++ if (unique_lag) {
++ priv->lag_hash_mode = hash;
++ } else if (priv->lag_hash_mode != hash) {
++ netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n");
++ return -EOPNOTSUPP;
++ }
++
++ return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
++ QCA8K_TRUNK_HASH_MASK, hash);
++}
++
++static int
++qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
++ struct net_device *lag, bool delete)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret, id, i;
++ u32 val;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ /* Read current port member */
++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
++ if (ret)
++ return ret;
++
++ /* Shift val to the correct trunk */
++ val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
++ val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
++ if (delete)
++ val &= ~BIT(port);
++ else
++ val |= BIT(port);
++
++ /* Update port member. With empty portmap disable trunk */
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
++ QCA8K_REG_GOL_TRUNK_MEMBER(id) |
++ QCA8K_REG_GOL_TRUNK_EN(id),
++ !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
++ val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
++
++ /* Search empty member if adding or port on deleting */
++ for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
++ if (ret)
++ return ret;
++
++ val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
++
++ if (delete) {
++ /* If port flagged to be disabled assume this member is
++ * empty
++ */
++ if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
++ continue;
++
++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
++ if (val != port)
++ continue;
++ } else {
++ /* If port flagged to be enabled assume this member is
++ * already set
++ */
++ if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
++ continue;
++ }
++
++ /* We have found the member to add/remove */
++ break;
++ }
++
++ /* Set port in the correct port mask or disable port if in delete mode */
++ return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
++ !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
++ port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
++}
++
++static int
++qca8k_port_lag_join(struct dsa_switch *ds, int port,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ int ret;
++
++ if (!qca8k_lag_can_offload(ds, lag, info))
++ return -EOPNOTSUPP;
++
++ ret = qca8k_lag_setup_hash(ds, lag, info);
++ if (ret)
++ return ret;
++
++ return qca8k_lag_refresh_portmap(ds, port, lag, false);
++}
++
++static int
++qca8k_port_lag_leave(struct dsa_switch *ds, int port,
++ struct net_device *lag)
++{
++ return qca8k_lag_refresh_portmap(ds, port, lag, true);
++}
++
++static void
++qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
++ bool operational)
++{
++ struct dsa_port *dp = master->dsa_ptr;
++ struct qca8k_priv *priv = ds->priv;
++
++ /* Ethernet MIB/MDIO is only supported for CPU port 0 */
++ if (dp->index != 0)
++ return;
++
++ mutex_lock(&priv->mgmt_eth_data.mutex);
++ mutex_lock(&priv->mib_eth_data.mutex);
++
++ priv->mgmt_master = operational ? (struct net_device *)master : NULL;
++
++ mutex_unlock(&priv->mib_eth_data.mutex);
++ mutex_unlock(&priv->mgmt_eth_data.mutex);
++}
++
++static int qca8k_connect_tag_protocol(struct dsa_switch *ds,
++ enum dsa_tag_protocol proto)
++{
++ struct qca_tagger_data *tagger_data;
++
++ switch (proto) {
++ case DSA_TAG_PROTO_QCA:
++ tagger_data = ds->tagger_data;
++
++ tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler;
++ tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler;
++
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ return 0;
++}
++
++static const struct dsa_switch_ops qca8k_switch_ops = {
++ .get_tag_protocol = qca8k_get_tag_protocol,
++ .setup = qca8k_setup,
++ .get_strings = qca8k_get_strings,
++ .get_ethtool_stats = qca8k_get_ethtool_stats,
++ .get_sset_count = qca8k_get_sset_count,
++ .set_ageing_time = qca8k_set_ageing_time,
++ .get_mac_eee = qca8k_get_mac_eee,
++ .set_mac_eee = qca8k_set_mac_eee,
++ .port_enable = qca8k_port_enable,
++ .port_disable = qca8k_port_disable,
++ .port_change_mtu = qca8k_port_change_mtu,
++ .port_max_mtu = qca8k_port_max_mtu,
++ .port_stp_state_set = qca8k_port_stp_state_set,
++ .port_bridge_join = qca8k_port_bridge_join,
++ .port_bridge_leave = qca8k_port_bridge_leave,
++ .port_fast_age = qca8k_port_fast_age,
++ .port_fdb_add = qca8k_port_fdb_add,
++ .port_fdb_del = qca8k_port_fdb_del,
++ .port_fdb_dump = qca8k_port_fdb_dump,
++ .port_mdb_add = qca8k_port_mdb_add,
++ .port_mdb_del = qca8k_port_mdb_del,
++ .port_mirror_add = qca8k_port_mirror_add,
++ .port_mirror_del = qca8k_port_mirror_del,
++ .port_vlan_filtering = qca8k_port_vlan_filtering,
++ .port_vlan_add = qca8k_port_vlan_add,
++ .port_vlan_del = qca8k_port_vlan_del,
++ .phylink_validate = qca8k_phylink_validate,
++ .phylink_mac_link_state = qca8k_phylink_mac_link_state,
++ .phylink_mac_config = qca8k_phylink_mac_config,
++ .phylink_mac_link_down = qca8k_phylink_mac_link_down,
++ .phylink_mac_link_up = qca8k_phylink_mac_link_up,
++ .get_phy_flags = qca8k_get_phy_flags,
++ .port_lag_join = qca8k_port_lag_join,
++ .port_lag_leave = qca8k_port_lag_leave,
++ .master_state_change = qca8k_master_change,
++ .connect_tag_protocol = qca8k_connect_tag_protocol,
++};
++
++static int qca8k_read_switch_id(struct qca8k_priv *priv)
++{
++ u32 val;
++ u8 id;
++ int ret;
++
++ if (!priv->info)
++ return -ENODEV;
++
++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
++ if (ret < 0)
++ return -ENODEV;
++
++ id = QCA8K_MASK_CTRL_DEVICE_ID(val);
++ if (id != priv->info->id) {
++ dev_err(priv->dev,
++ "Switch id detected %x but expected %x",
++ id, priv->info->id);
++ return -ENODEV;
++ }
++
++ priv->switch_id = id;
++
++ /* Save revision to communicate to the internal PHY driver */
++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
++
++ return 0;
++}
++
++static int
++qca8k_sw_probe(struct mdio_device *mdiodev)
++{
++ struct qca8k_priv *priv;
++ int ret;
++
++ /* allocate the private data struct so that we can probe the switches
++ * ID register
++ */
++ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->info = of_device_get_match_data(priv->dev);
++ priv->bus = mdiodev->bus;
++ priv->dev = &mdiodev->dev;
++
++ priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset",
++ GPIOD_ASIS);
++ if (IS_ERR(priv->reset_gpio))
++ return PTR_ERR(priv->reset_gpio);
++
++ if (priv->reset_gpio) {
++ gpiod_set_value_cansleep(priv->reset_gpio, 1);
++ /* The active low duration must be greater than 10 ms
++ * and checkpatch.pl wants 20 ms.
++ */
++ msleep(20);
++ gpiod_set_value_cansleep(priv->reset_gpio, 0);
++ }
++
++ /* Start by setting up the register mapping */
++ priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv,
++ &qca8k_regmap_config);
++ if (IS_ERR(priv->regmap)) {
++ dev_err(priv->dev, "regmap initialization failed");
++ return PTR_ERR(priv->regmap);
++ }
++
++ priv->mdio_cache.page = 0xffff;
++ priv->mdio_cache.lo = 0xffff;
++ priv->mdio_cache.hi = 0xffff;
++
++ /* Check the detected switch id */
++ ret = qca8k_read_switch_id(priv);
++ if (ret)
++ return ret;
++
++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL);
++ if (!priv->ds)
++ return -ENOMEM;
++
++ mutex_init(&priv->mgmt_eth_data.mutex);
++ init_completion(&priv->mgmt_eth_data.rw_done);
++
++ mutex_init(&priv->mib_eth_data.mutex);
++ init_completion(&priv->mib_eth_data.rw_done);
++
++ priv->ds->dev = &mdiodev->dev;
++ priv->ds->num_ports = QCA8K_NUM_PORTS;
++ priv->ds->priv = priv;
++ priv->ds->ops = &qca8k_switch_ops;
++ mutex_init(&priv->reg_mutex);
++ dev_set_drvdata(&mdiodev->dev, priv);
++
++ return dsa_register_switch(priv->ds);
++}
++
++static void
++qca8k_sw_remove(struct mdio_device *mdiodev)
++{
++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
++ int i;
++
++ if (!priv)
++ return;
++
++ for (i = 0; i < QCA8K_NUM_PORTS; i++)
++ qca8k_port_set_status(priv, i, 0);
++
++ dsa_unregister_switch(priv->ds);
++
++ dev_set_drvdata(&mdiodev->dev, NULL);
++}
++
++static void qca8k_sw_shutdown(struct mdio_device *mdiodev)
++{
++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);
++
++ if (!priv)
++ return;
++
++ dsa_switch_shutdown(priv->ds);
++
++ dev_set_drvdata(&mdiodev->dev, NULL);
++}
++
++#ifdef CONFIG_PM_SLEEP
++static void
++qca8k_set_pm(struct qca8k_priv *priv, int enable)
++{
++ int port;
++
++ for (port = 0; port < QCA8K_NUM_PORTS; port++) {
++ /* Do not enable on resume if the port was
++ * disabled before.
++ */
++ if (!(priv->port_enabled_map & BIT(port)))
++ continue;
++
++ qca8k_port_set_status(priv, port, enable);
++ }
++}
++
++static int qca8k_suspend(struct device *dev)
++{
++ struct qca8k_priv *priv = dev_get_drvdata(dev);
++
++ qca8k_set_pm(priv, 0);
++
++ return dsa_switch_suspend(priv->ds);
++}
++
++static int qca8k_resume(struct device *dev)
++{
++ struct qca8k_priv *priv = dev_get_drvdata(dev);
++
++ qca8k_set_pm(priv, 1);
++
++ return dsa_switch_resume(priv->ds);
++}
++#endif /* CONFIG_PM_SLEEP */
++
++static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
++ qca8k_suspend, qca8k_resume);
++
++static const struct qca8k_info_ops qca8xxx_ops = {
++ .autocast_mib = qca8k_get_ethtool_stats_eth,
++};
++
++static const struct qca8k_match_data qca8327 = {
++ .id = QCA8K_ID_QCA8327,
++ .reduced_package = true,
++ .mib_count = QCA8K_QCA832X_MIB_COUNT,
++ .ops = &qca8xxx_ops,
++};
++
++static const struct qca8k_match_data qca8328 = {
++ .id = QCA8K_ID_QCA8327,
++ .mib_count = QCA8K_QCA832X_MIB_COUNT,
++ .ops = &qca8xxx_ops,
++};
++
++static const struct qca8k_match_data qca833x = {
++ .id = QCA8K_ID_QCA8337,
++ .mib_count = QCA8K_QCA833X_MIB_COUNT,
++ .ops = &qca8xxx_ops,
++};
++
++static const struct of_device_id qca8k_of_match[] = {
++ { .compatible = "qca,qca8327", .data = &qca8327 },
++ { .compatible = "qca,qca8328", .data = &qca8328 },
++ { .compatible = "qca,qca8334", .data = &qca833x },
++ { .compatible = "qca,qca8337", .data = &qca833x },
++ { /* sentinel */ },
++};
++
++static struct mdio_driver qca8kmdio_driver = {
++ .probe = qca8k_sw_probe,
++ .remove = qca8k_sw_remove,
++ .shutdown = qca8k_sw_shutdown,
++ .mdiodrv.driver = {
++ .name = "qca8k",
++ .of_match_table = qca8k_of_match,
++ .pm = &qca8k_pm_ops,
++ },
++};
++
++mdio_module_driver(qca8kmdio_driver);
++
++MODULE_AUTHOR("Mathieu Olivari, John Crispin <john@phrozen.org>");
++MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:qca8k");
+--- /dev/null
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -0,0 +1,63 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
++ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
++ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved.
++ * Copyright (c) 2016 John Crispin <john@phrozen.org>
++ */
++
++#include <linux/netdevice.h>
++#include <net/dsa.h>
++
++#include "qca8k.h"
++
++#define MIB_DESC(_s, _o, _n) \
++ { \
++ .size = (_s), \
++ .offset = (_o), \
++ .name = (_n), \
++ }
++
++const struct qca8k_mib_desc ar8327_mib[] = {
++ MIB_DESC(1, 0x00, "RxBroad"),
++ MIB_DESC(1, 0x04, "RxPause"),
++ MIB_DESC(1, 0x08, "RxMulti"),
++ MIB_DESC(1, 0x0c, "RxFcsErr"),
++ MIB_DESC(1, 0x10, "RxAlignErr"),
++ MIB_DESC(1, 0x14, "RxRunt"),
++ MIB_DESC(1, 0x18, "RxFragment"),
++ MIB_DESC(1, 0x1c, "Rx64Byte"),
++ MIB_DESC(1, 0x20, "Rx128Byte"),
++ MIB_DESC(1, 0x24, "Rx256Byte"),
++ MIB_DESC(1, 0x28, "Rx512Byte"),
++ MIB_DESC(1, 0x2c, "Rx1024Byte"),
++ MIB_DESC(1, 0x30, "Rx1518Byte"),
++ MIB_DESC(1, 0x34, "RxMaxByte"),
++ MIB_DESC(1, 0x38, "RxTooLong"),
++ MIB_DESC(2, 0x3c, "RxGoodByte"),
++ MIB_DESC(2, 0x44, "RxBadByte"),
++ MIB_DESC(1, 0x4c, "RxOverFlow"),
++ MIB_DESC(1, 0x50, "Filtered"),
++ MIB_DESC(1, 0x54, "TxBroad"),
++ MIB_DESC(1, 0x58, "TxPause"),
++ MIB_DESC(1, 0x5c, "TxMulti"),
++ MIB_DESC(1, 0x60, "TxUnderRun"),
++ MIB_DESC(1, 0x64, "Tx64Byte"),
++ MIB_DESC(1, 0x68, "Tx128Byte"),
++ MIB_DESC(1, 0x6c, "Tx256Byte"),
++ MIB_DESC(1, 0x70, "Tx512Byte"),
++ MIB_DESC(1, 0x74, "Tx1024Byte"),
++ MIB_DESC(1, 0x78, "Tx1518Byte"),
++ MIB_DESC(1, 0x7c, "TxMaxByte"),
++ MIB_DESC(1, 0x80, "TxOverSize"),
++ MIB_DESC(2, 0x84, "TxByte"),
++ MIB_DESC(1, 0x8c, "TxCollision"),
++ MIB_DESC(1, 0x90, "TxAbortCol"),
++ MIB_DESC(1, 0x94, "TxMultiCol"),
++ MIB_DESC(1, 0x98, "TxSingleCol"),
++ MIB_DESC(1, 0x9c, "TxExcDefer"),
++ MIB_DESC(1, 0xa0, "TxDefer"),
++ MIB_DESC(1, 0xa4, "TxLateCol"),
++ MIB_DESC(1, 0xa8, "RXUnicast"),
++ MIB_DESC(1, 0xac, "TXUnicast"),
++};
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -414,4 +414,7 @@ struct qca8k_fdb {
+ u8 mac[6];
+ };
+
++/* Common setup function */
++extern const struct qca8k_mib_desc ar8327_mib[];
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch b/target/linux/generic/backport-6.1/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch
new file mode 100644
index 0000000000..012ab85474
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch
@@ -0,0 +1,135 @@
+From d5f901eab2e9dfed1095995dfc98f231f4fd2971 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:13 +0200
+Subject: [PATCH 04/14] net: dsa: qca8k: move qca8k read/write/rmw and reg
+ table to common code
+
+The same reg table and read/write/rmw function are used by drivers
+based on qca8k family switch.
+Move them to common code to make it accessible also by other drivers.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 42 ------------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 38 +++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 6 +++++
+ 3 files changed, 44 insertions(+), 42 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -133,24 +133,6 @@ qca8k_set_page(struct qca8k_priv *priv,
+ return 0;
+ }
+
+-static int
+-qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
+-{
+- return regmap_read(priv->regmap, reg, val);
+-}
+-
+-static int
+-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
+-{
+- return regmap_write(priv->regmap, reg, val);
+-}
+-
+-static int
+-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
+-{
+- return regmap_update_bits(priv->regmap, reg, mask, write_val);
+-}
+-
+ static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
+ {
+ struct qca8k_mgmt_eth_data *mgmt_eth_data;
+@@ -483,30 +465,6 @@ exit:
+ return ret;
+ }
+
+-static const struct regmap_range qca8k_readable_ranges[] = {
+- regmap_reg_range(0x0000, 0x00e4), /* Global control */
+- regmap_reg_range(0x0100, 0x0168), /* EEE control */
+- regmap_reg_range(0x0200, 0x0270), /* Parser control */
+- regmap_reg_range(0x0400, 0x0454), /* ACL */
+- regmap_reg_range(0x0600, 0x0718), /* Lookup */
+- regmap_reg_range(0x0800, 0x0b70), /* QM */
+- regmap_reg_range(0x0c00, 0x0c80), /* PKT */
+- regmap_reg_range(0x0e00, 0x0e98), /* L3 */
+- regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */
+- regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */
+- regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */
+- regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */
+- regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */
+- regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */
+- regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */
+-
+-};
+-
+-static const struct regmap_access_table qca8k_readable_table = {
+- .yes_ranges = qca8k_readable_ranges,
+- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
+-};
+-
+ static struct regmap_config qca8k_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 32,
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -61,3 +61,41 @@ const struct qca8k_mib_desc ar8327_mib[]
+ MIB_DESC(1, 0xa8, "RXUnicast"),
+ MIB_DESC(1, 0xac, "TXUnicast"),
+ };
++
++int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)
++{
++ return regmap_read(priv->regmap, reg, val);
++}
++
++int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
++{
++ return regmap_write(priv->regmap, reg, val);
++}
++
++int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
++{
++ return regmap_update_bits(priv->regmap, reg, mask, write_val);
++}
++
++static const struct regmap_range qca8k_readable_ranges[] = {
++ regmap_reg_range(0x0000, 0x00e4), /* Global control */
++ regmap_reg_range(0x0100, 0x0168), /* EEE control */
++ regmap_reg_range(0x0200, 0x0270), /* Parser control */
++ regmap_reg_range(0x0400, 0x0454), /* ACL */
++ regmap_reg_range(0x0600, 0x0718), /* Lookup */
++ regmap_reg_range(0x0800, 0x0b70), /* QM */
++ regmap_reg_range(0x0c00, 0x0c80), /* PKT */
++ regmap_reg_range(0x0e00, 0x0e98), /* L3 */
++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */
++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */
++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */
++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */
++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */
++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */
++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */
++};
++
++const struct regmap_access_table qca8k_readable_table = {
++ .yes_ranges = qca8k_readable_ranges,
++ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
++};
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -416,5 +416,11 @@ struct qca8k_fdb {
+
+ /* Common setup function */
+ extern const struct qca8k_mib_desc ar8327_mib[];
++extern const struct regmap_access_table qca8k_readable_table;
++
++/* Common read/write/rmw function */
++int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val);
++int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val);
++int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val);
+
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch b/target/linux/generic/backport-6.1/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch
new file mode 100644
index 0000000000..0ed7ed41fb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch
@@ -0,0 +1,145 @@
+From 910746444313dc463396cd63024cdf54ef04ef39 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:14 +0200
+Subject: [PATCH 05/14] net: dsa: qca8k: move qca8k bulk read/write helper to
+ common code
+
+The same ATU function are used by drivers based on qca8k family switch.
+Move the bulk read/write helper to common code to declare these shared
+ATU functions in common code.
+These helper will be dropped when regmap correctly support bulk
+read/write.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 39 ++----------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 39 ++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 8 ++++++
+ 3 files changed, 49 insertions(+), 37 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -343,43 +343,6 @@ qca8k_regmap_update_bits_eth(struct qca8
+ }
+
+ static int
+-qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- int i, count = len / sizeof(u32), ret;
+-
+- if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len))
+- return 0;
+-
+- for (i = 0; i < count; i++) {
+- ret = regmap_read(priv->regmap, reg + (i * 4), val + i);
+- if (ret < 0)
+- return ret;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+-{
+- int i, count = len / sizeof(u32), ret;
+- u32 tmp;
+-
+- if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len))
+- return 0;
+-
+- for (i = 0; i < count; i++) {
+- tmp = val[i];
+-
+- ret = regmap_write(priv->regmap, reg + (i * 4), tmp);
+- if (ret < 0)
+- return ret;
+- }
+-
+- return 0;
+-}
+-
+-static int
+ qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ctx;
+@@ -3096,6 +3059,8 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,
+
+ static const struct qca8k_info_ops qca8xxx_ops = {
+ .autocast_mib = qca8k_get_ethtool_stats_eth,
++ .read_eth = qca8k_read_eth,
++ .write_eth = qca8k_write_eth,
+ };
+
+ static const struct qca8k_match_data qca8327 = {
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -99,3 +99,42 @@ const struct regmap_access_table qca8k_r
+ .yes_ranges = qca8k_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges),
+ };
++
++/* TODO: remove these extra ops when we can support regmap bulk read/write */
++int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ int i, count = len / sizeof(u32), ret;
++
++ if (priv->mgmt_master && priv->info->ops->read_eth &&
++ !priv->info->ops->read_eth(priv, reg, val, len))
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ ret = regmap_read(priv->regmap, reg + (i * 4), val + i);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++/* TODO: remove these extra ops when we can support regmap bulk read/write */
++int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++{
++ int i, count = len / sizeof(u32), ret;
++ u32 tmp;
++
++ if (priv->mgmt_master && priv->info->ops->write_eth &&
++ !priv->info->ops->write_eth(priv, reg, val, len))
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ tmp = val[i];
++
++ ret = regmap_write(priv->regmap, reg + (i * 4), tmp);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -324,8 +324,13 @@ enum qca8k_mid_cmd {
+ QCA8K_MIB_CAST = 3,
+ };
+
++struct qca8k_priv;
++
+ struct qca8k_info_ops {
+ int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data);
++ /* TODO: remove these extra ops when we can support regmap bulk read/write */
++ int (*read_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
++ int (*write_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
+ };
+
+ struct qca8k_match_data {
+@@ -423,4 +428,7 @@ int qca8k_read(struct qca8k_priv *priv,
+ int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val);
+ int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val);
+
++int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
++int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch b/target/linux/generic/backport-6.1/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch
new file mode 100644
index 0000000000..a39a55b89b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch
@@ -0,0 +1,137 @@
+From fce1ec0c4e2d03d9c62ffc615a42bdba78eb4c14 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:15 +0200
+Subject: [PATCH 06/14] net: dsa: qca8k: move mib init function to common code
+
+The same mib function is used by drivers based on qca8k family switch.
+Move it to common code to make it accessible also by other drivers.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 37 ------------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 35 ++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 4 ++++
+ 3 files changed, 39 insertions(+), 37 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -442,15 +442,6 @@ static struct regmap_config qca8k_regmap
+ };
+
+ static int
+-qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+-{
+- u32 val;
+-
+- return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
+- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
+-}
+-
+-static int
+ qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+ {
+ u32 reg[3];
+@@ -777,34 +768,6 @@ out:
+ return ret;
+ }
+
+-static int
+-qca8k_mib_init(struct qca8k_priv *priv)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
+- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
+- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) |
+- QCA8K_MIB_BUSY);
+- if (ret)
+- goto exit;
+-
+- ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
+- if (ret)
+- goto exit;
+-
+- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
+- if (ret)
+- goto exit;
+-
+- ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+ static void
+ qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
+ {
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -7,6 +7,7 @@
+ */
+
+ #include <linux/netdevice.h>
++#include <linux/bitfield.h>
+ #include <net/dsa.h>
+
+ #include "qca8k.h"
+@@ -138,3 +139,38 @@ int qca8k_bulk_write(struct qca8k_priv *
+
+ return 0;
+ }
++
++int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
++{
++ u32 val;
++
++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0,
++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
++}
++
++int qca8k_mib_init(struct qca8k_priv *priv)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) |
++ QCA8K_MIB_BUSY);
++ if (ret)
++ goto exit;
++
++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY);
++ if (ret)
++ goto exit;
++
++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);
++ if (ret)
++ goto exit;
++
++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -422,6 +422,7 @@ struct qca8k_fdb {
+ /* Common setup function */
+ extern const struct qca8k_mib_desc ar8327_mib[];
+ extern const struct regmap_access_table qca8k_readable_table;
++int qca8k_mib_init(struct qca8k_priv *priv);
+
+ /* Common read/write/rmw function */
+ int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val);
+@@ -431,4 +432,7 @@ int qca8k_rmw(struct qca8k_priv *priv, u
+ int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
+ int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
+
++/* Common ops function */
++int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask);
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch b/target/linux/generic/backport-6.1/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch
new file mode 100644
index 0000000000..6fd1c66b0a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch
@@ -0,0 +1,281 @@
+From 472fcea160f27a5d9b7526093d9d8d89ba0b6137 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:16 +0200
+Subject: [PATCH 07/14] net: dsa: qca8k: move port set status/eee/ethtool stats
+ function to common code
+
+The same logic to disable/enable port, set eee and get ethtool stats is
+used by drivers based on qca8k family switch.
+Move it to common code to make it accessible also by other drivers.
+While at it also drop unnecessary qca8k_priv cast for void pointers.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 105 -----------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 102 ++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 11 +++
+ 3 files changed, 113 insertions(+), 105 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -768,21 +768,6 @@ out:
+ return ret;
+ }
+
+-static void
+-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
+-{
+- u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
+-
+- /* Port 0 and 6 have no internal PHY */
+- if (port > 0 && port < 6)
+- mask |= QCA8K_PORT_STATUS_LINK_AUTO;
+-
+- if (enable)
+- regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+- else
+- regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
+-}
+-
+ static int
+ qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
+ struct sk_buff *read_skb, u32 *val)
+@@ -1974,20 +1959,6 @@ qca8k_phylink_mac_link_up(struct dsa_swi
+ qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
+ }
+
+-static void
+-qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int i;
+-
+- if (stringset != ETH_SS_STATS)
+- return;
+-
+- for (i = 0; i < priv->info->mib_count; i++)
+- strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
+- ETH_GSTRING_LEN);
+-}
+-
+ static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb)
+ {
+ struct qca8k_mib_eth_data *mib_eth_data;
+@@ -2078,82 +2049,6 @@ exit:
+ }
+
+ static void
+-qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
+- uint64_t *data)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- const struct qca8k_mib_desc *mib;
+- u32 reg, i, val;
+- u32 hi = 0;
+- int ret;
+-
+- if (priv->mgmt_master && priv->info->ops->autocast_mib &&
+- priv->info->ops->autocast_mib(ds, port, data) > 0)
+- return;
+-
+- for (i = 0; i < priv->info->mib_count; i++) {
+- mib = &ar8327_mib[i];
+- reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
+-
+- ret = qca8k_read(priv, reg, &val);
+- if (ret < 0)
+- continue;
+-
+- if (mib->size == 2) {
+- ret = qca8k_read(priv, reg + 4, &hi);
+- if (ret < 0)
+- continue;
+- }
+-
+- data[i] = val;
+- if (mib->size == 2)
+- data[i] |= (u64)hi << 32;
+- }
+-}
+-
+-static int
+-qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- if (sset != ETH_SS_STATS)
+- return 0;
+-
+- return priv->info->mib_count;
+-}
+-
+-static int
+-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
+- u32 reg;
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, &reg);
+- if (ret < 0)
+- goto exit;
+-
+- if (eee->eee_enabled)
+- reg |= lpi_en;
+- else
+- reg &= ~lpi_en;
+- ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
+-{
+- /* Nothing to do on the port's MAC */
+- return 0;
+-}
+-
+-static void
+ qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+ {
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -174,3 +174,105 @@ exit:
+ mutex_unlock(&priv->reg_mutex);
+ return ret;
+ }
++
++void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)
++{
++ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
++
++ /* Port 0 and 6 have no internal PHY */
++ if (port > 0 && port < 6)
++ mask |= QCA8K_PORT_STATUS_LINK_AUTO;
++
++ if (enable)
++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
++ else
++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);
++}
++
++void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset,
++ uint8_t *data)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int i;
++
++ if (stringset != ETH_SS_STATS)
++ return;
++
++ for (i = 0; i < priv->info->mib_count; i++)
++ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,
++ ETH_GSTRING_LEN);
++}
++
++void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
++ uint64_t *data)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const struct qca8k_mib_desc *mib;
++ u32 reg, i, val;
++ u32 hi = 0;
++ int ret;
++
++ if (priv->mgmt_master && priv->info->ops->autocast_mib &&
++ priv->info->ops->autocast_mib(ds, port, data) > 0)
++ return;
++
++ for (i = 0; i < priv->info->mib_count; i++) {
++ mib = &ar8327_mib[i];
++ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset;
++
++ ret = qca8k_read(priv, reg, &val);
++ if (ret < 0)
++ continue;
++
++ if (mib->size == 2) {
++ ret = qca8k_read(priv, reg + 4, &hi);
++ if (ret < 0)
++ continue;
++ }
++
++ data[i] = val;
++ if (mib->size == 2)
++ data[i] |= (u64)hi << 32;
++ }
++}
++
++int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ if (sset != ETH_SS_STATS)
++ return 0;
++
++ return priv->info->mib_count;
++}
++
++int qca8k_set_mac_eee(struct dsa_switch *ds, int port,
++ struct ethtool_eee *eee)
++{
++ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port);
++ struct qca8k_priv *priv = ds->priv;
++ u32 reg;
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, &reg);
++ if (ret < 0)
++ goto exit;
++
++ if (eee->eee_enabled)
++ reg |= lpi_en;
++ else
++ reg &= ~lpi_en;
++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++int qca8k_get_mac_eee(struct dsa_switch *ds, int port,
++ struct ethtool_eee *e)
++{
++ /* Nothing to do on the port's MAC */
++ return 0;
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -423,6 +423,7 @@ struct qca8k_fdb {
+ extern const struct qca8k_mib_desc ar8327_mib[];
+ extern const struct regmap_access_table qca8k_readable_table;
+ int qca8k_mib_init(struct qca8k_priv *priv);
++void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable);
+
+ /* Common read/write/rmw function */
+ int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val);
+@@ -435,4 +436,14 @@ int qca8k_bulk_write(struct qca8k_priv *
+ /* Common ops function */
+ int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask);
+
++/* Common ethtool stats function */
++void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data);
++void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
++ uint64_t *data);
++int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset);
++
++/* Common eee function */
++int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee);
++int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch b/target/linux/generic/backport-6.1/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch
new file mode 100644
index 0000000000..3ca682d72c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch
@@ -0,0 +1,237 @@
+From fd3cae2f3ac190d06e48f43739237e02f9dc51ff Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:17 +0200
+Subject: [PATCH 08/14] net: dsa: qca8k: move bridge functions to common code
+
+The same bridge functions are used by drivers based on qca8k family
+switch. Move them to common code to make them accessible also by other
+drivers.
+While at it also drop unnecessary qca8k_priv cast for void pointers.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 93 ------------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 93 ++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 9 +++
+ 3 files changed, 102 insertions(+), 93 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -2049,97 +2049,6 @@ exit:
+ }
+
+ static void
+-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u32 stp_state;
+-
+- switch (state) {
+- case BR_STATE_DISABLED:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED;
+- break;
+- case BR_STATE_BLOCKING:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING;
+- break;
+- case BR_STATE_LISTENING:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING;
+- break;
+- case BR_STATE_LEARNING:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
+- break;
+- case BR_STATE_FORWARDING:
+- default:
+- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
+- break;
+- }
+-
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
+-}
+-
+-static int
+-qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int port_mask, cpu_port;
+- int i, ret;
+-
+- cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+- port_mask = BIT(cpu_port);
+-
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- if (dsa_is_cpu_port(ds, i))
+- continue;
+- if (dsa_to_port(ds, i)->bridge_dev != br)
+- continue;
+- /* Add this port to the portvlan mask of the other ports
+- * in the bridge
+- */
+- ret = regmap_set_bits(priv->regmap,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
+- if (ret)
+- return ret;
+- if (i != port)
+- port_mask |= BIT(i);
+- }
+-
+- /* Add all other ports to this ports portvlan mask */
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_MEMBER, port_mask);
+-
+- return ret;
+-}
+-
+-static void
+-qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- int cpu_port, i;
+-
+- cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
+-
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- if (dsa_is_cpu_port(ds, i))
+- continue;
+- if (dsa_to_port(ds, i)->bridge_dev != br)
+- continue;
+- /* Remove this port to the portvlan mask of the other ports
+- * in the bridge
+- */
+- regmap_clear_bits(priv->regmap,
+- QCA8K_PORT_LOOKUP_CTRL(i),
+- BIT(port));
+- }
+-
+- /* Set the cpu port to be the only one in the portvlan mask of
+- * this port
+- */
+- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+-}
+-
+-static void
+ qca8k_port_fast_age(struct dsa_switch *ds, int port)
+ {
+ struct qca8k_priv *priv = ds->priv;
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -9,6 +9,7 @@
+ #include <linux/netdevice.h>
+ #include <linux/bitfield.h>
+ #include <net/dsa.h>
++#include <linux/if_bridge.h>
+
+ #include "qca8k.h"
+
+@@ -276,3 +277,93 @@ int qca8k_get_mac_eee(struct dsa_switch
+ /* Nothing to do on the port's MAC */
+ return 0;
+ }
++
++void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
++{
++ struct qca8k_priv *priv = ds->priv;
++ u32 stp_state;
++
++ switch (state) {
++ case BR_STATE_DISABLED:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED;
++ break;
++ case BR_STATE_BLOCKING:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING;
++ break;
++ case BR_STATE_LISTENING:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING;
++ break;
++ case BR_STATE_LEARNING:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
++ break;
++ case BR_STATE_FORWARDING:
++ default:
++ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
++ break;
++ }
++
++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
++}
++
++int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
++ struct net_device *br)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int port_mask, cpu_port;
++ int i, ret;
++
++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
++ port_mask = BIT(cpu_port);
++
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
++ if (dsa_to_port(ds, i)->bridge_dev != br)
++ continue;
++ /* Add this port to the portvlan mask of the other ports
++ * in the bridge
++ */
++ ret = regmap_set_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
++ if (ret)
++ return ret;
++ if (i != port)
++ port_mask |= BIT(i);
++ }
++
++ /* Add all other ports to this ports portvlan mask */
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_MEMBER, port_mask);
++
++ return ret;
++}
++
++void qca8k_port_bridge_leave(struct dsa_switch *ds, int port,
++ struct net_device *br)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int cpu_port, i;
++
++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
++
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ if (dsa_is_cpu_port(ds, i))
++ continue;
++ if (dsa_to_port(ds, i)->bridge_dev != br)
++ continue;
++ /* Remove this port to the portvlan mask of the other ports
++ * in the bridge
++ */
++ regmap_clear_bits(priv->regmap,
++ QCA8K_PORT_LOOKUP_CTRL(i),
++ BIT(port));
++ }
++
++ /* Set the cpu port to be the only one in the portvlan mask of
++ * this port
++ */
++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -446,4 +446,11 @@ int qca8k_get_sset_count(struct dsa_swit
+ int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee);
+ int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
+
++/* Common bridge function */
++void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
++int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
++ struct net_device *br);
++void qca8k_port_bridge_leave(struct dsa_switch *ds, int port,
++ struct net_device *br);
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch b/target/linux/generic/backport-6.1/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch
new file mode 100644
index 0000000000..e3414408d6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch
@@ -0,0 +1,227 @@
+From b3a302b171f73425b41de8d3357fae3fa7057322 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:18 +0200
+Subject: [PATCH 09/14] net: dsa: qca8k: move set age/MTU/port enable/disable
+ functions to common code
+
+The same set age, MTU and port enable/disable function are used by
+driver based on qca8k family switch.
+Move them to common code to make them accessible also by other drivers.
+While at it also drop unnecessary qca8k_priv cast for void pointers.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 88 ------------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 85 +++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 12 ++++
+ 3 files changed, 97 insertions(+), 88 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -2059,94 +2059,6 @@ qca8k_port_fast_age(struct dsa_switch *d
+ }
+
+ static int
+-qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- unsigned int secs = msecs / 1000;
+- u32 val;
+-
+- /* AGE_TIME reg is set in 7s step */
+- val = secs / 7;
+-
+- /* Handle case with 0 as val to NOT disable
+- * learning
+- */
+- if (!val)
+- val = 1;
+-
+- return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK,
+- QCA8K_ATU_AGE_TIME(val));
+-}
+-
+-static int
+-qca8k_port_enable(struct dsa_switch *ds, int port,
+- struct phy_device *phy)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+-
+- qca8k_port_set_status(priv, port, 1);
+- priv->port_enabled_map |= BIT(port);
+-
+- if (dsa_is_user_port(ds, port))
+- phy_support_asym_pause(phy);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_port_disable(struct dsa_switch *ds, int port)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+-
+- qca8k_port_set_status(priv, port, 0);
+- priv->port_enabled_map &= ~BIT(port);
+-}
+-
+-static int
+-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- /* We have only have a general MTU setting.
+- * DSA always set the CPU port's MTU to the largest MTU of the slave
+- * ports.
+- * Setting MTU just for the CPU port is sufficient to correctly set a
+- * value for every port.
+- */
+- if (!dsa_is_cpu_port(ds, port))
+- return 0;
+-
+- /* To change the MAX_FRAME_SIZE the cpu ports must be off or
+- * the switch panics.
+- * Turn off both cpu ports before applying the new value to prevent
+- * this.
+- */
+- if (priv->port_enabled_map & BIT(0))
+- qca8k_port_set_status(priv, 0, 0);
+-
+- if (priv->port_enabled_map & BIT(6))
+- qca8k_port_set_status(priv, 6, 0);
+-
+- /* Include L2 header / FCS length */
+- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN);
+-
+- if (priv->port_enabled_map & BIT(0))
+- qca8k_port_set_status(priv, 0, 1);
+-
+- if (priv->port_enabled_map & BIT(6))
+- qca8k_port_set_status(priv, 6, 1);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_port_max_mtu(struct dsa_switch *ds, int port)
+-{
+- return QCA8K_MAX_MTU;
+-}
+-
+-static int
+ qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
+ u16 port_mask, u16 vid)
+ {
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -367,3 +367,88 @@ void qca8k_port_bridge_leave(struct dsa_
+ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+ }
++
++int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
++{
++ struct qca8k_priv *priv = ds->priv;
++ unsigned int secs = msecs / 1000;
++ u32 val;
++
++ /* AGE_TIME reg is set in 7s step */
++ val = secs / 7;
++
++ /* Handle case with 0 as val to NOT disable
++ * learning
++ */
++ if (!val)
++ val = 1;
++
++ return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL,
++ QCA8K_ATU_AGE_TIME_MASK,
++ QCA8K_ATU_AGE_TIME(val));
++}
++
++int qca8k_port_enable(struct dsa_switch *ds, int port,
++ struct phy_device *phy)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ qca8k_port_set_status(priv, port, 1);
++ priv->port_enabled_map |= BIT(port);
++
++ if (dsa_is_user_port(ds, port))
++ phy_support_asym_pause(phy);
++
++ return 0;
++}
++
++void qca8k_port_disable(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ qca8k_port_set_status(priv, port, 0);
++ priv->port_enabled_map &= ~BIT(port);
++}
++
++int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ /* We have only have a general MTU setting.
++ * DSA always set the CPU port's MTU to the largest MTU of the slave
++ * ports.
++ * Setting MTU just for the CPU port is sufficient to correctly set a
++ * value for every port.
++ */
++ if (!dsa_is_cpu_port(ds, port))
++ return 0;
++
++ /* To change the MAX_FRAME_SIZE the cpu ports must be off or
++ * the switch panics.
++ * Turn off both cpu ports before applying the new value to prevent
++ * this.
++ */
++ if (priv->port_enabled_map & BIT(0))
++ qca8k_port_set_status(priv, 0, 0);
++
++ if (priv->port_enabled_map & BIT(6))
++ qca8k_port_set_status(priv, 6, 0);
++
++ /* Include L2 header / FCS length */
++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu +
++ ETH_HLEN + ETH_FCS_LEN);
++
++ if (priv->port_enabled_map & BIT(0))
++ qca8k_port_set_status(priv, 0, 1);
++
++ if (priv->port_enabled_map & BIT(6))
++ qca8k_port_set_status(priv, 6, 1);
++
++ return ret;
++}
++
++int qca8k_port_max_mtu(struct dsa_switch *ds, int port)
++{
++ return QCA8K_MAX_MTU;
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -453,4 +453,16 @@ int qca8k_port_bridge_join(struct dsa_sw
+ void qca8k_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct net_device *br);
+
++/* Common port enable/disable function */
++int qca8k_port_enable(struct dsa_switch *ds, int port,
++ struct phy_device *phy);
++void qca8k_port_disable(struct dsa_switch *ds, int port);
++
++/* Common MTU function */
++int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu);
++int qca8k_port_max_mtu(struct dsa_switch *ds, int port);
++
++/* Common fast age function */
++int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs);
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch b/target/linux/generic/backport-6.1/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch
new file mode 100644
index 0000000000..96468ae74e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch
@@ -0,0 +1,704 @@
+From 2e5bd96eea86a246b4de3bf756f7a11b43e6187d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:19 +0200
+Subject: [PATCH 10/14] net: dsa: qca8k: move port FDB/MDB function to common
+ code
+
+The same port FDB/MDB function are used by drivers based on qca8k family
+switch. Move them to common code to make them accessible also by other
+drivers.
+Also drop bulk read/write functions and make them static
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 306 -----------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 297 +++++++++++++++++++++++++++-
+ drivers/net/dsa/qca/qca8k.h | 25 ++-
+ 3 files changed, 317 insertions(+), 311 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -442,217 +442,6 @@ static struct regmap_config qca8k_regmap
+ };
+
+ static int
+-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
+-{
+- u32 reg[3];
+- int ret;
+-
+- /* load the ARL table into an array */
+- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
+- if (ret)
+- return ret;
+-
+- /* vid - 83:72 */
+- fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
+- /* aging - 67:64 */
+- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
+- /* portmask - 54:48 */
+- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
+- /* mac - 47:0 */
+- fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
+- fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
+- fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
+- fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
+- fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
+- fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
+- u8 aging)
+-{
+- u32 reg[3] = { 0 };
+-
+- /* vid - 83:72 */
+- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
+- /* aging - 67:64 */
+- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
+- /* portmask - 54:48 */
+- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
+- /* mac - 47:0 */
+- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
+- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
+- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
+-
+- /* load the array into the ARL table */
+- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
+-}
+-
+-static int
+-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
+-{
+- u32 reg;
+- int ret;
+-
+- /* Set the command and FDB index */
+- reg = QCA8K_ATU_FUNC_BUSY;
+- reg |= cmd;
+- if (port >= 0) {
+- reg |= QCA8K_ATU_FUNC_PORT_EN;
+- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
+- }
+-
+- /* Write the function register triggering the table access */
+- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
+- if (ret)
+- return ret;
+-
+- /* wait for completion */
+- ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY);
+- if (ret)
+- return ret;
+-
+- /* Check for table full violation when adding an entry */
+- if (cmd == QCA8K_FDB_LOAD) {
+- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, &reg);
+- if (ret < 0)
+- return ret;
+- if (reg & QCA8K_ATU_FUNC_FULL)
+- return -1;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
+-{
+- int ret;
+-
+- qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
+- if (ret < 0)
+- return ret;
+-
+- return qca8k_fdb_read(priv, fdb);
+-}
+-
+-static int
+-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask,
+- u16 vid, u8 aging)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_write(priv, vid, port_mask, mac, aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid)
+-{
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_write(priv, vid, port_mask, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static void
+-qca8k_fdb_flush(struct qca8k_priv *priv)
+-{
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
+- mutex_unlock(&priv->reg_mutex);
+-}
+-
+-static int
+-qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
+- const u8 *mac, u16 vid)
+-{
+- struct qca8k_fdb fdb = { 0 };
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- qca8k_fdb_write(priv, vid, 0, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+- if (ret < 0)
+- goto exit;
+-
+- ret = qca8k_fdb_read(priv, &fdb);
+- if (ret < 0)
+- goto exit;
+-
+- /* Rule exist. Delete first */
+- if (!fdb.aging) {
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- if (ret)
+- goto exit;
+- }
+-
+- /* Add port to fdb portmask */
+- fdb.port_mask |= port_mask;
+-
+- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+-qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
+- const u8 *mac, u16 vid)
+-{
+- struct qca8k_fdb fdb = { 0 };
+- int ret;
+-
+- mutex_lock(&priv->reg_mutex);
+-
+- qca8k_fdb_write(priv, vid, 0, mac, 0);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
+- if (ret < 0)
+- goto exit;
+-
+- /* Rule doesn't exist. Why delete? */
+- if (!fdb.aging) {
+- ret = -EINVAL;
+- goto exit;
+- }
+-
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
+- if (ret)
+- goto exit;
+-
+- /* Only port in the rule is this port. Don't re insert */
+- if (fdb.port_mask == port_mask)
+- goto exit;
+-
+- /* Remove port from port mask */
+- fdb.port_mask &= ~port_mask;
+-
+- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
+- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
+-
+-exit:
+- mutex_unlock(&priv->reg_mutex);
+- return ret;
+-}
+-
+-static int
+ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+ {
+ u32 reg;
+@@ -2048,97 +1837,6 @@ exit:
+ return ret;
+ }
+
+-static void
+-qca8k_port_fast_age(struct dsa_switch *ds, int port)
+-{
+- struct qca8k_priv *priv = ds->priv;
+-
+- mutex_lock(&priv->reg_mutex);
+- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
+- mutex_unlock(&priv->reg_mutex);
+-}
+-
+-static int
+-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
+- u16 port_mask, u16 vid)
+-{
+- /* Set the vid to the port vlan id if no vid is set */
+- if (!vid)
+- vid = QCA8K_PORT_VID_DEF;
+-
+- return qca8k_fdb_add(priv, addr, port_mask, vid,
+- QCA8K_ATU_STATUS_STATIC);
+-}
+-
+-static int
+-qca8k_port_fdb_add(struct dsa_switch *ds, int port,
+- const unsigned char *addr, u16 vid)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u16 port_mask = BIT(port);
+-
+- return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
+-}
+-
+-static int
+-qca8k_port_fdb_del(struct dsa_switch *ds, int port,
+- const unsigned char *addr, u16 vid)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- u16 port_mask = BIT(port);
+-
+- if (!vid)
+- vid = QCA8K_PORT_VID_DEF;
+-
+- return qca8k_fdb_del(priv, addr, port_mask, vid);
+-}
+-
+-static int
+-qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
+- dsa_fdb_dump_cb_t *cb, void *data)
+-{
+- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+- struct qca8k_fdb _fdb = { 0 };
+- int cnt = QCA8K_NUM_FDB_RECORDS;
+- bool is_static;
+- int ret = 0;
+-
+- mutex_lock(&priv->reg_mutex);
+- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
+- if (!_fdb.aging)
+- break;
+- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
+- ret = cb(_fdb.mac, _fdb.vid, is_static, data);
+- if (ret)
+- break;
+- }
+- mutex_unlock(&priv->reg_mutex);
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_port_mdb_add(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_mdb *mdb)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- const u8 *addr = mdb->addr;
+- u16 vid = mdb->vid;
+-
+- return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
+-}
+-
+-static int
+-qca8k_port_mdb_del(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_mdb *mdb)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- const u8 *addr = mdb->addr;
+- u16 vid = mdb->vid;
+-
+- return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
+-}
+-
+ static int
+ qca8k_port_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror,
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -103,7 +103,7 @@ const struct regmap_access_table qca8k_r
+ };
+
+ /* TODO: remove these extra ops when we can support regmap bulk read/write */
+-int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++static int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+ {
+ int i, count = len / sizeof(u32), ret;
+
+@@ -121,7 +121,7 @@ int qca8k_bulk_read(struct qca8k_priv *p
+ }
+
+ /* TODO: remove these extra ops when we can support regmap bulk read/write */
+-int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
++static int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+ {
+ int i, count = len / sizeof(u32), ret;
+ u32 tmp;
+@@ -149,6 +149,211 @@ int qca8k_busy_wait(struct qca8k_priv *p
+ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);
+ }
+
++static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
++{
++ u32 reg[3];
++ int ret;
++
++ /* load the ARL table into an array */
++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++ if (ret)
++ return ret;
++
++ /* vid - 83:72 */
++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);
++ /* aging - 67:64 */
++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);
++ /* portmask - 54:48 */
++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);
++ /* mac - 47:0 */
++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]);
++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]);
++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]);
++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]);
++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]);
++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);
++
++ return 0;
++}
++
++static void qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask,
++ const u8 *mac, u8 aging)
++{
++ u32 reg[3] = { 0 };
++
++ /* vid - 83:72 */
++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);
++ /* aging - 67:64 */
++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);
++ /* portmask - 54:48 */
++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);
++ /* mac - 47:0 */
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]);
++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]);
++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);
++
++ /* load the array into the ARL table */
++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg));
++}
++
++static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd,
++ int port)
++{
++ u32 reg;
++ int ret;
++
++ /* Set the command and FDB index */
++ reg = QCA8K_ATU_FUNC_BUSY;
++ reg |= cmd;
++ if (port >= 0) {
++ reg |= QCA8K_ATU_FUNC_PORT_EN;
++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);
++ }
++
++ /* Write the function register triggering the table access */
++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
++ if (ret)
++ return ret;
++
++ /* wait for completion */
++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY);
++ if (ret)
++ return ret;
++
++ /* Check for table full violation when adding an entry */
++ if (cmd == QCA8K_FDB_LOAD) {
++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, &reg);
++ if (ret < 0)
++ return ret;
++ if (reg & QCA8K_ATU_FUNC_FULL)
++ return -1;
++ }
++
++ return 0;
++}
++
++static int qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb,
++ int port)
++{
++ int ret;
++
++ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
++ if (ret < 0)
++ return ret;
++
++ return qca8k_fdb_read(priv, fdb);
++}
++
++static int qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac,
++ u16 port_mask, u16 vid, u8 aging)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_write(priv, vid, port_mask, mac, aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac,
++ u16 port_mask, u16 vid)
++{
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_write(priv, vid, port_mask, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++void qca8k_fdb_flush(struct qca8k_priv *priv)
++{
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1);
++ mutex_unlock(&priv->reg_mutex);
++}
++
++static int qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ ret = qca8k_fdb_read(priv, &fdb);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule exist. Delete first */
++ if (!fdb.aging) {
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++ }
++
++ /* Add port to fdb portmask */
++ fdb.port_mask |= port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
++static int qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask,
++ const u8 *mac, u16 vid)
++{
++ struct qca8k_fdb fdb = { 0 };
++ int ret;
++
++ mutex_lock(&priv->reg_mutex);
++
++ qca8k_fdb_write(priv, vid, 0, mac, 0);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1);
++ if (ret < 0)
++ goto exit;
++
++ /* Rule doesn't exist. Why delete? */
++ if (!fdb.aging) {
++ ret = -EINVAL;
++ goto exit;
++ }
++
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1);
++ if (ret)
++ goto exit;
++
++ /* Only port in the rule is this port. Don't re insert */
++ if (fdb.port_mask == port_mask)
++ goto exit;
++
++ /* Remove port from port mask */
++ fdb.port_mask &= ~port_mask;
++
++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging);
++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1);
++
++exit:
++ mutex_unlock(&priv->reg_mutex);
++ return ret;
++}
++
+ int qca8k_mib_init(struct qca8k_priv *priv)
+ {
+ int ret;
+@@ -368,6 +573,15 @@ void qca8k_port_bridge_leave(struct dsa_
+ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));
+ }
+
++void qca8k_port_fast_age(struct dsa_switch *ds, int port)
++{
++ struct qca8k_priv *priv = ds->priv;
++
++ mutex_lock(&priv->reg_mutex);
++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port);
++ mutex_unlock(&priv->reg_mutex);
++}
++
+ int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+ {
+ struct qca8k_priv *priv = ds->priv;
+@@ -452,3 +666,78 @@ int qca8k_port_max_mtu(struct dsa_switch
+ {
+ return QCA8K_MAX_MTU;
+ }
++
++int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
++ u16 port_mask, u16 vid)
++{
++ /* Set the vid to the port vlan id if no vid is set */
++ if (!vid)
++ vid = QCA8K_PORT_VID_DEF;
++
++ return qca8k_fdb_add(priv, addr, port_mask, vid,
++ QCA8K_ATU_STATUS_STATIC);
++}
++
++int qca8k_port_fdb_add(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u16 port_mask = BIT(port);
++
++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
++}
++
++int qca8k_port_fdb_del(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ u16 port_mask = BIT(port);
++
++ if (!vid)
++ vid = QCA8K_PORT_VID_DEF;
++
++ return qca8k_fdb_del(priv, addr, port_mask, vid);
++}
++
++int qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
++ dsa_fdb_dump_cb_t *cb, void *data)
++{
++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++ struct qca8k_fdb _fdb = { 0 };
++ int cnt = QCA8K_NUM_FDB_RECORDS;
++ bool is_static;
++ int ret = 0;
++
++ mutex_lock(&priv->reg_mutex);
++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) {
++ if (!_fdb.aging)
++ break;
++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC);
++ ret = cb(_fdb.mac, _fdb.vid, is_static, data);
++ if (ret)
++ break;
++ }
++ mutex_unlock(&priv->reg_mutex);
++
++ return 0;
++}
++
++int qca8k_port_mdb_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid);
++}
++
++int qca8k_port_mdb_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb)
++{
++ struct qca8k_priv *priv = ds->priv;
++ const u8 *addr = mdb->addr;
++ u16 vid = mdb->vid;
++
++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -430,11 +430,9 @@ int qca8k_read(struct qca8k_priv *priv,
+ int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val);
+ int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val);
+
+-int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
+-int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len);
+-
+ /* Common ops function */
+ int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask);
++void qca8k_fdb_flush(struct qca8k_priv *priv);
+
+ /* Common ethtool stats function */
+ void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data);
+@@ -463,6 +461,23 @@ int qca8k_port_change_mtu(struct dsa_swi
+ int qca8k_port_max_mtu(struct dsa_switch *ds, int port);
+
+ /* Common fast age function */
++void qca8k_port_fast_age(struct dsa_switch *ds, int port);
+ int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs);
+
++/* Common FDB function */
++int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr,
++ u16 port_mask, u16 vid);
++int qca8k_port_fdb_add(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid);
++int qca8k_port_fdb_del(struct dsa_switch *ds, int port,
++ const unsigned char *addr, u16 vid);
++int qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
++ dsa_fdb_dump_cb_t *cb, void *data);
++
++/* Common MDB function */
++int qca8k_port_mdb_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb);
++int qca8k_port_mdb_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_mdb *mdb);
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch b/target/linux/generic/backport-6.1/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch
new file mode 100644
index 0000000000..c1336d4a92
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch
@@ -0,0 +1,232 @@
+From 742d37a84d3f7bb60d9b2d9ada9ad4e599f65ebf Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:20 +0200
+Subject: [PATCH 11/14] net: dsa: qca8k: move port mirror functions to common
+ code
+
+The same port mirror functions are used by drivers based on qca8k family
+switch. Move them to common code to make them accessible also by other
+drivers.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 93 ------------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 91 +++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 7 +++
+ 3 files changed, 98 insertions(+), 93 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1838,99 +1838,6 @@ exit:
+ }
+
+ static int
+-qca8k_port_mirror_add(struct dsa_switch *ds, int port,
+- struct dsa_mall_mirror_tc_entry *mirror,
+- bool ingress)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int monitor_port, ret;
+- u32 reg, val;
+-
+- /* Check for existent entry */
+- if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
+- return -EEXIST;
+-
+- ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
+- if (ret)
+- return ret;
+-
+- /* QCA83xx can have only one port set to mirror mode.
+- * Check that the correct port is requested and return error otherwise.
+- * When no mirror port is set, the values is set to 0xF
+- */
+- monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+- if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
+- return -EEXIST;
+-
+- /* Set the monitor port */
+- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
+- mirror->to_local_port);
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+- if (ret)
+- return ret;
+-
+- if (ingress) {
+- reg = QCA8K_PORT_LOOKUP_CTRL(port);
+- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+- } else {
+- reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+- }
+-
+- ret = regmap_update_bits(priv->regmap, reg, val, val);
+- if (ret)
+- return ret;
+-
+- /* Track mirror port for tx and rx to decide when the
+- * mirror port has to be disabled.
+- */
+- if (ingress)
+- priv->mirror_rx |= BIT(port);
+- else
+- priv->mirror_tx |= BIT(port);
+-
+- return 0;
+-}
+-
+-static void
+-qca8k_port_mirror_del(struct dsa_switch *ds, int port,
+- struct dsa_mall_mirror_tc_entry *mirror)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- u32 reg, val;
+- int ret;
+-
+- if (mirror->ingress) {
+- reg = QCA8K_PORT_LOOKUP_CTRL(port);
+- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
+- } else {
+- reg = QCA8K_REG_PORT_HOL_CTRL1(port);
+- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
+- }
+-
+- ret = regmap_clear_bits(priv->regmap, reg, val);
+- if (ret)
+- goto err;
+-
+- if (mirror->ingress)
+- priv->mirror_rx &= ~BIT(port);
+- else
+- priv->mirror_tx &= ~BIT(port);
+-
+- /* No port set to send packet to mirror port. Disable mirror port */
+- if (!priv->mirror_rx && !priv->mirror_tx) {
+- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
+- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
+- if (ret)
+- goto err;
+- }
+-err:
+- dev_err(priv->dev, "Failed to del mirror port from %d", port);
+-}
+-
+-static int
+ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+ struct netlink_ext_ack *extack)
+ {
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -741,3 +741,94 @@ int qca8k_port_mdb_del(struct dsa_switch
+
+ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
+ }
++
++int qca8k_port_mirror_add(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror,
++ bool ingress)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int monitor_port, ret;
++ u32 reg, val;
++
++ /* Check for existent entry */
++ if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))
++ return -EEXIST;
++
++ ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val);
++ if (ret)
++ return ret;
++
++ /* QCA83xx can have only one port set to mirror mode.
++ * Check that the correct port is requested and return error otherwise.
++ * When no mirror port is set, the values is set to 0xF
++ */
++ monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (monitor_port != 0xF && monitor_port != mirror->to_local_port)
++ return -EEXIST;
++
++ /* Set the monitor port */
++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM,
++ mirror->to_local_port);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (ret)
++ return ret;
++
++ if (ingress) {
++ reg = QCA8K_PORT_LOOKUP_CTRL(port);
++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
++ } else {
++ reg = QCA8K_REG_PORT_HOL_CTRL1(port);
++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
++ }
++
++ ret = regmap_update_bits(priv->regmap, reg, val, val);
++ if (ret)
++ return ret;
++
++ /* Track mirror port for tx and rx to decide when the
++ * mirror port has to be disabled.
++ */
++ if (ingress)
++ priv->mirror_rx |= BIT(port);
++ else
++ priv->mirror_tx |= BIT(port);
++
++ return 0;
++}
++
++void qca8k_port_mirror_del(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror)
++{
++ struct qca8k_priv *priv = ds->priv;
++ u32 reg, val;
++ int ret;
++
++ if (mirror->ingress) {
++ reg = QCA8K_PORT_LOOKUP_CTRL(port);
++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN;
++ } else {
++ reg = QCA8K_REG_PORT_HOL_CTRL1(port);
++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN;
++ }
++
++ ret = regmap_clear_bits(priv->regmap, reg, val);
++ if (ret)
++ goto err;
++
++ if (mirror->ingress)
++ priv->mirror_rx &= ~BIT(port);
++ else
++ priv->mirror_tx &= ~BIT(port);
++
++ /* No port set to send packet to mirror port. Disable mirror port */
++ if (!priv->mirror_rx && !priv->mirror_tx) {
++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF);
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0,
++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val);
++ if (ret)
++ goto err;
++ }
++err:
++ dev_err(priv->dev, "Failed to del mirror port from %d", port);
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -480,4 +480,11 @@ int qca8k_port_mdb_add(struct dsa_switch
+ int qca8k_port_mdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb);
+
++/* Common port mirror function */
++int qca8k_port_mirror_add(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror,
++ bool ingress);
++void qca8k_port_mirror_del(struct dsa_switch *ds, int port,
++ struct dsa_mall_mirror_tc_entry *mirror);
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch b/target/linux/generic/backport-6.1/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch
new file mode 100644
index 0000000000..898010f950
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch
@@ -0,0 +1,448 @@
+From c5290f636624b98e76a82bd63ffec0a8a9daa620 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:21 +0200
+Subject: [PATCH 12/14] net: dsa: qca8k: move port VLAN functions to common
+ code
+
+The same port VLAN functions are used by drivers based on qca8k family
+switch. Move them to common code to make them accessible also by other
+drivers.
+Also drop exposing busy_wait and make it static.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 182 -----------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 179 +++++++++++++++++++++++++++-
+ drivers/net/dsa/qca/qca8k.h | 10 +-
+ 3 files changed, 187 insertions(+), 184 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -15,7 +15,6 @@
+ #include <linux/of_net.h>
+ #include <linux/of_mdio.h>
+ #include <linux/of_platform.h>
+-#include <linux/if_bridge.h>
+ #include <linux/mdio.h>
+ #include <linux/phylink.h>
+ #include <linux/gpio/consumer.h>
+@@ -442,122 +441,6 @@ static struct regmap_config qca8k_regmap
+ };
+
+ static int
+-qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
+-{
+- u32 reg;
+- int ret;
+-
+- /* Set the command and VLAN index */
+- reg = QCA8K_VTU_FUNC1_BUSY;
+- reg |= cmd;
+- reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
+-
+- /* Write the function register triggering the table access */
+- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
+- if (ret)
+- return ret;
+-
+- /* wait for completion */
+- ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
+- if (ret)
+- return ret;
+-
+- /* Check for table full violation when adding an entry */
+- if (cmd == QCA8K_VLAN_LOAD) {
+- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, &reg);
+- if (ret < 0)
+- return ret;
+- if (reg & QCA8K_VTU_FUNC1_FULL)
+- return -ENOMEM;
+- }
+-
+- return 0;
+-}
+-
+-static int
+-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
+-{
+- u32 reg;
+- int ret;
+-
+- /*
+- We do the right thing with VLAN 0 and treat it as untagged while
+- preserving the tag on egress.
+- */
+- if (vid == 0)
+- return 0;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+- if (ret < 0)
+- goto out;
+-
+- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+- if (ret < 0)
+- goto out;
+- reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
+- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+- if (untagged)
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
+- else
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
+-
+- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+- if (ret)
+- goto out;
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+-
+-out:
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
+-{
+- u32 reg, mask;
+- int ret, i;
+- bool del;
+-
+- mutex_lock(&priv->reg_mutex);
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
+- if (ret < 0)
+- goto out;
+-
+- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
+- if (ret < 0)
+- goto out;
+- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
+- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
+-
+- /* Check if we're the last member to be removed */
+- del = true;
+- for (i = 0; i < QCA8K_NUM_PORTS; i++) {
+- mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
+-
+- if ((reg & mask) != mask) {
+- del = false;
+- break;
+- }
+- }
+-
+- if (del) {
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
+- } else {
+- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
+- if (ret)
+- goto out;
+- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
+- }
+-
+-out:
+- mutex_unlock(&priv->reg_mutex);
+-
+- return ret;
+-}
+-
+-static int
+ qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
+ struct sk_buff *read_skb, u32 *val)
+ {
+@@ -1836,71 +1719,6 @@ exit:
+
+ return ret;
+ }
+-
+-static int
+-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+- struct netlink_ext_ack *extack)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- if (vlan_filtering) {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+- QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
+- } else {
+- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
+- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
+- QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
+- }
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_port_vlan_add(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_vlan *vlan,
+- struct netlink_ext_ack *extack)
+-{
+- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
+- if (ret) {
+- dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
+- return ret;
+- }
+-
+- if (pvid) {
+- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
+- QCA8K_EGREES_VLAN_PORT_MASK(port),
+- QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
+- if (ret)
+- return ret;
+-
+- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
+- QCA8K_PORT_VLAN_CVID(vlan->vid) |
+- QCA8K_PORT_VLAN_SVID(vlan->vid));
+- }
+-
+- return ret;
+-}
+-
+-static int
+-qca8k_port_vlan_del(struct dsa_switch *ds, int port,
+- const struct switchdev_obj_port_vlan *vlan)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret;
+-
+- ret = qca8k_vlan_del(priv, port, vlan->vid);
+- if (ret)
+- dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
+-
+- return ret;
+-}
+
+ static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
+ {
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -141,7 +141,7 @@ static int qca8k_bulk_write(struct qca8k
+ return 0;
+ }
+
+-int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
++static int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
+ {
+ u32 val;
+
+@@ -354,6 +354,120 @@ exit:
+ return ret;
+ }
+
++static int qca8k_vlan_access(struct qca8k_priv *priv,
++ enum qca8k_vlan_cmd cmd, u16 vid)
++{
++ u32 reg;
++ int ret;
++
++ /* Set the command and VLAN index */
++ reg = QCA8K_VTU_FUNC1_BUSY;
++ reg |= cmd;
++ reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
++
++ /* Write the function register triggering the table access */
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
++ if (ret)
++ return ret;
++
++ /* wait for completion */
++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
++ if (ret)
++ return ret;
++
++ /* Check for table full violation when adding an entry */
++ if (cmd == QCA8K_VLAN_LOAD) {
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, &reg);
++ if (ret < 0)
++ return ret;
++ if (reg & QCA8K_VTU_FUNC1_FULL)
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static int qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid,
++ bool untagged)
++{
++ u32 reg;
++ int ret;
++
++ /* We do the right thing with VLAN 0 and treat it as untagged while
++ * preserving the tag on egress.
++ */
++ if (vid == 0)
++ return 0;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
++ if (ret < 0)
++ goto out;
++
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
++ if (ret < 0)
++ goto out;
++ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++ if (untagged)
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
++ else
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
++
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ if (ret)
++ goto out;
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
++
++out:
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
++static int qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
++{
++ u32 reg, mask;
++ int ret, i;
++ bool del;
++
++ mutex_lock(&priv->reg_mutex);
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
++ if (ret < 0)
++ goto out;
++
++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, &reg);
++ if (ret < 0)
++ goto out;
++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
++
++ /* Check if we're the last member to be removed */
++ del = true;
++ for (i = 0; i < QCA8K_NUM_PORTS; i++) {
++ mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
++
++ if ((reg & mask) != mask) {
++ del = false;
++ break;
++ }
++ }
++
++ if (del) {
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
++ } else {
++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
++ if (ret)
++ goto out;
++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
++ }
++
++out:
++ mutex_unlock(&priv->reg_mutex);
++
++ return ret;
++}
++
+ int qca8k_mib_init(struct qca8k_priv *priv)
+ {
+ int ret;
+@@ -832,3 +946,66 @@ void qca8k_port_mirror_del(struct dsa_sw
+ err:
+ dev_err(priv->dev, "Failed to del mirror port from %d", port);
+ }
++
++int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port,
++ bool vlan_filtering,
++ struct netlink_ext_ack *extack)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ if (vlan_filtering) {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
++ } else {
++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
++ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
++ }
++
++ return ret;
++}
++
++int qca8k_port_vlan_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_vlan *vlan,
++ struct netlink_ext_ack *extack)
++{
++ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
++ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
++ if (ret) {
++ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
++ return ret;
++ }
++
++ if (pvid) {
++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
++ QCA8K_EGREES_VLAN_PORT_MASK(port),
++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
++ if (ret)
++ return ret;
++
++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
++ QCA8K_PORT_VLAN_CVID(vlan->vid) |
++ QCA8K_PORT_VLAN_SVID(vlan->vid));
++ }
++
++ return ret;
++}
++
++int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_vlan *vlan)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret;
++
++ ret = qca8k_vlan_del(priv, port, vlan->vid);
++ if (ret)
++ dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
++
++ return ret;
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -431,7 +431,6 @@ int qca8k_write(struct qca8k_priv *priv,
+ int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val);
+
+ /* Common ops function */
+-int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask);
+ void qca8k_fdb_flush(struct qca8k_priv *priv);
+
+ /* Common ethtool stats function */
+@@ -487,4 +486,13 @@ int qca8k_port_mirror_add(struct dsa_swi
+ void qca8k_port_mirror_del(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror);
+
++/* Common port VLAN function */
++int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
++ struct netlink_ext_ack *extack);
++int qca8k_port_vlan_add(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_vlan *vlan,
++ struct netlink_ext_ack *extack);
++int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
++ const struct switchdev_obj_port_vlan *vlan);
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch b/target/linux/generic/backport-6.1/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch
new file mode 100644
index 0000000000..1802b17eaa
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch
@@ -0,0 +1,384 @@
+From e9bbf019af44b204b71ef8edf224002550aab641 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:22 +0200
+Subject: [PATCH 13/14] net: dsa: qca8k: move port LAG functions to common code
+
+The same port LAG functions are used by drivers based on qca8k family
+switch. Move them to common code to make them accessible also by other
+drivers.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 168 -----------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 165 ++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 6 ++
+ 3 files changed, 171 insertions(+), 168 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1743,178 +1743,6 @@ qca8k_get_tag_protocol(struct dsa_switch
+ return DSA_TAG_PROTO_QCA;
+ }
+
+-static bool
+-qca8k_lag_can_offload(struct dsa_switch *ds,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- struct dsa_port *dp;
+- int id, members = 0;
+-
+- id = dsa_lag_id(ds->dst, lag);
+- if (id < 0 || id >= ds->num_lag_ids)
+- return false;
+-
+- dsa_lag_foreach_port(dp, ds->dst, lag)
+- /* Includes the port joining the LAG */
+- members++;
+-
+- if (members > QCA8K_NUM_PORTS_FOR_LAG)
+- return false;
+-
+- if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+- return false;
+-
+- if (info->hash_type != NETDEV_LAG_HASH_L2 &&
+- info->hash_type != NETDEV_LAG_HASH_L23)
+- return false;
+-
+- return true;
+-}
+-
+-static int
+-qca8k_lag_setup_hash(struct dsa_switch *ds,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- bool unique_lag = true;
+- u32 hash = 0;
+- int i, id;
+-
+- id = dsa_lag_id(ds->dst, lag);
+-
+- switch (info->hash_type) {
+- case NETDEV_LAG_HASH_L23:
+- hash |= QCA8K_TRUNK_HASH_SIP_EN;
+- hash |= QCA8K_TRUNK_HASH_DIP_EN;
+- fallthrough;
+- case NETDEV_LAG_HASH_L2:
+- hash |= QCA8K_TRUNK_HASH_SA_EN;
+- hash |= QCA8K_TRUNK_HASH_DA_EN;
+- break;
+- default: /* We should NEVER reach this */
+- return -EOPNOTSUPP;
+- }
+-
+- /* Check if we are the unique configured LAG */
+- dsa_lags_foreach_id(i, ds->dst)
+- if (i != id && dsa_lag_dev(ds->dst, i)) {
+- unique_lag = false;
+- break;
+- }
+-
+- /* Hash Mode is global. Make sure the same Hash Mode
+- * is set to all the 4 possible lag.
+- * If we are the unique LAG we can set whatever hash
+- * mode we want.
+- * To change hash mode it's needed to remove all LAG
+- * and change the mode with the latest.
+- */
+- if (unique_lag) {
+- priv->lag_hash_mode = hash;
+- } else if (priv->lag_hash_mode != hash) {
+- netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n");
+- return -EOPNOTSUPP;
+- }
+-
+- return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
+- QCA8K_TRUNK_HASH_MASK, hash);
+-}
+-
+-static int
+-qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
+- struct net_device *lag, bool delete)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret, id, i;
+- u32 val;
+-
+- id = dsa_lag_id(ds->dst, lag);
+-
+- /* Read current port member */
+- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
+- if (ret)
+- return ret;
+-
+- /* Shift val to the correct trunk */
+- val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
+- val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
+- if (delete)
+- val &= ~BIT(port);
+- else
+- val |= BIT(port);
+-
+- /* Update port member. With empty portmap disable trunk */
+- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
+- QCA8K_REG_GOL_TRUNK_MEMBER(id) |
+- QCA8K_REG_GOL_TRUNK_EN(id),
+- !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
+- val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
+-
+- /* Search empty member if adding or port on deleting */
+- for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
+- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
+- if (ret)
+- return ret;
+-
+- val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
+- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
+-
+- if (delete) {
+- /* If port flagged to be disabled assume this member is
+- * empty
+- */
+- if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+- continue;
+-
+- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
+- if (val != port)
+- continue;
+- } else {
+- /* If port flagged to be enabled assume this member is
+- * already set
+- */
+- if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+- continue;
+- }
+-
+- /* We have found the member to add/remove */
+- break;
+- }
+-
+- /* Set port in the correct port mask or disable port if in delete mode */
+- return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
+- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
+- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
+- !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
+- port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
+-}
+-
+-static int
+-qca8k_port_lag_join(struct dsa_switch *ds, int port,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- int ret;
+-
+- if (!qca8k_lag_can_offload(ds, lag, info))
+- return -EOPNOTSUPP;
+-
+- ret = qca8k_lag_setup_hash(ds, lag, info);
+- if (ret)
+- return ret;
+-
+- return qca8k_lag_refresh_portmap(ds, port, lag, false);
+-}
+-
+-static int
+-qca8k_port_lag_leave(struct dsa_switch *ds, int port,
+- struct net_device *lag)
+-{
+- return qca8k_lag_refresh_portmap(ds, port, lag, true);
+-}
+-
+ static void
+ qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
+ bool operational)
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -1009,3 +1009,169 @@ int qca8k_port_vlan_del(struct dsa_switc
+
+ return ret;
+ }
++
++static bool qca8k_lag_can_offload(struct dsa_switch *ds,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct dsa_port *dp;
++ int id, members = 0;
++
++ id = dsa_lag_id(ds->dst, lag);
++ if (id < 0 || id >= ds->num_lag_ids)
++ return false;
++
++ dsa_lag_foreach_port(dp, ds->dst, lag)
++ /* Includes the port joining the LAG */
++ members++;
++
++ if (members > QCA8K_NUM_PORTS_FOR_LAG)
++ return false;
++
++ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
++ return false;
++
++ if (info->hash_type != NETDEV_LAG_HASH_L2 &&
++ info->hash_type != NETDEV_LAG_HASH_L23)
++ return false;
++
++ return true;
++}
++
++static int qca8k_lag_setup_hash(struct dsa_switch *ds,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct qca8k_priv *priv = ds->priv;
++ bool unique_lag = true;
++ u32 hash = 0;
++ int i, id;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ switch (info->hash_type) {
++ case NETDEV_LAG_HASH_L23:
++ hash |= QCA8K_TRUNK_HASH_SIP_EN;
++ hash |= QCA8K_TRUNK_HASH_DIP_EN;
++ fallthrough;
++ case NETDEV_LAG_HASH_L2:
++ hash |= QCA8K_TRUNK_HASH_SA_EN;
++ hash |= QCA8K_TRUNK_HASH_DA_EN;
++ break;
++ default: /* We should NEVER reach this */
++ return -EOPNOTSUPP;
++ }
++
++ /* Check if we are the unique configured LAG */
++ dsa_lags_foreach_id(i, ds->dst)
++ if (i != id && dsa_lag_dev(ds->dst, i)) {
++ unique_lag = false;
++ break;
++ }
++
++ /* Hash Mode is global. Make sure the same Hash Mode
++ * is set to all the 4 possible lag.
++ * If we are the unique LAG we can set whatever hash
++ * mode we want.
++ * To change hash mode it's needed to remove all LAG
++ * and change the mode with the latest.
++ */
++ if (unique_lag) {
++ priv->lag_hash_mode = hash;
++ } else if (priv->lag_hash_mode != hash) {
++ netdev_err(lag, "Error: Mismatched Hash Mode across different lag is not supported\n");
++ return -EOPNOTSUPP;
++ }
++
++ return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
++ QCA8K_TRUNK_HASH_MASK, hash);
++}
++
++static int qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
++ struct net_device *lag, bool delete)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret, id, i;
++ u32 val;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ /* Read current port member */
++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
++ if (ret)
++ return ret;
++
++ /* Shift val to the correct trunk */
++ val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
++ val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
++ if (delete)
++ val &= ~BIT(port);
++ else
++ val |= BIT(port);
++
++ /* Update port member. With empty portmap disable trunk */
++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
++ QCA8K_REG_GOL_TRUNK_MEMBER(id) |
++ QCA8K_REG_GOL_TRUNK_EN(id),
++ !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
++ val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
++
++ /* Search empty member if adding or port on deleting */
++ for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
++ if (ret)
++ return ret;
++
++ val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
++
++ if (delete) {
++ /* If port flagged to be disabled assume this member is
++ * empty
++ */
++ if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
++ continue;
++
++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
++ if (val != port)
++ continue;
++ } else {
++ /* If port flagged to be enabled assume this member is
++ * already set
++ */
++ if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
++ continue;
++ }
++
++ /* We have found the member to add/remove */
++ break;
++ }
++
++ /* Set port in the correct port mask or disable port if in delete mode */
++ return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
++ !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
++ port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
++}
++
++int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ int ret;
++
++ if (!qca8k_lag_can_offload(ds, lag, info))
++ return -EOPNOTSUPP;
++
++ ret = qca8k_lag_setup_hash(ds, lag, info);
++ if (ret)
++ return ret;
++
++ return qca8k_lag_refresh_portmap(ds, port, lag, false);
++}
++
++int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
++ struct net_device *lag)
++{
++ return qca8k_lag_refresh_portmap(ds, port, lag, true);
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -495,4 +495,10 @@ int qca8k_port_vlan_add(struct dsa_switc
+ int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan);
+
++/* Common port LAG function */
++int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct net_device *lag,
++ struct netdev_lag_upper_info *info);
++int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
++ struct net_device *lag);
++
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/backport-6.1/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch b/target/linux/generic/backport-6.1/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch
new file mode 100644
index 0000000000..d6ec8b77e0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch
@@ -0,0 +1,102 @@
+From 9d1bcb1f293f1391302a109c9819c3705c804700 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:23 +0200
+Subject: [PATCH 14/14] net: dsa: qca8k: move read_switch_id function to common
+ code
+
+The same function to read the switch id is used by drivers based on
+qca8k family switch. Move them to common code to make them accessible
+also by other drivers.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 29 -----------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 29 +++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 1 +
+ 3 files changed, 30 insertions(+), 29 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1822,35 +1822,6 @@ static const struct dsa_switch_ops qca8k
+ .connect_tag_protocol = qca8k_connect_tag_protocol,
+ };
+
+-static int qca8k_read_switch_id(struct qca8k_priv *priv)
+-{
+- u32 val;
+- u8 id;
+- int ret;
+-
+- if (!priv->info)
+- return -ENODEV;
+-
+- ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
+- if (ret < 0)
+- return -ENODEV;
+-
+- id = QCA8K_MASK_CTRL_DEVICE_ID(val);
+- if (id != priv->info->id) {
+- dev_err(priv->dev,
+- "Switch id detected %x but expected %x",
+- id, priv->info->id);
+- return -ENODEV;
+- }
+-
+- priv->switch_id = id;
+-
+- /* Save revision to communicate to the internal PHY driver */
+- priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
+-
+- return 0;
+-}
+-
+ static int
+ qca8k_sw_probe(struct mdio_device *mdiodev)
+ {
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -1175,3 +1175,32 @@ int qca8k_port_lag_leave(struct dsa_swit
+ {
+ return qca8k_lag_refresh_portmap(ds, port, lag, true);
+ }
++
++int qca8k_read_switch_id(struct qca8k_priv *priv)
++{
++ u32 val;
++ u8 id;
++ int ret;
++
++ if (!priv->info)
++ return -ENODEV;
++
++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val);
++ if (ret < 0)
++ return -ENODEV;
++
++ id = QCA8K_MASK_CTRL_DEVICE_ID(val);
++ if (id != priv->info->id) {
++ dev_err(priv->dev,
++ "Switch id detected %x but expected %x",
++ id, priv->info->id);
++ return -ENODEV;
++ }
++
++ priv->switch_id = id;
++
++ /* Save revision to communicate to the internal PHY driver */
++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);
++
++ return 0;
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -424,6 +424,7 @@ extern const struct qca8k_mib_desc ar832
+ extern const struct regmap_access_table qca8k_readable_table;
+ int qca8k_mib_init(struct qca8k_priv *priv);
+ void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable);
++int qca8k_read_switch_id(struct qca8k_priv *priv);
+
+ /* Common read/write/rmw function */
+ int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val);
diff --git a/target/linux/generic/backport-6.1/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch b/target/linux/generic/backport-6.1/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch
new file mode 100644
index 0000000000..0cca2788f6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch
@@ -0,0 +1,29 @@
+From 057bcf15db8e625276ddf02b2b7c668a3cb43f81 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sun, 4 Sep 2022 23:46:24 +0200
+Subject: [net PATCH] net: dsa: qca8k: fix NULL pointer dereference for
+ of_device_get_match_data
+
+of_device_get_match_data is called on priv->dev before priv->dev is
+actually set. Move of_device_get_match_data after priv->dev is correctly
+set to fix this kernel panic.
+
+Fixes: 3bb0844e7bcd ("net: dsa: qca8k: cache match data to speed up access")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1835,9 +1835,9 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ if (!priv)
+ return -ENOMEM;
+
+- priv->info = of_device_get_match_data(priv->dev);
+ priv->bus = mdiodev->bus;
+ priv->dev = &mdiodev->dev;
++ priv->info = of_device_get_match_data(priv->dev);
+
+ priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset",
+ GPIOD_ASIS);
diff --git a/target/linux/generic/backport-6.1/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch b/target/linux/generic/backport-6.1/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch
new file mode 100644
index 0000000000..44093eab95
--- /dev/null
+++ b/target/linux/generic/backport-6.1/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch
@@ -0,0 +1,77 @@
+From 381a730182f1d174e1950cd4e63e885b1c302051 Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Mon, 24 Jan 2022 22:09:43 +0100
+Subject: net: dsa: Move VLAN filtering syncing out of dsa_switch_bridge_leave
+
+Most of dsa_switch_bridge_leave was, in fact, dealing with the syncing
+of VLAN filtering for switches on which that is a global
+setting. Separate the two phases to prepare for the cross-chip related
+bugfix in the following commit.
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/switch.c | 38 +++++++++++++++++++++++++-------------
+ 1 file changed, 25 insertions(+), 13 deletions(-)
+
+--- a/net/dsa/switch.c
++++ b/net/dsa/switch.c
+@@ -113,25 +113,14 @@ static int dsa_switch_bridge_join(struct
+ return dsa_tag_8021q_bridge_join(ds, info);
+ }
+
+-static int dsa_switch_bridge_leave(struct dsa_switch *ds,
+- struct dsa_notifier_bridge_info *info)
++static int dsa_switch_sync_vlan_filtering(struct dsa_switch *ds,
++ struct dsa_notifier_bridge_info *info)
+ {
+- struct dsa_switch_tree *dst = ds->dst;
+ struct netlink_ext_ack extack = {0};
+ bool change_vlan_filtering = false;
+ bool vlan_filtering;
+ int err, port;
+
+- if (dst->index == info->tree_index && ds->index == info->sw_index &&
+- ds->ops->port_bridge_leave)
+- ds->ops->port_bridge_leave(ds, info->port, info->br);
+-
+- if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
+- ds->ops->crosschip_bridge_leave)
+- ds->ops->crosschip_bridge_leave(ds, info->tree_index,
+- info->sw_index, info->port,
+- info->br);
+-
+ if (ds->needs_standalone_vlan_filtering && !br_vlan_enabled(info->br)) {
+ change_vlan_filtering = true;
+ vlan_filtering = true;
+@@ -172,6 +161,29 @@ static int dsa_switch_bridge_leave(struc
+ return err;
+ }
+
++ return 0;
++}
++
++static int dsa_switch_bridge_leave(struct dsa_switch *ds,
++ struct dsa_notifier_bridge_info *info)
++{
++ struct dsa_switch_tree *dst = ds->dst;
++ int err;
++
++ if (dst->index == info->tree_index && ds->index == info->sw_index &&
++ ds->ops->port_bridge_leave)
++ ds->ops->port_bridge_leave(ds, info->port, info->br);
++
++ if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
++ ds->ops->crosschip_bridge_leave)
++ ds->ops->crosschip_bridge_leave(ds, info->tree_index,
++ info->sw_index, info->port,
++ info->br);
++
++ err = dsa_switch_sync_vlan_filtering(ds, info);
++ if (err)
++ return err;
++
+ return dsa_tag_8021q_bridge_leave(ds, info);
+ }
+
diff --git a/target/linux/generic/backport-6.1/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch b/target/linux/generic/backport-6.1/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch
new file mode 100644
index 0000000000..cdddbcf14e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch
@@ -0,0 +1,52 @@
+From 108dc8741c203e9d6ce4e973367f1bac20c7192b Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Mon, 24 Jan 2022 22:09:44 +0100
+Subject: net: dsa: Avoid cross-chip syncing of VLAN filtering
+
+Changes to VLAN filtering are not applicable to cross-chip
+notifications.
+
+On a system like this:
+
+.-----. .-----. .-----.
+| sw1 +---+ sw2 +---+ sw3 |
+'-1-2-' '-1-2-' '-1-2-'
+
+Before this change, upon sw1p1 leaving a bridge, a call to
+dsa_port_vlan_filtering would also be made to sw2p1 and sw3p1.
+
+In this scenario:
+
+.---------. .-----. .-----.
+| sw1 +---+ sw2 +---+ sw3 |
+'-1-2-3-4-' '-1-2-' '-1-2-'
+
+When sw1p4 would leave a bridge, dsa_port_vlan_filtering would be
+called for sw2 and sw3 with a non-existing port - leading to array
+out-of-bounds accesses and crashes on mv88e6xxx.
+
+Fixes: d371b7c92d19 ("net: dsa: Unset vlan_filtering when ports leave the bridge")
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/dsa/switch.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/net/dsa/switch.c
++++ b/net/dsa/switch.c
+@@ -180,9 +180,11 @@ static int dsa_switch_bridge_leave(struc
+ info->sw_index, info->port,
+ info->br);
+
+- err = dsa_switch_sync_vlan_filtering(ds, info);
+- if (err)
+- return err;
++ if (ds->dst->index == info->tree_index && ds->index == info->sw_index) {
++ err = dsa_switch_sync_vlan_filtering(ds, info);
++ if (err)
++ return err;
++ }
+
+ return dsa_tag_8021q_bridge_leave(ds, info);
+ }
diff --git a/target/linux/generic/backport-6.1/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch b/target/linux/generic/backport-6.1/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch
new file mode 100644
index 0000000000..78570c5e6e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch
@@ -0,0 +1,141 @@
+From c9111895fd38dadf125e07be627778a9950d8d77 Mon Sep 17 00:00:00 2001
+From: DENG Qingfang <dqfext@gmail.com>
+Date: Sun, 26 Sep 2021 00:59:24 +0200
+Subject: [PATCH 01/11] net: dsa: rtl8366rb: Support bridge offloading
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Use port isolation registers to configure bridge offloading.
+
+Tested on the D-Link DIR-685, switching between ports and
+sniffing ports to make sure no packets leak.
+
+Cc: Vladimir Oltean <olteanv@gmail.com>
+Cc: Mauri Sandberg <sandberg@mailfence.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: DENG Qingfang <dqfext@gmail.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/rtl8366rb.c | 86 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 86 insertions(+)
+
+--- a/drivers/net/dsa/rtl8366rb.c
++++ b/drivers/net/dsa/rtl8366rb.c
+@@ -300,6 +300,13 @@
+ #define RTL8366RB_INTERRUPT_STATUS_REG 0x0442
+ #define RTL8366RB_NUM_INTERRUPT 14 /* 0..13 */
+
++/* Port isolation registers */
++#define RTL8366RB_PORT_ISO_BASE 0x0F08
++#define RTL8366RB_PORT_ISO(pnum) (RTL8366RB_PORT_ISO_BASE + (pnum))
++#define RTL8366RB_PORT_ISO_EN BIT(0)
++#define RTL8366RB_PORT_ISO_PORTS_MASK GENMASK(7, 1)
++#define RTL8366RB_PORT_ISO_PORTS(pmask) ((pmask) << 1)
++
+ /* bits 0..5 enable force when cleared */
+ #define RTL8366RB_MAC_FORCE_CTRL_REG 0x0F11
+
+@@ -835,6 +842,21 @@ static int rtl8366rb_setup(struct dsa_sw
+ if (ret)
+ return ret;
+
++ /* Isolate all user ports so they can only send packets to itself and the CPU port */
++ for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) {
++ ret = regmap_write(smi->map, RTL8366RB_PORT_ISO(i),
++ RTL8366RB_PORT_ISO_PORTS(BIT(RTL8366RB_PORT_NUM_CPU)) |
++ RTL8366RB_PORT_ISO_EN);
++ if (ret)
++ return ret;
++ }
++ /* CPU port can send packets to all ports */
++ ret = regmap_write(smi->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU),
++ RTL8366RB_PORT_ISO_PORTS(dsa_user_ports(ds)) |
++ RTL8366RB_PORT_ISO_EN);
++ if (ret)
++ return ret;
++
+ /* Set up the "green ethernet" feature */
+ ret = rtl8366rb_jam_table(rtl8366rb_green_jam,
+ ARRAY_SIZE(rtl8366rb_green_jam), smi, false);
+@@ -1127,6 +1149,68 @@ rtl8366rb_port_disable(struct dsa_switch
+ rb8366rb_set_port_led(smi, port, false);
+ }
+
++static int
++rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port,
++ struct net_device *bridge)
++{
++ struct realtek_smi *smi = ds->priv;
++ unsigned int port_bitmap = 0;
++ int ret, i;
++
++ /* Loop over all other ports than the current one */
++ for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) {
++ /* Current port handled last */
++ if (i == port)
++ continue;
++ /* Not on this bridge */
++ if (dsa_to_port(ds, i)->bridge_dev != bridge)
++ continue;
++ /* Join this port to each other port on the bridge */
++ ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i),
++ RTL8366RB_PORT_ISO_PORTS(BIT(port)),
++ RTL8366RB_PORT_ISO_PORTS(BIT(port)));
++ if (ret)
++ dev_err(smi->dev, "failed to join port %d\n", port);
++
++ port_bitmap |= BIT(i);
++ }
++
++ /* Set the bits for the ports we can access */
++ return regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(port),
++ RTL8366RB_PORT_ISO_PORTS(port_bitmap),
++ RTL8366RB_PORT_ISO_PORTS(port_bitmap));
++}
++
++static void
++rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port,
++ struct net_device *bridge)
++{
++ struct realtek_smi *smi = ds->priv;
++ unsigned int port_bitmap = 0;
++ int ret, i;
++
++ /* Loop over all other ports than this one */
++ for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) {
++ /* Current port handled last */
++ if (i == port)
++ continue;
++ /* Not on this bridge */
++ if (dsa_to_port(ds, i)->bridge_dev != bridge)
++ continue;
++ /* Remove this port from any other port on the bridge */
++ ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i),
++ RTL8366RB_PORT_ISO_PORTS(BIT(port)), 0);
++ if (ret)
++ dev_err(smi->dev, "failed to leave port %d\n", port);
++
++ port_bitmap |= BIT(i);
++ }
++
++ /* Clear the bits for the ports we can not access, leave ourselves */
++ regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(port),
++ RTL8366RB_PORT_ISO_PORTS(port_bitmap), 0);
++}
++
+ static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+ {
+ struct realtek_smi *smi = ds->priv;
+@@ -1510,6 +1594,8 @@ static const struct dsa_switch_ops rtl83
+ .get_strings = rtl8366_get_strings,
+ .get_ethtool_stats = rtl8366_get_ethtool_stats,
+ .get_sset_count = rtl8366_get_sset_count,
++ .port_bridge_join = rtl8366rb_port_bridge_join,
++ .port_bridge_leave = rtl8366rb_port_bridge_leave,
+ .port_vlan_filtering = rtl8366_vlan_filtering,
+ .port_vlan_add = rtl8366_vlan_add,
+ .port_vlan_del = rtl8366_vlan_del,
diff --git a/target/linux/generic/backport-6.1/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch b/target/linux/generic/backport-6.1/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch
new file mode 100644
index 0000000000..e61349a32c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch
@@ -0,0 +1,118 @@
+From 96cf10a8e7297065459473c081a6fb6432a22312 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Sun, 26 Sep 2021 00:59:25 +0200
+Subject: [PATCH 02/11] net: dsa: rtl8366: Drop custom VLAN set-up
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This hacky default VLAN setup was done in order to direct
+packets to the right ports and provide port isolation, both
+which we now support properly using custom tags and proper
+bridge port isolation.
+
+We can drop the custom VLAN code and leave all VLAN handling
+alone, as users expect things to be. We can also drop
+ds->configure_vlan_while_not_filtering = false; and let
+the core deal with any VLANs it wants.
+
+Cc: Mauri Sandberg <sandberg@mailfence.com>
+Cc: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek-smi-core.h | 1 -
+ drivers/net/dsa/rtl8366.c | 48 ------------------------------
+ drivers/net/dsa/rtl8366rb.c | 4 +--
+ 3 files changed, 1 insertion(+), 52 deletions(-)
+
+--- a/drivers/net/dsa/realtek-smi-core.h
++++ b/drivers/net/dsa/realtek-smi-core.h
+@@ -129,7 +129,6 @@ int rtl8366_set_pvid(struct realtek_smi
+ int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable);
+ int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable);
+ int rtl8366_reset_vlan(struct realtek_smi *smi);
+-int rtl8366_init_vlan(struct realtek_smi *smi);
+ int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+ struct netlink_ext_ack *extack);
+ int rtl8366_vlan_add(struct dsa_switch *ds, int port,
+--- a/drivers/net/dsa/rtl8366.c
++++ b/drivers/net/dsa/rtl8366.c
+@@ -292,54 +292,6 @@ int rtl8366_reset_vlan(struct realtek_sm
+ }
+ EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
+
+-int rtl8366_init_vlan(struct realtek_smi *smi)
+-{
+- int port;
+- int ret;
+-
+- ret = rtl8366_reset_vlan(smi);
+- if (ret)
+- return ret;
+-
+- /* Loop over the available ports, for each port, associate
+- * it with the VLAN (port+1)
+- */
+- for (port = 0; port < smi->num_ports; port++) {
+- u32 mask;
+-
+- if (port == smi->cpu_port)
+- /* For the CPU port, make all ports members of this
+- * VLAN.
+- */
+- mask = GENMASK((int)smi->num_ports - 1, 0);
+- else
+- /* For all other ports, enable itself plus the
+- * CPU port.
+- */
+- mask = BIT(port) | BIT(smi->cpu_port);
+-
+- /* For each port, set the port as member of VLAN (port+1)
+- * and untagged, except for the CPU port: the CPU port (5) is
+- * member of VLAN 6 and so are ALL the other ports as well.
+- * Use filter 0 (no filter).
+- */
+- dev_info(smi->dev, "VLAN%d port mask for port %d, %08x\n",
+- (port + 1), port, mask);
+- ret = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0);
+- if (ret)
+- return ret;
+-
+- dev_info(smi->dev, "VLAN%d port %d, PVID set to %d\n",
+- (port + 1), port, (port + 1));
+- ret = rtl8366_set_pvid(smi, port, (port + 1));
+- if (ret)
+- return ret;
+- }
+-
+- return rtl8366_enable_vlan(smi, true);
+-}
+-EXPORT_SYMBOL_GPL(rtl8366_init_vlan);
+-
+ int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+ struct netlink_ext_ack *extack)
+ {
+--- a/drivers/net/dsa/rtl8366rb.c
++++ b/drivers/net/dsa/rtl8366rb.c
+@@ -985,7 +985,7 @@ static int rtl8366rb_setup(struct dsa_sw
+ return ret;
+ }
+
+- ret = rtl8366_init_vlan(smi);
++ ret = rtl8366_reset_vlan(smi);
+ if (ret)
+ return ret;
+
+@@ -999,8 +999,6 @@ static int rtl8366rb_setup(struct dsa_sw
+ return -ENODEV;
+ }
+
+- ds->configure_vlan_while_not_filtering = false;
+-
+ return 0;
+ }
+
diff --git a/target/linux/generic/backport-6.1/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch b/target/linux/generic/backport-6.1/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch
new file mode 100644
index 0000000000..2b19752399
--- /dev/null
+++ b/target/linux/generic/backport-6.1/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch
@@ -0,0 +1,270 @@
+From 7028f54b620f8df344b18e46e4a78e266091ab45 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Sun, 26 Sep 2021 00:59:26 +0200
+Subject: [PATCH 03/11] net: dsa: rtl8366rb: Rewrite weird VLAN filering
+ enablement
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+While we were defining one VLAN per port for isolating the ports
+the port_vlan_filtering() callback was implemented to enable a
+VLAN on the port + 1. This function makes no sense, not only is
+it incomplete as it only enables the VLAN, it doesn't do what
+the callback is supposed to do, which is to selectively enable
+and disable filtering on a certain port.
+
+Implement the correct callback: we have two registers dealing
+with filtering on the RTL9366RB, so we implement an ASIC-specific
+callback and implement filering using the register bit that makes
+the switch drop frames if the port is not in the VLAN member set.
+
+The DSA documentation Documentation/networking/switchdev.rst states:
+
+ When the bridge has VLAN filtering enabled and a PVID is not
+ configured on the ingress port, untagged and 802.1p tagged
+ packets must be dropped. When the bridge has VLAN filtering
+ enabled and a PVID exists on the ingress port, untagged and
+ priority-tagged packets must be accepted and forwarded according
+ to the bridge's port membership of the PVID VLAN. When the
+ bridge has VLAN filtering disabled, the presence/lack of a
+ PVID should not influence the packet forwarding decision.
+
+To comply with this, we add two arrays of bool in the RTL8366RB
+state that keeps track of if filtering and PVID is enabled or
+not for each port. We then add code such that whenever filtering
+or PVID changes, we update the filter according to the
+specification.
+
+Cc: Vladimir Oltean <olteanv@gmail.com>
+Cc: Mauri Sandberg <sandberg@mailfence.com>
+Cc: Alvin Šipraga <alsi@bang-olufsen.dk>
+Cc: Florian Fainelli <f.fainelli@gmail.com>
+Cc: DENG Qingfang <dqfext@gmail.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/realtek-smi-core.h | 2 -
+ drivers/net/dsa/rtl8366.c | 35 ----------
+ drivers/net/dsa/rtl8366rb.c | 102 +++++++++++++++++++++++++++--
+ 3 files changed, 95 insertions(+), 44 deletions(-)
+
+--- a/drivers/net/dsa/realtek-smi-core.h
++++ b/drivers/net/dsa/realtek-smi-core.h
+@@ -129,8 +129,6 @@ int rtl8366_set_pvid(struct realtek_smi
+ int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable);
+ int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable);
+ int rtl8366_reset_vlan(struct realtek_smi *smi);
+-int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+- struct netlink_ext_ack *extack);
+ int rtl8366_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct netlink_ext_ack *extack);
+--- a/drivers/net/dsa/rtl8366.c
++++ b/drivers/net/dsa/rtl8366.c
+@@ -292,41 +292,6 @@ int rtl8366_reset_vlan(struct realtek_sm
+ }
+ EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
+
+-int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+- struct netlink_ext_ack *extack)
+-{
+- struct realtek_smi *smi = ds->priv;
+- struct rtl8366_vlan_4k vlan4k;
+- int ret;
+-
+- /* Use VLAN nr port + 1 since VLAN0 is not valid */
+- if (!smi->ops->is_vlan_valid(smi, port + 1))
+- return -EINVAL;
+-
+- dev_info(smi->dev, "%s filtering on port %d\n",
+- vlan_filtering ? "enable" : "disable",
+- port);
+-
+- /* TODO:
+- * The hardware support filter ID (FID) 0..7, I have no clue how to
+- * support this in the driver when the callback only says on/off.
+- */
+- ret = smi->ops->get_vlan_4k(smi, port + 1, &vlan4k);
+- if (ret)
+- return ret;
+-
+- /* Just set the filter to FID 1 for now then */
+- ret = rtl8366_set_vlan(smi, port + 1,
+- vlan4k.member,
+- vlan4k.untag,
+- 1);
+- if (ret)
+- return ret;
+-
+- return 0;
+-}
+-EXPORT_SYMBOL_GPL(rtl8366_vlan_filtering);
+-
+ int rtl8366_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct netlink_ext_ack *extack)
+--- a/drivers/net/dsa/rtl8366rb.c
++++ b/drivers/net/dsa/rtl8366rb.c
+@@ -143,6 +143,21 @@
+ #define RTL8366RB_PHY_NO_OFFSET 9
+ #define RTL8366RB_PHY_NO_MASK (0x1f << 9)
+
++/* VLAN Ingress Control Register 1, one bit per port.
++ * bit 0 .. 5 will make the switch drop ingress frames without
++ * VID such as untagged or priority-tagged frames for respective
++ * port.
++ * bit 6 .. 11 will make the switch drop ingress frames carrying
++ * a C-tag with VID != 0 for respective port.
++ */
++#define RTL8366RB_VLAN_INGRESS_CTRL1_REG 0x037E
++#define RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) (BIT((port)) | BIT((port) + 6))
++
++/* VLAN Ingress Control Register 2, one bit per port.
++ * bit0 .. bit5 will make the switch drop all ingress frames with
++ * a VLAN classification that does not include the port is in its
++ * member set.
++ */
+ #define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f
+
+ /* LED control registers */
+@@ -321,9 +336,13 @@
+ /**
+ * struct rtl8366rb - RTL8366RB-specific data
+ * @max_mtu: per-port max MTU setting
++ * @pvid_enabled: if PVID is set for respective port
++ * @vlan_filtering: if VLAN filtering is enabled for respective port
+ */
+ struct rtl8366rb {
+ unsigned int max_mtu[RTL8366RB_NUM_PORTS];
++ bool pvid_enabled[RTL8366RB_NUM_PORTS];
++ bool vlan_filtering[RTL8366RB_NUM_PORTS];
+ };
+
+ static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = {
+@@ -933,11 +952,13 @@ static int rtl8366rb_setup(struct dsa_sw
+ if (ret)
+ return ret;
+
+- /* Discard VLAN tagged packets if the port is not a member of
+- * the VLAN with which the packets is associated.
+- */
++ /* Accept all packets by default, we enable filtering on-demand */
++ ret = regmap_write(smi->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG,
++ 0);
++ if (ret)
++ return ret;
+ ret = regmap_write(smi->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG,
+- RTL8366RB_PORT_ALL);
++ 0);
+ if (ret)
+ return ret;
+
+@@ -1209,6 +1230,53 @@ rtl8366rb_port_bridge_leave(struct dsa_s
+ RTL8366RB_PORT_ISO_PORTS(port_bitmap), 0);
+ }
+
++/**
++ * rtl8366rb_drop_untagged() - make the switch drop untagged and C-tagged frames
++ * @smi: SMI state container
++ * @port: the port to drop untagged and C-tagged frames on
++ * @drop: whether to drop or pass untagged and C-tagged frames
++ */
++static int rtl8366rb_drop_untagged(struct realtek_smi *smi, int port, bool drop)
++{
++ return regmap_update_bits(smi->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG,
++ RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port),
++ drop ? RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) : 0);
++}
++
++static int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port,
++ bool vlan_filtering,
++ struct netlink_ext_ack *extack)
++{
++ struct realtek_smi *smi = ds->priv;
++ struct rtl8366rb *rb;
++ int ret;
++
++ rb = smi->chip_data;
++
++ dev_dbg(smi->dev, "port %d: %s VLAN filtering\n", port,
++ vlan_filtering ? "enable" : "disable");
++
++ /* If the port is not in the member set, the frame will be dropped */
++ ret = regmap_update_bits(smi->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG,
++ BIT(port), vlan_filtering ? BIT(port) : 0);
++ if (ret)
++ return ret;
++
++ /* Keep track if filtering is enabled on each port */
++ rb->vlan_filtering[port] = vlan_filtering;
++
++ /* If VLAN filtering is enabled and PVID is also enabled, we must
++ * not drop any untagged or C-tagged frames. If we turn off VLAN
++ * filtering on a port, we need ti accept any frames.
++ */
++ if (vlan_filtering)
++ ret = rtl8366rb_drop_untagged(smi, port, !rb->pvid_enabled[port]);
++ else
++ ret = rtl8366rb_drop_untagged(smi, port, false);
++
++ return ret;
++}
++
+ static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+ {
+ struct realtek_smi *smi = ds->priv;
+@@ -1420,14 +1488,34 @@ static int rtl8366rb_get_mc_index(struct
+
+ static int rtl8366rb_set_mc_index(struct realtek_smi *smi, int port, int index)
+ {
++ struct rtl8366rb *rb;
++ bool pvid_enabled;
++ int ret;
++
++ rb = smi->chip_data;
++ pvid_enabled = !!index;
++
+ if (port >= smi->num_ports || index >= RTL8366RB_NUM_VLANS)
+ return -EINVAL;
+
+- return regmap_update_bits(smi->map, RTL8366RB_PORT_VLAN_CTRL_REG(port),
++ ret = regmap_update_bits(smi->map, RTL8366RB_PORT_VLAN_CTRL_REG(port),
+ RTL8366RB_PORT_VLAN_CTRL_MASK <<
+ RTL8366RB_PORT_VLAN_CTRL_SHIFT(port),
+ (index & RTL8366RB_PORT_VLAN_CTRL_MASK) <<
+ RTL8366RB_PORT_VLAN_CTRL_SHIFT(port));
++ if (ret)
++ return ret;
++
++ rb->pvid_enabled[port] = pvid_enabled;
++
++ /* If VLAN filtering is enabled and PVID is also enabled, we must
++ * not drop any untagged or C-tagged frames. Make sure to update the
++ * filtering setting.
++ */
++ if (rb->vlan_filtering[port])
++ ret = rtl8366rb_drop_untagged(smi, port, !pvid_enabled);
++
++ return ret;
+ }
+
+ static bool rtl8366rb_is_vlan_valid(struct realtek_smi *smi, unsigned int vlan)
+@@ -1437,7 +1525,7 @@ static bool rtl8366rb_is_vlan_valid(stru
+ if (smi->vlan4k_enabled)
+ max = RTL8366RB_NUM_VIDS - 1;
+
+- if (vlan == 0 || vlan > max)
++ if (vlan > max)
+ return false;
+
+ return true;
+@@ -1594,7 +1682,7 @@ static const struct dsa_switch_ops rtl83
+ .get_sset_count = rtl8366_get_sset_count,
+ .port_bridge_join = rtl8366rb_port_bridge_join,
+ .port_bridge_leave = rtl8366rb_port_bridge_leave,
+- .port_vlan_filtering = rtl8366_vlan_filtering,
++ .port_vlan_filtering = rtl8366rb_vlan_filtering,
+ .port_vlan_add = rtl8366_vlan_add,
+ .port_vlan_del = rtl8366_vlan_del,
+ .port_enable = rtl8366rb_port_enable,
diff --git a/target/linux/generic/backport-6.1/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch b/target/linux/generic/backport-6.1/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch
new file mode 100644
index 0000000000..b56032c366
--- /dev/null
+++ b/target/linux/generic/backport-6.1/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch
@@ -0,0 +1,51 @@
+From ddb59a5dc42714999c335dab4bf256125ba3120c Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Sun, 26 Sep 2021 00:59:29 +0200
+Subject: [PATCH 06/11] net: dsa: rtl8366: Drop and depromote pointless prints
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We don't need a message for every VLAN association, dbg
+is fine. The message about adding the DSA or CPU
+port to a VLAN is directly misleading, this is perfectly
+fine.
+
+Cc: Vladimir Oltean <olteanv@gmail.com>
+Cc: Mauri Sandberg <sandberg@mailfence.com>
+Cc: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/rtl8366.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/dsa/rtl8366.c
++++ b/drivers/net/dsa/rtl8366.c
+@@ -318,12 +318,9 @@ int rtl8366_vlan_add(struct dsa_switch *
+ return ret;
+ }
+
+- dev_info(smi->dev, "add VLAN %d on port %d, %s, %s\n",
+- vlan->vid, port, untagged ? "untagged" : "tagged",
+- pvid ? " PVID" : "no PVID");
+-
+- if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
+- dev_err(smi->dev, "port is DSA or CPU port\n");
++ dev_dbg(smi->dev, "add VLAN %d on port %d, %s, %s\n",
++ vlan->vid, port, untagged ? "untagged" : "tagged",
++ pvid ? "PVID" : "no PVID");
+
+ member |= BIT(port);
+
+@@ -356,7 +353,7 @@ int rtl8366_vlan_del(struct dsa_switch *
+ struct realtek_smi *smi = ds->priv;
+ int ret, i;
+
+- dev_info(smi->dev, "del VLAN %04x on port %d\n", vlan->vid, port);
++ dev_dbg(smi->dev, "del VLAN %d on port %d\n", vlan->vid, port);
+
+ for (i = 0; i < smi->num_vlan_mc; i++) {
+ struct rtl8366_vlan_mc vlanmc;
diff --git a/target/linux/generic/backport-6.1/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch b/target/linux/generic/backport-6.1/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch
new file mode 100644
index 0000000000..8cd1df97f2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch
@@ -0,0 +1,61 @@
+From 5c9b66f3c8a3f72fa2a58e89a57c6d7afd550bf0 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Wed, 29 Sep 2021 13:23:22 +0200
+Subject: [PATCH 07/11] net: dsa: rtl8366rb: Use core filtering tracking
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We added a state variable to track whether a certain port
+was VLAN filtering or not, but we can just inquire the DSA
+core about this.
+
+Cc: Vladimir Oltean <olteanv@gmail.com>
+Cc: Mauri Sandberg <sandberg@mailfence.com>
+Cc: DENG Qingfang <dqfext@gmail.com>
+Cc: Alvin Šipraga <alsi@bang-olufsen.dk>
+Cc: Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/rtl8366rb.c | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/dsa/rtl8366rb.c
++++ b/drivers/net/dsa/rtl8366rb.c
+@@ -337,12 +337,10 @@
+ * struct rtl8366rb - RTL8366RB-specific data
+ * @max_mtu: per-port max MTU setting
+ * @pvid_enabled: if PVID is set for respective port
+- * @vlan_filtering: if VLAN filtering is enabled for respective port
+ */
+ struct rtl8366rb {
+ unsigned int max_mtu[RTL8366RB_NUM_PORTS];
+ bool pvid_enabled[RTL8366RB_NUM_PORTS];
+- bool vlan_filtering[RTL8366RB_NUM_PORTS];
+ };
+
+ static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = {
+@@ -1262,12 +1260,9 @@ static int rtl8366rb_vlan_filtering(stru
+ if (ret)
+ return ret;
+
+- /* Keep track if filtering is enabled on each port */
+- rb->vlan_filtering[port] = vlan_filtering;
+-
+ /* If VLAN filtering is enabled and PVID is also enabled, we must
+ * not drop any untagged or C-tagged frames. If we turn off VLAN
+- * filtering on a port, we need ti accept any frames.
++ * filtering on a port, we need to accept any frames.
+ */
+ if (vlan_filtering)
+ ret = rtl8366rb_drop_untagged(smi, port, !rb->pvid_enabled[port]);
+@@ -1512,7 +1507,7 @@ static int rtl8366rb_set_mc_index(struct
+ * not drop any untagged or C-tagged frames. Make sure to update the
+ * filtering setting.
+ */
+- if (rb->vlan_filtering[port])
++ if (dsa_port_is_vlan_filtering(dsa_to_port(smi->ds, port)))
+ ret = rtl8366rb_drop_untagged(smi, port, !pvid_enabled);
+
+ return ret;
diff --git a/target/linux/generic/backport-6.1/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch b/target/linux/generic/backport-6.1/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch
new file mode 100644
index 0000000000..8306eb5aef
--- /dev/null
+++ b/target/linux/generic/backport-6.1/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch
@@ -0,0 +1,115 @@
+From 831a3d26bea0d14f8563eecf96def660a74a3000 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Tue, 5 Oct 2021 21:47:02 +0200
+Subject: [PATCH 08/11] net: dsa: rtl8366rb: Support disabling learning
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The RTL8366RB hardware supports disabling learning per-port
+so let's make use of this feature. Rename some unfortunately
+named registers in the process.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Cc: Alvin Šipraga <alsi@bang-olufsen.dk>
+Cc: Mauri Sandberg <sandberg@mailfence.com>
+Cc: Florian Fainelli <f.fainelli@gmail.com>
+Cc: DENG Qingfang <dqfext@gmail.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/rtl8366rb.c | 50 ++++++++++++++++++++++++++++++++-----
+ 1 file changed, 44 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/rtl8366rb.c
++++ b/drivers/net/dsa/rtl8366rb.c
+@@ -14,6 +14,7 @@
+
+ #include <linux/bitops.h>
+ #include <linux/etherdevice.h>
++#include <linux/if_bridge.h>
+ #include <linux/interrupt.h>
+ #include <linux/irqdomain.h>
+ #include <linux/irqchip/chained_irq.h>
+@@ -42,9 +43,12 @@
+ /* Port Enable Control register */
+ #define RTL8366RB_PECR 0x0001
+
+-/* Switch Security Control registers */
+-#define RTL8366RB_SSCR0 0x0002
+-#define RTL8366RB_SSCR1 0x0003
++/* Switch per-port learning disablement register */
++#define RTL8366RB_PORT_LEARNDIS_CTRL 0x0002
++
++/* Security control, actually aging register */
++#define RTL8366RB_SECURITY_CTRL 0x0003
++
+ #define RTL8366RB_SSCR2 0x0004
+ #define RTL8366RB_SSCR2_DROP_UNKNOWN_DA BIT(0)
+
+@@ -927,13 +931,14 @@ static int rtl8366rb_setup(struct dsa_sw
+ /* layer 2 size, see rtl8366rb_change_mtu() */
+ rb->max_mtu[i] = 1532;
+
+- /* Enable learning for all ports */
+- ret = regmap_write(smi->map, RTL8366RB_SSCR0, 0);
++ /* Disable learning for all ports */
++ ret = regmap_write(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL,
++ RTL8366RB_PORT_ALL);
+ if (ret)
+ return ret;
+
+ /* Enable auto ageing for all ports */
+- ret = regmap_write(smi->map, RTL8366RB_SSCR1, 0);
++ ret = regmap_write(smi->map, RTL8366RB_SECURITY_CTRL, 0);
+ if (ret)
+ return ret;
+
+@@ -1272,6 +1277,37 @@ static int rtl8366rb_vlan_filtering(stru
+ return ret;
+ }
+
++static int
++rtl8366rb_port_pre_bridge_flags(struct dsa_switch *ds, int port,
++ struct switchdev_brport_flags flags,
++ struct netlink_ext_ack *extack)
++{
++ /* We support enabling/disabling learning */
++ if (flags.mask & ~(BR_LEARNING))
++ return -EINVAL;
++
++ return 0;
++}
++
++static int
++rtl8366rb_port_bridge_flags(struct dsa_switch *ds, int port,
++ struct switchdev_brport_flags flags,
++ struct netlink_ext_ack *extack)
++{
++ struct realtek_smi *smi = ds->priv;
++ int ret;
++
++ if (flags.mask & BR_LEARNING) {
++ ret = regmap_update_bits(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL,
++ BIT(port),
++ (flags.val & BR_LEARNING) ? 0 : BIT(port));
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
+ static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+ {
+ struct realtek_smi *smi = ds->priv;
+@@ -1682,6 +1718,8 @@ static const struct dsa_switch_ops rtl83
+ .port_vlan_del = rtl8366_vlan_del,
+ .port_enable = rtl8366rb_port_enable,
+ .port_disable = rtl8366rb_port_disable,
++ .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags,
++ .port_bridge_flags = rtl8366rb_port_bridge_flags,
+ .port_change_mtu = rtl8366rb_change_mtu,
+ .port_max_mtu = rtl8366rb_max_mtu,
+ };
diff --git a/target/linux/generic/backport-6.1/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch b/target/linux/generic/backport-6.1/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch
new file mode 100644
index 0000000000..a74108e23d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch
@@ -0,0 +1,57 @@
+From 8eb13420eb9ab4a4e2ebd612bf5dc9dba0039236 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Tue, 5 Oct 2021 21:47:03 +0200
+Subject: [PATCH 09/11] net: dsa: rtl8366rb: Support fast aging
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This implements fast aging per-port using the special "security"
+register, which will flush any learned L2 LUT entries on a port.
+
+The vendor API just enabled setting and clearing this bit, so
+we set it to age out any entries on the port and then we clear
+it again.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Cc: Mauri Sandberg <sandberg@mailfence.com>
+Cc: DENG Qingfang <dqfext@gmail.com>
+Cc: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/rtl8366rb.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/net/dsa/rtl8366rb.c
++++ b/drivers/net/dsa/rtl8366rb.c
+@@ -1308,6 +1308,19 @@ rtl8366rb_port_bridge_flags(struct dsa_s
+ return 0;
+ }
+
++static void
++rtl8366rb_port_fast_age(struct dsa_switch *ds, int port)
++{
++ struct realtek_smi *smi = ds->priv;
++
++ /* This will age out any learned L2 entries */
++ regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL,
++ BIT(port), BIT(port));
++ /* Restore the normal state of things */
++ regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL,
++ BIT(port), 0);
++}
++
+ static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+ {
+ struct realtek_smi *smi = ds->priv;
+@@ -1720,6 +1733,7 @@ static const struct dsa_switch_ops rtl83
+ .port_disable = rtl8366rb_port_disable,
+ .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags,
+ .port_bridge_flags = rtl8366rb_port_bridge_flags,
++ .port_fast_age = rtl8366rb_port_fast_age,
+ .port_change_mtu = rtl8366rb_change_mtu,
+ .port_max_mtu = rtl8366rb_max_mtu,
+ };
diff --git a/target/linux/generic/backport-6.1/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch b/target/linux/generic/backport-6.1/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch
new file mode 100644
index 0000000000..e787ce9481
--- /dev/null
+++ b/target/linux/generic/backport-6.1/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch
@@ -0,0 +1,107 @@
+From 90c855471a89d3e05ecf5b6464bd04abf2c83b70 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Tue, 5 Oct 2021 21:47:04 +0200
+Subject: [PATCH 10/11] net: dsa: rtl8366rb: Support setting STP state
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This adds support for setting the STP state to the RTL8366RB
+DSA switch. This rids the following message from the kernel on
+e.g. OpenWrt:
+
+DSA: failed to set STP state 3 (-95)
+
+Since the RTL8366RB has one STP state register per FID with
+two bit per port in each, we simply loop over all the FIDs
+and set the state on all of them.
+
+Cc: Vladimir Oltean <olteanv@gmail.com>
+Cc: Alvin Šipraga <alsi@bang-olufsen.dk>
+Cc: Mauri Sandberg <sandberg@mailfence.com>
+Cc: DENG Qingfang <dqfext@gmail.com>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/rtl8366rb.c | 48 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 48 insertions(+)
+
+--- a/drivers/net/dsa/rtl8366rb.c
++++ b/drivers/net/dsa/rtl8366rb.c
+@@ -110,6 +110,18 @@
+
+ #define RTL8366RB_POWER_SAVING_REG 0x0021
+
++/* Spanning tree status (STP) control, two bits per port per FID */
++#define RTL8366RB_STP_STATE_BASE 0x0050 /* 0x0050..0x0057 */
++#define RTL8366RB_STP_STATE_DISABLED 0x0
++#define RTL8366RB_STP_STATE_BLOCKING 0x1
++#define RTL8366RB_STP_STATE_LEARNING 0x2
++#define RTL8366RB_STP_STATE_FORWARDING 0x3
++#define RTL8366RB_STP_MASK GENMASK(1, 0)
++#define RTL8366RB_STP_STATE(port, state) \
++ ((state) << ((port) * 2))
++#define RTL8366RB_STP_STATE_MASK(port) \
++ RTL8366RB_STP_STATE((port), RTL8366RB_STP_MASK)
++
+ /* CPU port control reg */
+ #define RTL8368RB_CPU_CTRL_REG 0x0061
+ #define RTL8368RB_CPU_PORTS_MSK 0x00FF
+@@ -234,6 +246,7 @@
+ #define RTL8366RB_NUM_LEDGROUPS 4
+ #define RTL8366RB_NUM_VIDS 4096
+ #define RTL8366RB_PRIORITYMAX 7
++#define RTL8366RB_NUM_FIDS 8
+ #define RTL8366RB_FIDMAX 7
+
+ #define RTL8366RB_PORT_1 BIT(0) /* In userspace port 0 */
+@@ -1309,6 +1322,40 @@ rtl8366rb_port_bridge_flags(struct dsa_s
+ }
+
+ static void
++rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
++{
++ struct realtek_smi *smi = ds->priv;
++ u32 val;
++ int i;
++
++ switch (state) {
++ case BR_STATE_DISABLED:
++ val = RTL8366RB_STP_STATE_DISABLED;
++ break;
++ case BR_STATE_BLOCKING:
++ case BR_STATE_LISTENING:
++ val = RTL8366RB_STP_STATE_BLOCKING;
++ break;
++ case BR_STATE_LEARNING:
++ val = RTL8366RB_STP_STATE_LEARNING;
++ break;
++ case BR_STATE_FORWARDING:
++ val = RTL8366RB_STP_STATE_FORWARDING;
++ break;
++ default:
++ dev_err(smi->dev, "unknown bridge state requested\n");
++ return;
++ };
++
++ /* Set the same status for the port on all the FIDs */
++ for (i = 0; i < RTL8366RB_NUM_FIDS; i++) {
++ regmap_update_bits(smi->map, RTL8366RB_STP_STATE_BASE + i,
++ RTL8366RB_STP_STATE_MASK(port),
++ RTL8366RB_STP_STATE(port, val));
++ }
++}
++
++static void
+ rtl8366rb_port_fast_age(struct dsa_switch *ds, int port)
+ {
+ struct realtek_smi *smi = ds->priv;
+@@ -1733,6 +1780,7 @@ static const struct dsa_switch_ops rtl83
+ .port_disable = rtl8366rb_port_disable,
+ .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags,
+ .port_bridge_flags = rtl8366rb_port_bridge_flags,
++ .port_stp_state_set = rtl8366rb_port_stp_state_set,
+ .port_fast_age = rtl8366rb_port_fast_age,
+ .port_change_mtu = rtl8366rb_change_mtu,
+ .port_max_mtu = rtl8366rb_max_mtu,
diff --git a/target/linux/generic/backport-6.1/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch b/target/linux/generic/backport-6.1/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch
new file mode 100644
index 0000000000..9b9ab04af6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch
@@ -0,0 +1,142 @@
+From f9ec5723c3dbfcede9c7b0dcdf85e401ce16316c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sat, 23 Jul 2022 16:29:29 +0200
+Subject: [PATCH 1/5] net: ethernet: stmicro: stmmac: move queue reset to
+ dedicated functions
+
+Move queue reset to dedicated functions. This aside from a simple
+cleanup is also required to allocate a dma conf without resetting the tx
+queue while the device is temporarily detached as now the reset is not
+part of the dma init function and can be done later in the code flow.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ .../net/ethernet/stmicro/stmmac/stmmac_main.c | 59 ++++++++++---------
+ 1 file changed, 31 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -130,6 +130,9 @@ static irqreturn_t stmmac_mac_interrupt(
+ static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id);
+ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data);
+ static irqreturn_t stmmac_msi_intr_rx(int irq, void *data);
++static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue);
++static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue);
++static void stmmac_reset_queues_param(struct stmmac_priv *priv);
+ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue);
+ static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);
+
+@@ -1712,9 +1715,6 @@ static int __init_dma_rx_desc_rings(stru
+ return -ENOMEM;
+ }
+
+- rx_q->cur_rx = 0;
+- rx_q->dirty_rx = 0;
+-
+ /* Setup the chained descriptor addresses */
+ if (priv->mode == STMMAC_CHAIN_MODE) {
+ if (priv->extend_desc)
+@@ -1820,12 +1820,6 @@ static int __init_dma_tx_desc_rings(stru
+ tx_q->tx_skbuff[i] = NULL;
+ }
+
+- tx_q->dirty_tx = 0;
+- tx_q->cur_tx = 0;
+- tx_q->mss = 0;
+-
+- netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue));
+-
+ return 0;
+ }
+
+@@ -2694,10 +2688,7 @@ static void stmmac_tx_err(struct stmmac_
+ stmmac_stop_tx_dma(priv, chan);
+ dma_free_tx_skbufs(priv, chan);
+ stmmac_clear_tx_descriptors(priv, chan);
+- tx_q->dirty_tx = 0;
+- tx_q->cur_tx = 0;
+- tx_q->mss = 0;
+- netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, chan));
++ stmmac_reset_tx_queue(priv, chan);
+ stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ tx_q->dma_tx_phy, chan);
+ stmmac_start_tx_dma(priv, chan);
+@@ -3781,6 +3772,8 @@ static int stmmac_open(struct net_device
+ }
+ }
+
++ stmmac_reset_queues_param(priv);
++
+ ret = stmmac_hw_setup(dev, true);
+ if (ret < 0) {
+ netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
+@@ -6430,6 +6423,7 @@ void stmmac_enable_rx_queue(struct stmma
+ return;
+ }
+
++ stmmac_reset_rx_queue(priv, queue);
+ stmmac_clear_rx_descriptors(priv, queue);
+
+ stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+@@ -6491,6 +6485,7 @@ void stmmac_enable_tx_queue(struct stmma
+ return;
+ }
+
++ stmmac_reset_tx_queue(priv, queue);
+ stmmac_clear_tx_descriptors(priv, queue);
+
+ stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+@@ -7417,6 +7412,25 @@ int stmmac_suspend(struct device *dev)
+ }
+ EXPORT_SYMBOL_GPL(stmmac_suspend);
+
++static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)
++{
++ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++
++ rx_q->cur_rx = 0;
++ rx_q->dirty_rx = 0;
++}
++
++static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue)
++{
++ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++
++ tx_q->cur_tx = 0;
++ tx_q->dirty_tx = 0;
++ tx_q->mss = 0;
++
++ netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue));
++}
++
+ /**
+ * stmmac_reset_queues_param - reset queue parameters
+ * @priv: device pointer
+@@ -7427,22 +7441,11 @@ static void stmmac_reset_queues_param(st
+ u32 tx_cnt = priv->plat->tx_queues_to_use;
+ u32 queue;
+
+- for (queue = 0; queue < rx_cnt; queue++) {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ for (queue = 0; queue < rx_cnt; queue++)
++ stmmac_reset_rx_queue(priv, queue);
+
+- rx_q->cur_rx = 0;
+- rx_q->dirty_rx = 0;
+- }
+-
+- for (queue = 0; queue < tx_cnt; queue++) {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
+-
+- tx_q->cur_tx = 0;
+- tx_q->dirty_tx = 0;
+- tx_q->mss = 0;
+-
+- netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue));
+- }
++ for (queue = 0; queue < tx_cnt; queue++)
++ stmmac_reset_tx_queue(priv, queue);
+ }
+
+ /**
diff --git a/target/linux/generic/backport-6.1/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch b/target/linux/generic/backport-6.1/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch
new file mode 100644
index 0000000000..8eca92a5c5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch
@@ -0,0 +1,37 @@
+From 7028471edb646bfc532fec0973e50e784cdcb7c6 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sat, 23 Jul 2022 16:29:30 +0200
+Subject: [PATCH 2/5] net: ethernet: stmicro: stmmac: first disable all queues
+ and disconnect in release
+
+Disable all queues and disconnect before tx_disable in stmmac_release to
+prevent a corner case where packet may be still queued at the same time
+tx_disable is called resulting in kernel panic if some packet still has
+to be processed.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -3833,8 +3833,6 @@ static int stmmac_release(struct net_dev
+ struct stmmac_priv *priv = netdev_priv(dev);
+ u32 chan;
+
+- netif_tx_disable(dev);
+-
+ if (device_may_wakeup(priv->device))
+ phylink_speed_down(priv->phylink, false);
+ /* Stop and disconnect the PHY */
+@@ -3846,6 +3844,8 @@ static int stmmac_release(struct net_dev
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+ hrtimer_cancel(&priv->tx_queue[chan].txtimer);
+
++ netif_tx_disable(dev);
++
+ /* Free the IRQ lines */
+ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+
diff --git a/target/linux/generic/backport-6.1/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch b/target/linux/generic/backport-6.1/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch
new file mode 100644
index 0000000000..d4c2567a1f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch
@@ -0,0 +1,1289 @@
+From 8531c80800c10e8ef7952022326c2f983e1314bf Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sat, 23 Jul 2022 16:29:31 +0200
+Subject: [PATCH 3/5] net: ethernet: stmicro: stmmac: move dma conf to
+ dedicated struct
+
+Move dma buf conf to dedicated struct. This in preparation for code
+rework that will permit to allocate separate dma_conf without affecting
+the priv struct.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ .../net/ethernet/stmicro/stmmac/chain_mode.c | 6 +-
+ .../net/ethernet/stmicro/stmmac/ring_mode.c | 4 +-
+ drivers/net/ethernet/stmicro/stmmac/stmmac.h | 21 +-
+ .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 4 +-
+ .../net/ethernet/stmicro/stmmac/stmmac_main.c | 286 +++++++++---------
+ .../stmicro/stmmac/stmmac_selftests.c | 8 +-
+ .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 6 +-
+ 7 files changed, 172 insertions(+), 163 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
++++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
+@@ -46,7 +46,7 @@ static int jumbo_frm(void *p, struct sk_
+
+ while (len != 0) {
+ tx_q->tx_skbuff[entry] = NULL;
+- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size);
++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ desc = tx_q->dma_tx + entry;
+
+ if (len > bmax) {
+@@ -137,7 +137,7 @@ static void refill_desc3(void *priv_ptr,
+ */
+ p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy +
+ (((rx_q->dirty_rx) + 1) %
+- priv->dma_rx_size) *
++ priv->dma_conf.dma_rx_size) *
+ sizeof(struct dma_desc)));
+ }
+
+@@ -155,7 +155,7 @@ static void clean_desc3(void *priv_ptr,
+ */
+ p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy +
+ ((tx_q->dirty_tx + 1) %
+- priv->dma_tx_size))
++ priv->dma_conf.dma_tx_size))
+ * sizeof(struct dma_desc)));
+ }
+
+--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
++++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+@@ -51,7 +51,7 @@ static int jumbo_frm(void *p, struct sk_
+ stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum,
+ STMMAC_RING_MODE, 0, false, skb->len);
+ tx_q->tx_skbuff[entry] = NULL;
+- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size);
++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
+
+ if (priv->extend_desc)
+ desc = (struct dma_desc *)(tx_q->dma_etx + entry);
+@@ -107,7 +107,7 @@ static void refill_desc3(void *priv_ptr,
+ struct stmmac_priv *priv = rx_q->priv_data;
+
+ /* Fill DES3 in case of RING mode */
+- if (priv->dma_buf_sz == BUF_SIZE_16KiB)
++ if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB)
+ p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB);
+ }
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+@@ -185,6 +185,18 @@ struct stmmac_rfs_entry {
+ int tc;
+ };
+
++struct stmmac_dma_conf {
++ unsigned int dma_buf_sz;
++
++ /* RX Queue */
++ struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES];
++ unsigned int dma_rx_size;
++
++ /* TX Queue */
++ struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES];
++ unsigned int dma_tx_size;
++};
++
+ struct stmmac_priv {
+ /* Frequently used values are kept adjacent for cache effect */
+ u32 tx_coal_frames[MTL_MAX_TX_QUEUES];
+@@ -199,7 +211,6 @@ struct stmmac_priv {
+ int sph_cap;
+ u32 sarc_type;
+
+- unsigned int dma_buf_sz;
+ unsigned int rx_copybreak;
+ u32 rx_riwt[MTL_MAX_TX_QUEUES];
+ int hwts_rx_en;
+@@ -211,13 +222,7 @@ struct stmmac_priv {
+ int (*hwif_quirks)(struct stmmac_priv *priv);
+ struct mutex lock;
+
+- /* RX Queue */
+- struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES];
+- unsigned int dma_rx_size;
+-
+- /* TX Queue */
+- struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES];
+- unsigned int dma_tx_size;
++ struct stmmac_dma_conf dma_conf;
+
+ /* Generic channel for NAPI */
+ struct stmmac_channel channel[STMMAC_CH_MAX];
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+@@ -484,8 +484,8 @@ static void stmmac_get_ringparam(struct
+
+ ring->rx_max_pending = DMA_MAX_RX_SIZE;
+ ring->tx_max_pending = DMA_MAX_TX_SIZE;
+- ring->rx_pending = priv->dma_rx_size;
+- ring->tx_pending = priv->dma_tx_size;
++ ring->rx_pending = priv->dma_conf.dma_rx_size;
++ ring->tx_pending = priv->dma_conf.dma_tx_size;
+ }
+
+ static int stmmac_set_ringparam(struct net_device *netdev,
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -74,8 +74,8 @@ static int phyaddr = -1;
+ module_param(phyaddr, int, 0444);
+ MODULE_PARM_DESC(phyaddr, "Physical device address");
+
+-#define STMMAC_TX_THRESH(x) ((x)->dma_tx_size / 4)
+-#define STMMAC_RX_THRESH(x) ((x)->dma_rx_size / 4)
++#define STMMAC_TX_THRESH(x) ((x)->dma_conf.dma_tx_size / 4)
++#define STMMAC_RX_THRESH(x) ((x)->dma_conf.dma_rx_size / 4)
+
+ /* Limit to make sure XDP TX and slow path can coexist */
+ #define STMMAC_XSK_TX_BUDGET_MAX 256
+@@ -232,7 +232,7 @@ static void stmmac_disable_all_queues(st
+
+ /* synchronize_rcu() needed for pending XDP buffers to drain */
+ for (queue = 0; queue < rx_queues_cnt; queue++) {
+- rx_q = &priv->rx_queue[queue];
++ rx_q = &priv->dma_conf.rx_queue[queue];
+ if (rx_q->xsk_pool) {
+ synchronize_rcu();
+ break;
+@@ -358,13 +358,13 @@ static void print_pkt(unsigned char *buf
+
+ static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ u32 avail;
+
+ if (tx_q->dirty_tx > tx_q->cur_tx)
+ avail = tx_q->dirty_tx - tx_q->cur_tx - 1;
+ else
+- avail = priv->dma_tx_size - tx_q->cur_tx + tx_q->dirty_tx - 1;
++ avail = priv->dma_conf.dma_tx_size - tx_q->cur_tx + tx_q->dirty_tx - 1;
+
+ return avail;
+ }
+@@ -376,13 +376,13 @@ static inline u32 stmmac_tx_avail(struct
+ */
+ static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ u32 dirty;
+
+ if (rx_q->dirty_rx <= rx_q->cur_rx)
+ dirty = rx_q->cur_rx - rx_q->dirty_rx;
+ else
+- dirty = priv->dma_rx_size - rx_q->dirty_rx + rx_q->cur_rx;
++ dirty = priv->dma_conf.dma_rx_size - rx_q->dirty_rx + rx_q->cur_rx;
+
+ return dirty;
+ }
+@@ -410,7 +410,7 @@ static int stmmac_enable_eee_mode(struct
+
+ /* check if all TX queues have the work finished */
+ for (queue = 0; queue < tx_cnt; queue++) {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+
+ if (tx_q->dirty_tx != tx_q->cur_tx)
+ return -EBUSY; /* still unfinished work */
+@@ -1309,7 +1309,7 @@ static void stmmac_display_rx_rings(stru
+
+ /* Display RX rings */
+ for (queue = 0; queue < rx_cnt; queue++) {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+
+ pr_info("\tRX Queue %u rings\n", queue);
+
+@@ -1322,7 +1322,7 @@ static void stmmac_display_rx_rings(stru
+ }
+
+ /* Display RX ring */
+- stmmac_display_ring(priv, head_rx, priv->dma_rx_size, true,
++ stmmac_display_ring(priv, head_rx, priv->dma_conf.dma_rx_size, true,
+ rx_q->dma_rx_phy, desc_size);
+ }
+ }
+@@ -1336,7 +1336,7 @@ static void stmmac_display_tx_rings(stru
+
+ /* Display TX rings */
+ for (queue = 0; queue < tx_cnt; queue++) {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+
+ pr_info("\tTX Queue %d rings\n", queue);
+
+@@ -1351,7 +1351,7 @@ static void stmmac_display_tx_rings(stru
+ desc_size = sizeof(struct dma_desc);
+ }
+
+- stmmac_display_ring(priv, head_tx, priv->dma_tx_size, false,
++ stmmac_display_ring(priv, head_tx, priv->dma_conf.dma_tx_size, false,
+ tx_q->dma_tx_phy, desc_size);
+ }
+ }
+@@ -1392,21 +1392,21 @@ static int stmmac_set_bfsize(int mtu, in
+ */
+ static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ int i;
+
+ /* Clear the RX descriptors */
+- for (i = 0; i < priv->dma_rx_size; i++)
++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++)
+ if (priv->extend_desc)
+ stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic,
+ priv->use_riwt, priv->mode,
+- (i == priv->dma_rx_size - 1),
+- priv->dma_buf_sz);
++ (i == priv->dma_conf.dma_rx_size - 1),
++ priv->dma_conf.dma_buf_sz);
+ else
+ stmmac_init_rx_desc(priv, &rx_q->dma_rx[i],
+ priv->use_riwt, priv->mode,
+- (i == priv->dma_rx_size - 1),
+- priv->dma_buf_sz);
++ (i == priv->dma_conf.dma_rx_size - 1),
++ priv->dma_conf.dma_buf_sz);
+ }
+
+ /**
+@@ -1418,12 +1418,12 @@ static void stmmac_clear_rx_descriptors(
+ */
+ static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ int i;
+
+ /* Clear the TX descriptors */
+- for (i = 0; i < priv->dma_tx_size; i++) {
+- int last = (i == (priv->dma_tx_size - 1));
++ for (i = 0; i < priv->dma_conf.dma_tx_size; i++) {
++ int last = (i == (priv->dma_conf.dma_tx_size - 1));
+ struct dma_desc *p;
+
+ if (priv->extend_desc)
+@@ -1471,7 +1471,7 @@ static void stmmac_clear_descriptors(str
+ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
+ int i, gfp_t flags, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+
+ if (!buf->page) {
+@@ -1496,7 +1496,7 @@ static int stmmac_init_rx_buffers(struct
+ buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
+
+ stmmac_set_desc_addr(priv, p, buf->addr);
+- if (priv->dma_buf_sz == BUF_SIZE_16KiB)
++ if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB)
+ stmmac_init_desc3(priv, p);
+
+ return 0;
+@@ -1510,7 +1510,7 @@ static int stmmac_init_rx_buffers(struct
+ */
+ static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+
+ if (buf->page)
+@@ -1530,7 +1530,7 @@ static void stmmac_free_rx_buffer(struct
+ */
+ static void stmmac_free_tx_buffer(struct stmmac_priv *priv, u32 queue, int i)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+
+ if (tx_q->tx_skbuff_dma[i].buf &&
+ tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) {
+@@ -1575,17 +1575,17 @@ static void dma_free_rx_skbufs(struct st
+ {
+ int i;
+
+- for (i = 0; i < priv->dma_rx_size; i++)
++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++)
+ stmmac_free_rx_buffer(priv, queue, i);
+ }
+
+ static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv, u32 queue,
+ gfp_t flags)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ int i;
+
+- for (i = 0; i < priv->dma_rx_size; i++) {
++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) {
+ struct dma_desc *p;
+ int ret;
+
+@@ -1612,10 +1612,10 @@ static int stmmac_alloc_rx_buffers(struc
+ */
+ static void dma_free_rx_xskbufs(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ int i;
+
+- for (i = 0; i < priv->dma_rx_size; i++) {
++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) {
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+
+ if (!buf->xdp)
+@@ -1628,10 +1628,10 @@ static void dma_free_rx_xskbufs(struct s
+
+ static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ int i;
+
+- for (i = 0; i < priv->dma_rx_size; i++) {
++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) {
+ struct stmmac_rx_buffer *buf;
+ dma_addr_t dma_addr;
+ struct dma_desc *p;
+@@ -1674,7 +1674,7 @@ static struct xsk_buff_pool *stmmac_get_
+ */
+ static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, u32 queue, gfp_t flags)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ int ret;
+
+ netif_dbg(priv, probe, priv->dev,
+@@ -1720,11 +1720,11 @@ static int __init_dma_rx_desc_rings(stru
+ if (priv->extend_desc)
+ stmmac_mode_init(priv, rx_q->dma_erx,
+ rx_q->dma_rx_phy,
+- priv->dma_rx_size, 1);
++ priv->dma_conf.dma_rx_size, 1);
+ else
+ stmmac_mode_init(priv, rx_q->dma_rx,
+ rx_q->dma_rx_phy,
+- priv->dma_rx_size, 0);
++ priv->dma_conf.dma_rx_size, 0);
+ }
+
+ return 0;
+@@ -1751,7 +1751,7 @@ static int init_dma_rx_desc_rings(struct
+
+ err_init_rx_buffers:
+ while (queue >= 0) {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+
+ if (rx_q->xsk_pool)
+ dma_free_rx_xskbufs(priv, queue);
+@@ -1780,7 +1780,7 @@ err_init_rx_buffers:
+ */
+ static int __init_dma_tx_desc_rings(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ int i;
+
+ netif_dbg(priv, probe, priv->dev,
+@@ -1792,16 +1792,16 @@ static int __init_dma_tx_desc_rings(stru
+ if (priv->extend_desc)
+ stmmac_mode_init(priv, tx_q->dma_etx,
+ tx_q->dma_tx_phy,
+- priv->dma_tx_size, 1);
++ priv->dma_conf.dma_tx_size, 1);
+ else if (!(tx_q->tbs & STMMAC_TBS_AVAIL))
+ stmmac_mode_init(priv, tx_q->dma_tx,
+ tx_q->dma_tx_phy,
+- priv->dma_tx_size, 0);
++ priv->dma_conf.dma_tx_size, 0);
+ }
+
+ tx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue);
+
+- for (i = 0; i < priv->dma_tx_size; i++) {
++ for (i = 0; i < priv->dma_conf.dma_tx_size; i++) {
+ struct dma_desc *p;
+
+ if (priv->extend_desc)
+@@ -1871,12 +1871,12 @@ static int init_dma_desc_rings(struct ne
+ */
+ static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ int i;
+
+ tx_q->xsk_frames_done = 0;
+
+- for (i = 0; i < priv->dma_tx_size; i++)
++ for (i = 0; i < priv->dma_conf.dma_tx_size; i++)
+ stmmac_free_tx_buffer(priv, queue, i);
+
+ if (tx_q->xsk_pool && tx_q->xsk_frames_done) {
+@@ -1906,7 +1906,7 @@ static void stmmac_free_tx_skbufs(struct
+ */
+ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+
+ /* Release the DMA RX socket buffers */
+ if (rx_q->xsk_pool)
+@@ -1919,11 +1919,11 @@ static void __free_dma_rx_desc_resources
+
+ /* Free DMA regions of consistent memory previously allocated */
+ if (!priv->extend_desc)
+- dma_free_coherent(priv->device, priv->dma_rx_size *
++ dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size *
+ sizeof(struct dma_desc),
+ rx_q->dma_rx, rx_q->dma_rx_phy);
+ else
+- dma_free_coherent(priv->device, priv->dma_rx_size *
++ dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size *
+ sizeof(struct dma_extended_desc),
+ rx_q->dma_erx, rx_q->dma_rx_phy);
+
+@@ -1952,7 +1952,7 @@ static void free_dma_rx_desc_resources(s
+ */
+ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ size_t size;
+ void *addr;
+
+@@ -1970,7 +1970,7 @@ static void __free_dma_tx_desc_resources
+ addr = tx_q->dma_tx;
+ }
+
+- size *= priv->dma_tx_size;
++ size *= priv->dma_conf.dma_tx_size;
+
+ dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);
+
+@@ -1999,7 +1999,7 @@ static void free_dma_tx_desc_resources(s
+ */
+ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_channel *ch = &priv->channel[queue];
+ bool xdp_prog = stmmac_xdp_is_enabled(priv);
+ struct page_pool_params pp_params = { 0 };
+@@ -2011,8 +2011,8 @@ static int __alloc_dma_rx_desc_resources
+ rx_q->priv_data = priv;
+
+ pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
+- pp_params.pool_size = priv->dma_rx_size;
+- num_pages = DIV_ROUND_UP(priv->dma_buf_sz, PAGE_SIZE);
++ pp_params.pool_size = priv->dma_conf.dma_rx_size;
++ num_pages = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE);
+ pp_params.order = ilog2(num_pages);
+ pp_params.nid = dev_to_node(priv->device);
+ pp_params.dev = priv->device;
+@@ -2027,7 +2027,7 @@ static int __alloc_dma_rx_desc_resources
+ return ret;
+ }
+
+- rx_q->buf_pool = kcalloc(priv->dma_rx_size,
++ rx_q->buf_pool = kcalloc(priv->dma_conf.dma_rx_size,
+ sizeof(*rx_q->buf_pool),
+ GFP_KERNEL);
+ if (!rx_q->buf_pool)
+@@ -2035,7 +2035,7 @@ static int __alloc_dma_rx_desc_resources
+
+ if (priv->extend_desc) {
+ rx_q->dma_erx = dma_alloc_coherent(priv->device,
+- priv->dma_rx_size *
++ priv->dma_conf.dma_rx_size *
+ sizeof(struct dma_extended_desc),
+ &rx_q->dma_rx_phy,
+ GFP_KERNEL);
+@@ -2044,7 +2044,7 @@ static int __alloc_dma_rx_desc_resources
+
+ } else {
+ rx_q->dma_rx = dma_alloc_coherent(priv->device,
+- priv->dma_rx_size *
++ priv->dma_conf.dma_rx_size *
+ sizeof(struct dma_desc),
+ &rx_q->dma_rx_phy,
+ GFP_KERNEL);
+@@ -2101,20 +2101,20 @@ err_dma:
+ */
+ static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ size_t size;
+ void *addr;
+
+ tx_q->queue_index = queue;
+ tx_q->priv_data = priv;
+
+- tx_q->tx_skbuff_dma = kcalloc(priv->dma_tx_size,
++ tx_q->tx_skbuff_dma = kcalloc(priv->dma_conf.dma_tx_size,
+ sizeof(*tx_q->tx_skbuff_dma),
+ GFP_KERNEL);
+ if (!tx_q->tx_skbuff_dma)
+ return -ENOMEM;
+
+- tx_q->tx_skbuff = kcalloc(priv->dma_tx_size,
++ tx_q->tx_skbuff = kcalloc(priv->dma_conf.dma_tx_size,
+ sizeof(struct sk_buff *),
+ GFP_KERNEL);
+ if (!tx_q->tx_skbuff)
+@@ -2127,7 +2127,7 @@ static int __alloc_dma_tx_desc_resources
+ else
+ size = sizeof(struct dma_desc);
+
+- size *= priv->dma_tx_size;
++ size *= priv->dma_conf.dma_tx_size;
+
+ addr = dma_alloc_coherent(priv->device, size,
+ &tx_q->dma_tx_phy, GFP_KERNEL);
+@@ -2371,7 +2371,7 @@ static void stmmac_dma_operation_mode(st
+
+ /* configure all channels */
+ for (chan = 0; chan < rx_channels_count; chan++) {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
+ u32 buf_size;
+
+ qmode = priv->plat->rx_queues_cfg[chan].mode_to_use;
+@@ -2386,7 +2386,7 @@ static void stmmac_dma_operation_mode(st
+ chan);
+ } else {
+ stmmac_set_dma_bfsize(priv, priv->ioaddr,
+- priv->dma_buf_sz,
++ priv->dma_conf.dma_buf_sz,
+ chan);
+ }
+ }
+@@ -2402,7 +2402,7 @@ static void stmmac_dma_operation_mode(st
+ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
+ {
+ struct netdev_queue *nq = netdev_get_tx_queue(priv->dev, queue);
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct xsk_buff_pool *pool = tx_q->xsk_pool;
+ unsigned int entry = tx_q->cur_tx;
+ struct dma_desc *tx_desc = NULL;
+@@ -2477,7 +2477,7 @@ static bool stmmac_xdp_xmit_zc(struct st
+
+ stmmac_enable_dma_transmission(priv, priv->ioaddr);
+
+- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_tx_size);
++ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
+ entry = tx_q->cur_tx;
+ }
+
+@@ -2503,7 +2503,7 @@ static bool stmmac_xdp_xmit_zc(struct st
+ */
+ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ unsigned int bytes_compl = 0, pkts_compl = 0;
+ unsigned int entry, xmits = 0, count = 0;
+
+@@ -2516,7 +2516,7 @@ static int stmmac_tx_clean(struct stmmac
+ entry = tx_q->dirty_tx;
+
+ /* Try to clean all TX complete frame in 1 shot */
+- while ((entry != tx_q->cur_tx) && count < priv->dma_tx_size) {
++ while ((entry != tx_q->cur_tx) && count < priv->dma_conf.dma_tx_size) {
+ struct xdp_frame *xdpf;
+ struct sk_buff *skb;
+ struct dma_desc *p;
+@@ -2616,7 +2616,7 @@ static int stmmac_tx_clean(struct stmmac
+
+ stmmac_release_tx_desc(priv, p, priv->mode);
+
+- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size);
++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ }
+ tx_q->dirty_tx = entry;
+
+@@ -2681,7 +2681,7 @@ static int stmmac_tx_clean(struct stmmac
+ */
+ static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+
+ netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan));
+
+@@ -2748,8 +2748,8 @@ static int stmmac_napi_check(struct stmm
+ {
+ int status = stmmac_dma_interrupt_status(priv, priv->ioaddr,
+ &priv->xstats, chan, dir);
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan];
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ struct stmmac_channel *ch = &priv->channel[chan];
+ struct napi_struct *rx_napi;
+ struct napi_struct *tx_napi;
+@@ -2925,7 +2925,7 @@ static int stmmac_init_dma_engine(struct
+
+ /* DMA RX Channel Configuration */
+ for (chan = 0; chan < rx_channels_count; chan++) {
+- rx_q = &priv->rx_queue[chan];
++ rx_q = &priv->dma_conf.rx_queue[chan];
+
+ stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ rx_q->dma_rx_phy, chan);
+@@ -2939,7 +2939,7 @@ static int stmmac_init_dma_engine(struct
+
+ /* DMA TX Channel Configuration */
+ for (chan = 0; chan < tx_channels_count; chan++) {
+- tx_q = &priv->tx_queue[chan];
++ tx_q = &priv->dma_conf.tx_queue[chan];
+
+ stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ tx_q->dma_tx_phy, chan);
+@@ -2954,7 +2954,7 @@ static int stmmac_init_dma_engine(struct
+
+ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+
+ hrtimer_start(&tx_q->txtimer,
+ STMMAC_COAL_TIMER(priv->tx_coal_timer[queue]),
+@@ -3004,7 +3004,7 @@ static void stmmac_init_coalesce(struct
+ u32 chan;
+
+ for (chan = 0; chan < tx_channel_count; chan++) {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+
+ priv->tx_coal_frames[chan] = STMMAC_TX_FRAMES;
+ priv->tx_coal_timer[chan] = STMMAC_COAL_TX_TIMER;
+@@ -3026,12 +3026,12 @@ static void stmmac_set_rings_length(stru
+ /* set TX ring length */
+ for (chan = 0; chan < tx_channels_count; chan++)
+ stmmac_set_tx_ring_len(priv, priv->ioaddr,
+- (priv->dma_tx_size - 1), chan);
++ (priv->dma_conf.dma_tx_size - 1), chan);
+
+ /* set RX ring length */
+ for (chan = 0; chan < rx_channels_count; chan++)
+ stmmac_set_rx_ring_len(priv, priv->ioaddr,
+- (priv->dma_rx_size - 1), chan);
++ (priv->dma_conf.dma_rx_size - 1), chan);
+ }
+
+ /**
+@@ -3366,7 +3366,7 @@ static int stmmac_hw_setup(struct net_de
+ /* Enable TSO */
+ if (priv->tso) {
+ for (chan = 0; chan < tx_cnt; chan++) {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+
+ /* TSO and TBS cannot co-exist */
+ if (tx_q->tbs & STMMAC_TBS_AVAIL)
+@@ -3388,7 +3388,7 @@ static int stmmac_hw_setup(struct net_de
+
+ /* TBS */
+ for (chan = 0; chan < tx_cnt; chan++) {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ int enable = tx_q->tbs & STMMAC_TBS_AVAIL;
+
+ stmmac_enable_tbs(priv, priv->ioaddr, enable, chan);
+@@ -3432,7 +3432,7 @@ static void stmmac_free_irq(struct net_d
+ for (j = irq_idx - 1; j >= 0; j--) {
+ if (priv->tx_irq[j] > 0) {
+ irq_set_affinity_hint(priv->tx_irq[j], NULL);
+- free_irq(priv->tx_irq[j], &priv->tx_queue[j]);
++ free_irq(priv->tx_irq[j], &priv->dma_conf.tx_queue[j]);
+ }
+ }
+ irq_idx = priv->plat->rx_queues_to_use;
+@@ -3441,7 +3441,7 @@ static void stmmac_free_irq(struct net_d
+ for (j = irq_idx - 1; j >= 0; j--) {
+ if (priv->rx_irq[j] > 0) {
+ irq_set_affinity_hint(priv->rx_irq[j], NULL);
+- free_irq(priv->rx_irq[j], &priv->rx_queue[j]);
++ free_irq(priv->rx_irq[j], &priv->dma_conf.rx_queue[j]);
+ }
+ }
+
+@@ -3574,7 +3574,7 @@ static int stmmac_request_irq_multi_msi(
+ sprintf(int_name, "%s:%s-%d", dev->name, "rx", i);
+ ret = request_irq(priv->rx_irq[i],
+ stmmac_msi_intr_rx,
+- 0, int_name, &priv->rx_queue[i]);
++ 0, int_name, &priv->dma_conf.rx_queue[i]);
+ if (unlikely(ret < 0)) {
+ netdev_err(priv->dev,
+ "%s: alloc rx-%d MSI %d (error: %d)\n",
+@@ -3597,7 +3597,7 @@ static int stmmac_request_irq_multi_msi(
+ sprintf(int_name, "%s:%s-%d", dev->name, "tx", i);
+ ret = request_irq(priv->tx_irq[i],
+ stmmac_msi_intr_tx,
+- 0, int_name, &priv->tx_queue[i]);
++ 0, int_name, &priv->dma_conf.tx_queue[i]);
+ if (unlikely(ret < 0)) {
+ netdev_err(priv->dev,
+ "%s: alloc tx-%d MSI %d (error: %d)\n",
+@@ -3728,21 +3728,21 @@ static int stmmac_open(struct net_device
+ bfsize = 0;
+
+ if (bfsize < BUF_SIZE_16KiB)
+- bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz);
++ bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_conf.dma_buf_sz);
+
+- priv->dma_buf_sz = bfsize;
++ priv->dma_conf.dma_buf_sz = bfsize;
+ buf_sz = bfsize;
+
+ priv->rx_copybreak = STMMAC_RX_COPYBREAK;
+
+- if (!priv->dma_tx_size)
+- priv->dma_tx_size = DMA_DEFAULT_TX_SIZE;
+- if (!priv->dma_rx_size)
+- priv->dma_rx_size = DMA_DEFAULT_RX_SIZE;
++ if (!priv->dma_conf.dma_tx_size)
++ priv->dma_conf.dma_tx_size = DMA_DEFAULT_TX_SIZE;
++ if (!priv->dma_conf.dma_rx_size)
++ priv->dma_conf.dma_rx_size = DMA_DEFAULT_RX_SIZE;
+
+ /* Earlier check for TBS */
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+ int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en;
+
+ /* Setup per-TXQ tbs flag before TX descriptor alloc */
+@@ -3800,7 +3800,7 @@ irq_error:
+ phylink_stop(priv->phylink);
+
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+- hrtimer_cancel(&priv->tx_queue[chan].txtimer);
++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+
+ stmmac_hw_teardown(dev);
+ init_error:
+@@ -3842,7 +3842,7 @@ static int stmmac_release(struct net_dev
+ stmmac_disable_all_queues(priv);
+
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+- hrtimer_cancel(&priv->tx_queue[chan].txtimer);
++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+
+ netif_tx_disable(dev);
+
+@@ -3906,7 +3906,7 @@ static bool stmmac_vlan_insert(struct st
+ return false;
+
+ stmmac_set_tx_owner(priv, p);
+- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_tx_size);
++ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
+ return true;
+ }
+
+@@ -3924,7 +3924,7 @@ static bool stmmac_vlan_insert(struct st
+ static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
+ int total_len, bool last_segment, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct dma_desc *desc;
+ u32 buff_size;
+ int tmp_len;
+@@ -3935,7 +3935,7 @@ static void stmmac_tso_allocator(struct
+ dma_addr_t curr_addr;
+
+ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx,
+- priv->dma_tx_size);
++ priv->dma_conf.dma_tx_size);
+ WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
+
+ if (tx_q->tbs & STMMAC_TBS_AVAIL)
+@@ -3963,7 +3963,7 @@ static void stmmac_tso_allocator(struct
+
+ static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ int desc_size;
+
+ if (likely(priv->extend_desc))
+@@ -4025,7 +4025,7 @@ static netdev_tx_t stmmac_tso_xmit(struc
+ dma_addr_t des;
+ int i;
+
+- tx_q = &priv->tx_queue[queue];
++ tx_q = &priv->dma_conf.tx_queue[queue];
+ first_tx = tx_q->cur_tx;
+
+ /* Compute header lengths */
+@@ -4065,7 +4065,7 @@ static netdev_tx_t stmmac_tso_xmit(struc
+ stmmac_set_mss(priv, mss_desc, mss);
+ tx_q->mss = mss;
+ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx,
+- priv->dma_tx_size);
++ priv->dma_conf.dma_tx_size);
+ WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
+ }
+
+@@ -4177,7 +4177,7 @@ static netdev_tx_t stmmac_tso_xmit(struc
+ * ndo_start_xmit will fill this descriptor the next time it's
+ * called and stmmac_tx_clean may clean up to this descriptor.
+ */
+- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_tx_size);
++ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size);
+
+ if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) {
+ netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n",
+@@ -4265,7 +4265,7 @@ static netdev_tx_t stmmac_xmit(struct sk
+ int entry, first_tx;
+ dma_addr_t des;
+
+- tx_q = &priv->tx_queue[queue];
++ tx_q = &priv->dma_conf.tx_queue[queue];
+ first_tx = tx_q->cur_tx;
+
+ if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en)
+@@ -4328,7 +4328,7 @@ static netdev_tx_t stmmac_xmit(struct sk
+ int len = skb_frag_size(frag);
+ bool last_segment = (i == (nfrags - 1));
+
+- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size);
++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ WARN_ON(tx_q->tx_skbuff[entry]);
+
+ if (likely(priv->extend_desc))
+@@ -4399,7 +4399,7 @@ static netdev_tx_t stmmac_xmit(struct sk
+ * ndo_start_xmit will fill this descriptor the next time it's
+ * called and stmmac_tx_clean may clean up to this descriptor.
+ */
+- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size);
++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ tx_q->cur_tx = entry;
+
+ if (netif_msg_pktdata(priv)) {
+@@ -4514,7 +4514,7 @@ static void stmmac_rx_vlan(struct net_de
+ */
+ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ int dirty = stmmac_rx_dirty(priv, queue);
+ unsigned int entry = rx_q->dirty_rx;
+
+@@ -4564,7 +4564,7 @@ static inline void stmmac_rx_refill(stru
+ dma_wmb();
+ stmmac_set_rx_owner(priv, p, use_rx_wd);
+
+- entry = STMMAC_GET_ENTRY(entry, priv->dma_rx_size);
++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size);
+ }
+ rx_q->dirty_rx = entry;
+ rx_q->rx_tail_addr = rx_q->dma_rx_phy +
+@@ -4592,12 +4592,12 @@ static unsigned int stmmac_rx_buf1_len(s
+
+ /* First descriptor, not last descriptor and not split header */
+ if (status & rx_not_ls)
+- return priv->dma_buf_sz;
++ return priv->dma_conf.dma_buf_sz;
+
+ plen = stmmac_get_rx_frame_len(priv, p, coe);
+
+ /* First descriptor and last descriptor and not split header */
+- return min_t(unsigned int, priv->dma_buf_sz, plen);
++ return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen);
+ }
+
+ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
+@@ -4613,7 +4613,7 @@ static unsigned int stmmac_rx_buf2_len(s
+
+ /* Not last descriptor */
+ if (status & rx_not_ls)
+- return priv->dma_buf_sz;
++ return priv->dma_conf.dma_buf_sz;
+
+ plen = stmmac_get_rx_frame_len(priv, p, coe);
+
+@@ -4624,7 +4624,7 @@ static unsigned int stmmac_rx_buf2_len(s
+ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
+ struct xdp_frame *xdpf, bool dma_map)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ unsigned int entry = tx_q->cur_tx;
+ struct dma_desc *tx_desc;
+ dma_addr_t dma_addr;
+@@ -4687,7 +4687,7 @@ static int stmmac_xdp_xmit_xdpf(struct s
+
+ stmmac_enable_dma_transmission(priv, priv->ioaddr);
+
+- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size);
++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ tx_q->cur_tx = entry;
+
+ return STMMAC_XDP_TX;
+@@ -4861,7 +4861,7 @@ static void stmmac_dispatch_skb_zc(struc
+
+ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ unsigned int entry = rx_q->dirty_rx;
+ struct dma_desc *rx_desc = NULL;
+ bool ret = true;
+@@ -4904,7 +4904,7 @@ static bool stmmac_rx_refill_zc(struct s
+ dma_wmb();
+ stmmac_set_rx_owner(priv, rx_desc, use_rx_wd);
+
+- entry = STMMAC_GET_ENTRY(entry, priv->dma_rx_size);
++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size);
+ }
+
+ if (rx_desc) {
+@@ -4919,7 +4919,7 @@ static bool stmmac_rx_refill_zc(struct s
+
+ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ unsigned int count = 0, error = 0, len = 0;
+ int dirty = stmmac_rx_dirty(priv, queue);
+ unsigned int next_entry = rx_q->cur_rx;
+@@ -4941,7 +4941,7 @@ static int stmmac_rx_zc(struct stmmac_pr
+ desc_size = sizeof(struct dma_desc);
+ }
+
+- stmmac_display_ring(priv, rx_head, priv->dma_rx_size, true,
++ stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true,
+ rx_q->dma_rx_phy, desc_size);
+ }
+ while (count < limit) {
+@@ -4988,7 +4988,7 @@ read_again:
+
+ /* Prefetch the next RX descriptor */
+ rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx,
+- priv->dma_rx_size);
++ priv->dma_conf.dma_rx_size);
+ next_entry = rx_q->cur_rx;
+
+ if (priv->extend_desc)
+@@ -5109,7 +5109,7 @@ read_again:
+ */
+ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_channel *ch = &priv->channel[queue];
+ unsigned int count = 0, error = 0, len = 0;
+ int status = 0, coe = priv->hw->rx_csum;
+@@ -5122,7 +5122,7 @@ static int stmmac_rx(struct stmmac_priv
+ int buf_sz;
+
+ dma_dir = page_pool_get_dma_dir(rx_q->page_pool);
+- buf_sz = DIV_ROUND_UP(priv->dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
++ buf_sz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
+
+ if (netif_msg_rx_status(priv)) {
+ void *rx_head;
+@@ -5136,7 +5136,7 @@ static int stmmac_rx(struct stmmac_priv
+ desc_size = sizeof(struct dma_desc);
+ }
+
+- stmmac_display_ring(priv, rx_head, priv->dma_rx_size, true,
++ stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true,
+ rx_q->dma_rx_phy, desc_size);
+ }
+ while (count < limit) {
+@@ -5180,7 +5180,7 @@ read_again:
+ break;
+
+ rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx,
+- priv->dma_rx_size);
++ priv->dma_conf.dma_rx_size);
+ next_entry = rx_q->cur_rx;
+
+ if (priv->extend_desc)
+@@ -5314,7 +5314,7 @@ read_again:
+ buf1_len, dma_dir);
+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
+ buf->page, buf->page_offset, buf1_len,
+- priv->dma_buf_sz);
++ priv->dma_conf.dma_buf_sz);
+
+ /* Data payload appended into SKB */
+ page_pool_release_page(rx_q->page_pool, buf->page);
+@@ -5326,7 +5326,7 @@ read_again:
+ buf2_len, dma_dir);
+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
+ buf->sec_page, 0, buf2_len,
+- priv->dma_buf_sz);
++ priv->dma_conf.dma_buf_sz);
+
+ /* Data payload appended into SKB */
+ page_pool_release_page(rx_q->page_pool, buf->sec_page);
+@@ -5768,11 +5768,13 @@ static irqreturn_t stmmac_safety_interru
+ static irqreturn_t stmmac_msi_intr_tx(int irq, void *data)
+ {
+ struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)data;
++ struct stmmac_dma_conf *dma_conf;
+ int chan = tx_q->queue_index;
+ struct stmmac_priv *priv;
+ int status;
+
+- priv = container_of(tx_q, struct stmmac_priv, tx_queue[chan]);
++ dma_conf = container_of(tx_q, struct stmmac_dma_conf, tx_queue[chan]);
++ priv = container_of(dma_conf, struct stmmac_priv, dma_conf);
+
+ if (unlikely(!data)) {
+ netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__);
+@@ -5812,10 +5814,12 @@ static irqreturn_t stmmac_msi_intr_tx(in
+ static irqreturn_t stmmac_msi_intr_rx(int irq, void *data)
+ {
+ struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)data;
++ struct stmmac_dma_conf *dma_conf;
+ int chan = rx_q->queue_index;
+ struct stmmac_priv *priv;
+
+- priv = container_of(rx_q, struct stmmac_priv, rx_queue[chan]);
++ dma_conf = container_of(rx_q, struct stmmac_dma_conf, rx_queue[chan]);
++ priv = container_of(dma_conf, struct stmmac_priv, dma_conf);
+
+ if (unlikely(!data)) {
+ netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__);
+@@ -5846,10 +5850,10 @@ static void stmmac_poll_controller(struc
+
+ if (priv->plat->multi_msi_en) {
+ for (i = 0; i < priv->plat->rx_queues_to_use; i++)
+- stmmac_msi_intr_rx(0, &priv->rx_queue[i]);
++ stmmac_msi_intr_rx(0, &priv->dma_conf.rx_queue[i]);
+
+ for (i = 0; i < priv->plat->tx_queues_to_use; i++)
+- stmmac_msi_intr_tx(0, &priv->tx_queue[i]);
++ stmmac_msi_intr_tx(0, &priv->dma_conf.tx_queue[i]);
+ } else {
+ disable_irq(dev->irq);
+ stmmac_interrupt(dev->irq, dev);
+@@ -6030,34 +6034,34 @@ static int stmmac_rings_status_show(stru
+ return 0;
+
+ for (queue = 0; queue < rx_count; queue++) {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+
+ seq_printf(seq, "RX Queue %d:\n", queue);
+
+ if (priv->extend_desc) {
+ seq_printf(seq, "Extended descriptor ring:\n");
+ sysfs_display_ring((void *)rx_q->dma_erx,
+- priv->dma_rx_size, 1, seq, rx_q->dma_rx_phy);
++ priv->dma_conf.dma_rx_size, 1, seq, rx_q->dma_rx_phy);
+ } else {
+ seq_printf(seq, "Descriptor ring:\n");
+ sysfs_display_ring((void *)rx_q->dma_rx,
+- priv->dma_rx_size, 0, seq, rx_q->dma_rx_phy);
++ priv->dma_conf.dma_rx_size, 0, seq, rx_q->dma_rx_phy);
+ }
+ }
+
+ for (queue = 0; queue < tx_count; queue++) {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+
+ seq_printf(seq, "TX Queue %d:\n", queue);
+
+ if (priv->extend_desc) {
+ seq_printf(seq, "Extended descriptor ring:\n");
+ sysfs_display_ring((void *)tx_q->dma_etx,
+- priv->dma_tx_size, 1, seq, tx_q->dma_tx_phy);
++ priv->dma_conf.dma_tx_size, 1, seq, tx_q->dma_tx_phy);
+ } else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) {
+ seq_printf(seq, "Descriptor ring:\n");
+ sysfs_display_ring((void *)tx_q->dma_tx,
+- priv->dma_tx_size, 0, seq, tx_q->dma_tx_phy);
++ priv->dma_conf.dma_tx_size, 0, seq, tx_q->dma_tx_phy);
+ }
+ }
+
+@@ -6404,7 +6408,7 @@ void stmmac_disable_rx_queue(struct stmm
+
+ void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_channel *ch = &priv->channel[queue];
+ unsigned long flags;
+ u32 buf_size;
+@@ -6441,7 +6445,7 @@ void stmmac_enable_rx_queue(struct stmma
+ rx_q->queue_index);
+ } else {
+ stmmac_set_dma_bfsize(priv, priv->ioaddr,
+- priv->dma_buf_sz,
++ priv->dma_conf.dma_buf_sz,
+ rx_q->queue_index);
+ }
+
+@@ -6467,7 +6471,7 @@ void stmmac_disable_tx_queue(struct stmm
+
+ void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+ struct stmmac_channel *ch = &priv->channel[queue];
+ unsigned long flags;
+ int ret;
+@@ -6517,7 +6521,7 @@ void stmmac_xdp_release(struct net_devic
+ stmmac_disable_all_queues(priv);
+
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+- hrtimer_cancel(&priv->tx_queue[chan].txtimer);
++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+
+ /* Free the IRQ lines */
+ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+@@ -6576,7 +6580,7 @@ int stmmac_xdp_open(struct net_device *d
+
+ /* DMA RX Channel Configuration */
+ for (chan = 0; chan < rx_cnt; chan++) {
+- rx_q = &priv->rx_queue[chan];
++ rx_q = &priv->dma_conf.rx_queue[chan];
+
+ stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ rx_q->dma_rx_phy, chan);
+@@ -6594,7 +6598,7 @@ int stmmac_xdp_open(struct net_device *d
+ rx_q->queue_index);
+ } else {
+ stmmac_set_dma_bfsize(priv, priv->ioaddr,
+- priv->dma_buf_sz,
++ priv->dma_conf.dma_buf_sz,
+ rx_q->queue_index);
+ }
+
+@@ -6603,7 +6607,7 @@ int stmmac_xdp_open(struct net_device *d
+
+ /* DMA TX Channel Configuration */
+ for (chan = 0; chan < tx_cnt; chan++) {
+- tx_q = &priv->tx_queue[chan];
++ tx_q = &priv->dma_conf.tx_queue[chan];
+
+ stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ tx_q->dma_tx_phy, chan);
+@@ -6636,7 +6640,7 @@ int stmmac_xdp_open(struct net_device *d
+
+ irq_error:
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+- hrtimer_cancel(&priv->tx_queue[chan].txtimer);
++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+
+ stmmac_hw_teardown(dev);
+ init_error:
+@@ -6663,8 +6667,8 @@ int stmmac_xsk_wakeup(struct net_device
+ queue >= priv->plat->tx_queues_to_use)
+ return -EINVAL;
+
+- rx_q = &priv->rx_queue[queue];
+- tx_q = &priv->tx_queue[queue];
++ rx_q = &priv->dma_conf.rx_queue[queue];
++ tx_q = &priv->dma_conf.tx_queue[queue];
+ ch = &priv->channel[queue];
+
+ if (!rx_q->xsk_pool && !tx_q->xsk_pool)
+@@ -6924,8 +6928,8 @@ int stmmac_reinit_ringparam(struct net_d
+ if (netif_running(dev))
+ stmmac_release(dev);
+
+- priv->dma_rx_size = rx_size;
+- priv->dma_tx_size = tx_size;
++ priv->dma_conf.dma_rx_size = rx_size;
++ priv->dma_conf.dma_tx_size = tx_size;
+
+ if (netif_running(dev))
+ ret = stmmac_open(dev);
+@@ -7363,7 +7367,7 @@ int stmmac_suspend(struct device *dev)
+ stmmac_disable_all_queues(priv);
+
+ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
+- hrtimer_cancel(&priv->tx_queue[chan].txtimer);
++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
+
+ if (priv->eee_enabled) {
+ priv->tx_path_in_lpi_mode = false;
+@@ -7414,7 +7418,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend);
+
+ static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+
+ rx_q->cur_rx = 0;
+ rx_q->dirty_rx = 0;
+@@ -7422,7 +7426,7 @@ static void stmmac_reset_rx_queue(struct
+
+ static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+
+ tx_q->cur_tx = 0;
+ tx_q->dirty_tx = 0;
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
+@@ -795,8 +795,8 @@ static int stmmac_test_flowctrl(struct s
+ struct stmmac_channel *ch = &priv->channel[i];
+ u32 tail;
+
+- tail = priv->rx_queue[i].dma_rx_phy +
+- (priv->dma_rx_size * sizeof(struct dma_desc));
++ tail = priv->dma_conf.rx_queue[i].dma_rx_phy +
++ (priv->dma_conf.dma_rx_size * sizeof(struct dma_desc));
+
+ stmmac_set_rx_tail_ptr(priv, priv->ioaddr, tail, i);
+ stmmac_start_rx(priv, priv->ioaddr, i);
+@@ -1684,7 +1684,7 @@ cleanup:
+ static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue)
+ {
+ struct stmmac_packet_attrs attr = { };
+- int size = priv->dma_buf_sz;
++ int size = priv->dma_conf.dma_buf_sz;
+
+ attr.dst = priv->dev->dev_addr;
+ attr.max_size = size - ETH_FCS_LEN;
+@@ -1767,7 +1767,7 @@ static int stmmac_test_tbs(struct stmmac
+
+ /* Find first TBS enabled Queue, if any */
+ for (i = 0; i < priv->plat->tx_queues_to_use; i++)
+- if (priv->tx_queue[i].tbs & STMMAC_TBS_AVAIL)
++ if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_AVAIL)
+ break;
+
+ if (i >= priv->plat->tx_queues_to_use)
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+@@ -970,13 +970,13 @@ static int tc_setup_etf(struct stmmac_pr
+ return -EOPNOTSUPP;
+ if (qopt->queue >= priv->plat->tx_queues_to_use)
+ return -EINVAL;
+- if (!(priv->tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
++ if (!(priv->dma_conf.tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL))
+ return -EINVAL;
+
+ if (qopt->enable)
+- priv->tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN;
++ priv->dma_conf.tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN;
+ else
+- priv->tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN;
++ priv->dma_conf.tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN;
+
+ netdev_info(priv->dev, "%s ETF for Queue %d\n",
+ qopt->enable ? "enabled" : "disabled", qopt->queue);
diff --git a/target/linux/generic/backport-6.1/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch b/target/linux/generic/backport-6.1/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch
new file mode 100644
index 0000000000..e84373b471
--- /dev/null
+++ b/target/linux/generic/backport-6.1/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch
@@ -0,0 +1,1161 @@
+From ba39b344e9240a4a5fd4ab8178200b85cd1809da Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sat, 23 Jul 2022 16:29:32 +0200
+Subject: [PATCH 4/5] net: ethernet: stmicro: stmmac: generate stmmac dma conf
+ before open
+
+Rework the driver to generate the stmmac dma_conf before stmmac_open.
+This permits a function to first check if it's possible to allocate a
+new dma_config and then pass it directly to __stmmac_open and "open" the
+interface with the new configuration.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ .../net/ethernet/stmicro/stmmac/stmmac_main.c | 462 +++++++++++-------
+ 1 file changed, 289 insertions(+), 173 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -1300,7 +1300,8 @@ static int stmmac_phy_setup(struct stmma
+ return 0;
+ }
+
+-static void stmmac_display_rx_rings(struct stmmac_priv *priv)
++static void stmmac_display_rx_rings(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ u32 rx_cnt = priv->plat->rx_queues_to_use;
+ unsigned int desc_size;
+@@ -1309,7 +1310,7 @@ static void stmmac_display_rx_rings(stru
+
+ /* Display RX rings */
+ for (queue = 0; queue < rx_cnt; queue++) {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+
+ pr_info("\tRX Queue %u rings\n", queue);
+
+@@ -1322,12 +1323,13 @@ static void stmmac_display_rx_rings(stru
+ }
+
+ /* Display RX ring */
+- stmmac_display_ring(priv, head_rx, priv->dma_conf.dma_rx_size, true,
++ stmmac_display_ring(priv, head_rx, dma_conf->dma_rx_size, true,
+ rx_q->dma_rx_phy, desc_size);
+ }
+ }
+
+-static void stmmac_display_tx_rings(struct stmmac_priv *priv)
++static void stmmac_display_tx_rings(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ u32 tx_cnt = priv->plat->tx_queues_to_use;
+ unsigned int desc_size;
+@@ -1336,7 +1338,7 @@ static void stmmac_display_tx_rings(stru
+
+ /* Display TX rings */
+ for (queue = 0; queue < tx_cnt; queue++) {
+- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
+
+ pr_info("\tTX Queue %d rings\n", queue);
+
+@@ -1351,18 +1353,19 @@ static void stmmac_display_tx_rings(stru
+ desc_size = sizeof(struct dma_desc);
+ }
+
+- stmmac_display_ring(priv, head_tx, priv->dma_conf.dma_tx_size, false,
++ stmmac_display_ring(priv, head_tx, dma_conf->dma_tx_size, false,
+ tx_q->dma_tx_phy, desc_size);
+ }
+ }
+
+-static void stmmac_display_rings(struct stmmac_priv *priv)
++static void stmmac_display_rings(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ /* Display RX ring */
+- stmmac_display_rx_rings(priv);
++ stmmac_display_rx_rings(priv, dma_conf);
+
+ /* Display TX ring */
+- stmmac_display_tx_rings(priv);
++ stmmac_display_tx_rings(priv, dma_conf);
+ }
+
+ static int stmmac_set_bfsize(int mtu, int bufsize)
+@@ -1386,44 +1389,50 @@ static int stmmac_set_bfsize(int mtu, in
+ /**
+ * stmmac_clear_rx_descriptors - clear RX descriptors
+ * @priv: driver private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: RX queue index
+ * Description: this function is called to clear the RX descriptors
+ * in case of both basic and extended descriptors are used.
+ */
+-static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue)
++static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ int i;
+
+ /* Clear the RX descriptors */
+- for (i = 0; i < priv->dma_conf.dma_rx_size; i++)
++ for (i = 0; i < dma_conf->dma_rx_size; i++)
+ if (priv->extend_desc)
+ stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic,
+ priv->use_riwt, priv->mode,
+- (i == priv->dma_conf.dma_rx_size - 1),
+- priv->dma_conf.dma_buf_sz);
++ (i == dma_conf->dma_rx_size - 1),
++ dma_conf->dma_buf_sz);
+ else
+ stmmac_init_rx_desc(priv, &rx_q->dma_rx[i],
+ priv->use_riwt, priv->mode,
+- (i == priv->dma_conf.dma_rx_size - 1),
+- priv->dma_conf.dma_buf_sz);
++ (i == dma_conf->dma_rx_size - 1),
++ dma_conf->dma_buf_sz);
+ }
+
+ /**
+ * stmmac_clear_tx_descriptors - clear tx descriptors
+ * @priv: driver private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: TX queue index.
+ * Description: this function is called to clear the TX descriptors
+ * in case of both basic and extended descriptors are used.
+ */
+-static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue)
++static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
+ int i;
+
+ /* Clear the TX descriptors */
+- for (i = 0; i < priv->dma_conf.dma_tx_size; i++) {
+- int last = (i == (priv->dma_conf.dma_tx_size - 1));
++ for (i = 0; i < dma_conf->dma_tx_size; i++) {
++ int last = (i == (dma_conf->dma_tx_size - 1));
+ struct dma_desc *p;
+
+ if (priv->extend_desc)
+@@ -1440,10 +1449,12 @@ static void stmmac_clear_tx_descriptors(
+ /**
+ * stmmac_clear_descriptors - clear descriptors
+ * @priv: driver private structure
++ * @dma_conf: structure to take the dma data
+ * Description: this function is called to clear the TX and RX descriptors
+ * in case of both basic and extended descriptors are used.
+ */
+-static void stmmac_clear_descriptors(struct stmmac_priv *priv)
++static void stmmac_clear_descriptors(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ u32 rx_queue_cnt = priv->plat->rx_queues_to_use;
+ u32 tx_queue_cnt = priv->plat->tx_queues_to_use;
+@@ -1451,16 +1462,17 @@ static void stmmac_clear_descriptors(str
+
+ /* Clear the RX descriptors */
+ for (queue = 0; queue < rx_queue_cnt; queue++)
+- stmmac_clear_rx_descriptors(priv, queue);
++ stmmac_clear_rx_descriptors(priv, dma_conf, queue);
+
+ /* Clear the TX descriptors */
+ for (queue = 0; queue < tx_queue_cnt; queue++)
+- stmmac_clear_tx_descriptors(priv, queue);
++ stmmac_clear_tx_descriptors(priv, dma_conf, queue);
+ }
+
+ /**
+ * stmmac_init_rx_buffers - init the RX descriptor buffer.
+ * @priv: driver private structure
++ * @dma_conf: structure to take the dma data
+ * @p: descriptor pointer
+ * @i: descriptor index
+ * @flags: gfp flag
+@@ -1468,10 +1480,12 @@ static void stmmac_clear_descriptors(str
+ * Description: this function is called to allocate a receive buffer, perform
+ * the DMA mapping and init the descriptor.
+ */
+-static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
++static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ struct dma_desc *p,
+ int i, gfp_t flags, u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+
+ if (!buf->page) {
+@@ -1496,7 +1510,7 @@ static int stmmac_init_rx_buffers(struct
+ buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
+
+ stmmac_set_desc_addr(priv, p, buf->addr);
+- if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB)
++ if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
+ stmmac_init_desc3(priv, p);
+
+ return 0;
+@@ -1505,12 +1519,13 @@ static int stmmac_init_rx_buffers(struct
+ /**
+ * stmmac_free_rx_buffer - free RX dma buffers
+ * @priv: private structure
+- * @queue: RX queue index
++ * @rx_q: RX queue
+ * @i: buffer index.
+ */
+-static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i)
++static void stmmac_free_rx_buffer(struct stmmac_priv *priv,
++ struct stmmac_rx_queue *rx_q,
++ int i)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+
+ if (buf->page)
+@@ -1525,12 +1540,15 @@ static void stmmac_free_rx_buffer(struct
+ /**
+ * stmmac_free_tx_buffer - free RX dma buffers
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: RX queue index
+ * @i: buffer index.
+ */
+-static void stmmac_free_tx_buffer(struct stmmac_priv *priv, u32 queue, int i)
++static void stmmac_free_tx_buffer(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue, int i)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
+
+ if (tx_q->tx_skbuff_dma[i].buf &&
+ tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) {
+@@ -1569,23 +1587,28 @@ static void stmmac_free_tx_buffer(struct
+ /**
+ * dma_free_rx_skbufs - free RX dma buffers
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: RX queue index
+ */
+-static void dma_free_rx_skbufs(struct stmmac_priv *priv, u32 queue)
++static void dma_free_rx_skbufs(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ int i;
+
+- for (i = 0; i < priv->dma_conf.dma_rx_size; i++)
+- stmmac_free_rx_buffer(priv, queue, i);
++ for (i = 0; i < dma_conf->dma_rx_size; i++)
++ stmmac_free_rx_buffer(priv, rx_q, i);
+ }
+
+-static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv, u32 queue,
+- gfp_t flags)
++static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue, gfp_t flags)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ int i;
+
+- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) {
++ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ struct dma_desc *p;
+ int ret;
+
+@@ -1594,7 +1617,7 @@ static int stmmac_alloc_rx_buffers(struc
+ else
+ p = rx_q->dma_rx + i;
+
+- ret = stmmac_init_rx_buffers(priv, p, i, flags,
++ ret = stmmac_init_rx_buffers(priv, dma_conf, p, i, flags,
+ queue);
+ if (ret)
+ return ret;
+@@ -1608,14 +1631,17 @@ static int stmmac_alloc_rx_buffers(struc
+ /**
+ * dma_free_rx_xskbufs - free RX dma buffers from XSK pool
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: RX queue index
+ */
+-static void dma_free_rx_xskbufs(struct stmmac_priv *priv, u32 queue)
++static void dma_free_rx_xskbufs(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ int i;
+
+- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) {
++ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+
+ if (!buf->xdp)
+@@ -1626,12 +1652,14 @@ static void dma_free_rx_xskbufs(struct s
+ }
+ }
+
+-static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv, u32 queue)
++static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ int i;
+
+- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) {
++ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ struct stmmac_rx_buffer *buf;
+ dma_addr_t dma_addr;
+ struct dma_desc *p;
+@@ -1666,22 +1694,25 @@ static struct xsk_buff_pool *stmmac_get_
+ /**
+ * __init_dma_rx_desc_rings - init the RX descriptor ring (per queue)
+ * @priv: driver private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: RX queue index
+ * @flags: gfp flag.
+ * Description: this function initializes the DMA RX descriptors
+ * and allocates the socket buffers. It supports the chained and ring
+ * modes.
+ */
+-static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, u32 queue, gfp_t flags)
++static int __init_dma_rx_desc_rings(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue, gfp_t flags)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ int ret;
+
+ netif_dbg(priv, probe, priv->dev,
+ "(%s) dma_rx_phy=0x%08x\n", __func__,
+ (u32)rx_q->dma_rx_phy);
+
+- stmmac_clear_rx_descriptors(priv, queue);
++ stmmac_clear_rx_descriptors(priv, dma_conf, queue);
+
+ xdp_rxq_info_unreg_mem_model(&rx_q->xdp_rxq);
+
+@@ -1708,9 +1739,9 @@ static int __init_dma_rx_desc_rings(stru
+ /* RX XDP ZC buffer pool may not be populated, e.g.
+ * xdpsock TX-only.
+ */
+- stmmac_alloc_rx_buffers_zc(priv, queue);
++ stmmac_alloc_rx_buffers_zc(priv, dma_conf, queue);
+ } else {
+- ret = stmmac_alloc_rx_buffers(priv, queue, flags);
++ ret = stmmac_alloc_rx_buffers(priv, dma_conf, queue, flags);
+ if (ret < 0)
+ return -ENOMEM;
+ }
+@@ -1720,17 +1751,19 @@ static int __init_dma_rx_desc_rings(stru
+ if (priv->extend_desc)
+ stmmac_mode_init(priv, rx_q->dma_erx,
+ rx_q->dma_rx_phy,
+- priv->dma_conf.dma_rx_size, 1);
++ dma_conf->dma_rx_size, 1);
+ else
+ stmmac_mode_init(priv, rx_q->dma_rx,
+ rx_q->dma_rx_phy,
+- priv->dma_conf.dma_rx_size, 0);
++ dma_conf->dma_rx_size, 0);
+ }
+
+ return 0;
+ }
+
+-static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags)
++static int init_dma_rx_desc_rings(struct net_device *dev,
++ struct stmmac_dma_conf *dma_conf,
++ gfp_t flags)
+ {
+ struct stmmac_priv *priv = netdev_priv(dev);
+ u32 rx_count = priv->plat->rx_queues_to_use;
+@@ -1742,7 +1775,7 @@ static int init_dma_rx_desc_rings(struct
+ "SKB addresses:\nskb\t\tskb data\tdma data\n");
+
+ for (queue = 0; queue < rx_count; queue++) {
+- ret = __init_dma_rx_desc_rings(priv, queue, flags);
++ ret = __init_dma_rx_desc_rings(priv, dma_conf, queue, flags);
+ if (ret)
+ goto err_init_rx_buffers;
+ }
+@@ -1751,12 +1784,12 @@ static int init_dma_rx_desc_rings(struct
+
+ err_init_rx_buffers:
+ while (queue >= 0) {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+
+ if (rx_q->xsk_pool)
+- dma_free_rx_xskbufs(priv, queue);
++ dma_free_rx_xskbufs(priv, dma_conf, queue);
+ else
+- dma_free_rx_skbufs(priv, queue);
++ dma_free_rx_skbufs(priv, dma_conf, queue);
+
+ rx_q->buf_alloc_num = 0;
+ rx_q->xsk_pool = NULL;
+@@ -1773,14 +1806,17 @@ err_init_rx_buffers:
+ /**
+ * __init_dma_tx_desc_rings - init the TX descriptor ring (per queue)
+ * @priv: driver private structure
+- * @queue : TX queue index
++ * @dma_conf: structure to take the dma data
++ * @queue: TX queue index
+ * Description: this function initializes the DMA TX descriptors
+ * and allocates the socket buffers. It supports the chained and ring
+ * modes.
+ */
+-static int __init_dma_tx_desc_rings(struct stmmac_priv *priv, u32 queue)
++static int __init_dma_tx_desc_rings(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
+ int i;
+
+ netif_dbg(priv, probe, priv->dev,
+@@ -1792,16 +1828,16 @@ static int __init_dma_tx_desc_rings(stru
+ if (priv->extend_desc)
+ stmmac_mode_init(priv, tx_q->dma_etx,
+ tx_q->dma_tx_phy,
+- priv->dma_conf.dma_tx_size, 1);
++ dma_conf->dma_tx_size, 1);
+ else if (!(tx_q->tbs & STMMAC_TBS_AVAIL))
+ stmmac_mode_init(priv, tx_q->dma_tx,
+ tx_q->dma_tx_phy,
+- priv->dma_conf.dma_tx_size, 0);
++ dma_conf->dma_tx_size, 0);
+ }
+
+ tx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue);
+
+- for (i = 0; i < priv->dma_conf.dma_tx_size; i++) {
++ for (i = 0; i < dma_conf->dma_tx_size; i++) {
+ struct dma_desc *p;
+
+ if (priv->extend_desc)
+@@ -1823,7 +1859,8 @@ static int __init_dma_tx_desc_rings(stru
+ return 0;
+ }
+
+-static int init_dma_tx_desc_rings(struct net_device *dev)
++static int init_dma_tx_desc_rings(struct net_device *dev,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ struct stmmac_priv *priv = netdev_priv(dev);
+ u32 tx_queue_cnt;
+@@ -1832,7 +1869,7 @@ static int init_dma_tx_desc_rings(struct
+ tx_queue_cnt = priv->plat->tx_queues_to_use;
+
+ for (queue = 0; queue < tx_queue_cnt; queue++)
+- __init_dma_tx_desc_rings(priv, queue);
++ __init_dma_tx_desc_rings(priv, dma_conf, queue);
+
+ return 0;
+ }
+@@ -1840,26 +1877,29 @@ static int init_dma_tx_desc_rings(struct
+ /**
+ * init_dma_desc_rings - init the RX/TX descriptor rings
+ * @dev: net device structure
++ * @dma_conf: structure to take the dma data
+ * @flags: gfp flag.
+ * Description: this function initializes the DMA RX/TX descriptors
+ * and allocates the socket buffers. It supports the chained and ring
+ * modes.
+ */
+-static int init_dma_desc_rings(struct net_device *dev, gfp_t flags)
++static int init_dma_desc_rings(struct net_device *dev,
++ struct stmmac_dma_conf *dma_conf,
++ gfp_t flags)
+ {
+ struct stmmac_priv *priv = netdev_priv(dev);
+ int ret;
+
+- ret = init_dma_rx_desc_rings(dev, flags);
++ ret = init_dma_rx_desc_rings(dev, dma_conf, flags);
+ if (ret)
+ return ret;
+
+- ret = init_dma_tx_desc_rings(dev);
++ ret = init_dma_tx_desc_rings(dev, dma_conf);
+
+- stmmac_clear_descriptors(priv);
++ stmmac_clear_descriptors(priv, dma_conf);
+
+ if (netif_msg_hw(priv))
+- stmmac_display_rings(priv);
++ stmmac_display_rings(priv, dma_conf);
+
+ return ret;
+ }
+@@ -1867,17 +1907,20 @@ static int init_dma_desc_rings(struct ne
+ /**
+ * dma_free_tx_skbufs - free TX dma buffers
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: TX queue index
+ */
+-static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue)
++static void dma_free_tx_skbufs(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
+ int i;
+
+ tx_q->xsk_frames_done = 0;
+
+- for (i = 0; i < priv->dma_conf.dma_tx_size; i++)
+- stmmac_free_tx_buffer(priv, queue, i);
++ for (i = 0; i < dma_conf->dma_tx_size; i++)
++ stmmac_free_tx_buffer(priv, dma_conf, queue, i);
+
+ if (tx_q->xsk_pool && tx_q->xsk_frames_done) {
+ xsk_tx_completed(tx_q->xsk_pool, tx_q->xsk_frames_done);
+@@ -1896,34 +1939,37 @@ static void stmmac_free_tx_skbufs(struct
+ u32 queue;
+
+ for (queue = 0; queue < tx_queue_cnt; queue++)
+- dma_free_tx_skbufs(priv, queue);
++ dma_free_tx_skbufs(priv, &priv->dma_conf, queue);
+ }
+
+ /**
+ * __free_dma_rx_desc_resources - free RX dma desc resources (per queue)
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: RX queue index
+ */
+-static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue)
++static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+
+ /* Release the DMA RX socket buffers */
+ if (rx_q->xsk_pool)
+- dma_free_rx_xskbufs(priv, queue);
++ dma_free_rx_xskbufs(priv, dma_conf, queue);
+ else
+- dma_free_rx_skbufs(priv, queue);
++ dma_free_rx_skbufs(priv, dma_conf, queue);
+
+ rx_q->buf_alloc_num = 0;
+ rx_q->xsk_pool = NULL;
+
+ /* Free DMA regions of consistent memory previously allocated */
+ if (!priv->extend_desc)
+- dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size *
++ dma_free_coherent(priv->device, dma_conf->dma_rx_size *
+ sizeof(struct dma_desc),
+ rx_q->dma_rx, rx_q->dma_rx_phy);
+ else
+- dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size *
++ dma_free_coherent(priv->device, dma_conf->dma_rx_size *
+ sizeof(struct dma_extended_desc),
+ rx_q->dma_erx, rx_q->dma_rx_phy);
+
+@@ -1935,29 +1981,33 @@ static void __free_dma_rx_desc_resources
+ page_pool_destroy(rx_q->page_pool);
+ }
+
+-static void free_dma_rx_desc_resources(struct stmmac_priv *priv)
++static void free_dma_rx_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ u32 rx_count = priv->plat->rx_queues_to_use;
+ u32 queue;
+
+ /* Free RX queue resources */
+ for (queue = 0; queue < rx_count; queue++)
+- __free_dma_rx_desc_resources(priv, queue);
++ __free_dma_rx_desc_resources(priv, dma_conf, queue);
+ }
+
+ /**
+ * __free_dma_tx_desc_resources - free TX dma desc resources (per queue)
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: TX queue index
+ */
+-static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue)
++static void __free_dma_tx_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
+ size_t size;
+ void *addr;
+
+ /* Release the DMA TX socket buffers */
+- dma_free_tx_skbufs(priv, queue);
++ dma_free_tx_skbufs(priv, dma_conf, queue);
+
+ if (priv->extend_desc) {
+ size = sizeof(struct dma_extended_desc);
+@@ -1970,7 +2020,7 @@ static void __free_dma_tx_desc_resources
+ addr = tx_q->dma_tx;
+ }
+
+- size *= priv->dma_conf.dma_tx_size;
++ size *= dma_conf->dma_tx_size;
+
+ dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy);
+
+@@ -1978,28 +2028,32 @@ static void __free_dma_tx_desc_resources
+ kfree(tx_q->tx_skbuff);
+ }
+
+-static void free_dma_tx_desc_resources(struct stmmac_priv *priv)
++static void free_dma_tx_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ u32 tx_count = priv->plat->tx_queues_to_use;
+ u32 queue;
+
+ /* Free TX queue resources */
+ for (queue = 0; queue < tx_count; queue++)
+- __free_dma_tx_desc_resources(priv, queue);
++ __free_dma_tx_desc_resources(priv, dma_conf, queue);
+ }
+
+ /**
+ * __alloc_dma_rx_desc_resources - alloc RX resources (per queue).
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: RX queue index
+ * Description: according to which descriptor can be used (extend or basic)
+ * this function allocates the resources for TX and RX paths. In case of
+ * reception, for example, it pre-allocated the RX socket buffer in order to
+ * allow zero-copy mechanism.
+ */
+-static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue)
++static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ struct stmmac_channel *ch = &priv->channel[queue];
+ bool xdp_prog = stmmac_xdp_is_enabled(priv);
+ struct page_pool_params pp_params = { 0 };
+@@ -2011,8 +2065,8 @@ static int __alloc_dma_rx_desc_resources
+ rx_q->priv_data = priv;
+
+ pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
+- pp_params.pool_size = priv->dma_conf.dma_rx_size;
+- num_pages = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE);
++ pp_params.pool_size = dma_conf->dma_rx_size;
++ num_pages = DIV_ROUND_UP(dma_conf->dma_buf_sz, PAGE_SIZE);
+ pp_params.order = ilog2(num_pages);
+ pp_params.nid = dev_to_node(priv->device);
+ pp_params.dev = priv->device;
+@@ -2027,7 +2081,7 @@ static int __alloc_dma_rx_desc_resources
+ return ret;
+ }
+
+- rx_q->buf_pool = kcalloc(priv->dma_conf.dma_rx_size,
++ rx_q->buf_pool = kcalloc(dma_conf->dma_rx_size,
+ sizeof(*rx_q->buf_pool),
+ GFP_KERNEL);
+ if (!rx_q->buf_pool)
+@@ -2035,7 +2089,7 @@ static int __alloc_dma_rx_desc_resources
+
+ if (priv->extend_desc) {
+ rx_q->dma_erx = dma_alloc_coherent(priv->device,
+- priv->dma_conf.dma_rx_size *
++ dma_conf->dma_rx_size *
+ sizeof(struct dma_extended_desc),
+ &rx_q->dma_rx_phy,
+ GFP_KERNEL);
+@@ -2044,7 +2098,7 @@ static int __alloc_dma_rx_desc_resources
+
+ } else {
+ rx_q->dma_rx = dma_alloc_coherent(priv->device,
+- priv->dma_conf.dma_rx_size *
++ dma_conf->dma_rx_size *
+ sizeof(struct dma_desc),
+ &rx_q->dma_rx_phy,
+ GFP_KERNEL);
+@@ -2069,7 +2123,8 @@ static int __alloc_dma_rx_desc_resources
+ return 0;
+ }
+
+-static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv)
++static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ u32 rx_count = priv->plat->rx_queues_to_use;
+ u32 queue;
+@@ -2077,7 +2132,7 @@ static int alloc_dma_rx_desc_resources(s
+
+ /* RX queues buffers and DMA */
+ for (queue = 0; queue < rx_count; queue++) {
+- ret = __alloc_dma_rx_desc_resources(priv, queue);
++ ret = __alloc_dma_rx_desc_resources(priv, dma_conf, queue);
+ if (ret)
+ goto err_dma;
+ }
+@@ -2085,7 +2140,7 @@ static int alloc_dma_rx_desc_resources(s
+ return 0;
+
+ err_dma:
+- free_dma_rx_desc_resources(priv);
++ free_dma_rx_desc_resources(priv, dma_conf);
+
+ return ret;
+ }
+@@ -2093,28 +2148,31 @@ err_dma:
+ /**
+ * __alloc_dma_tx_desc_resources - alloc TX resources (per queue).
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ * @queue: TX queue index
+ * Description: according to which descriptor can be used (extend or basic)
+ * this function allocates the resources for TX and RX paths. In case of
+ * reception, for example, it pre-allocated the RX socket buffer in order to
+ * allow zero-copy mechanism.
+ */
+-static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue)
++static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf,
++ u32 queue)
+ {
+- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
+ size_t size;
+ void *addr;
+
+ tx_q->queue_index = queue;
+ tx_q->priv_data = priv;
+
+- tx_q->tx_skbuff_dma = kcalloc(priv->dma_conf.dma_tx_size,
++ tx_q->tx_skbuff_dma = kcalloc(dma_conf->dma_tx_size,
+ sizeof(*tx_q->tx_skbuff_dma),
+ GFP_KERNEL);
+ if (!tx_q->tx_skbuff_dma)
+ return -ENOMEM;
+
+- tx_q->tx_skbuff = kcalloc(priv->dma_conf.dma_tx_size,
++ tx_q->tx_skbuff = kcalloc(dma_conf->dma_tx_size,
+ sizeof(struct sk_buff *),
+ GFP_KERNEL);
+ if (!tx_q->tx_skbuff)
+@@ -2127,7 +2185,7 @@ static int __alloc_dma_tx_desc_resources
+ else
+ size = sizeof(struct dma_desc);
+
+- size *= priv->dma_conf.dma_tx_size;
++ size *= dma_conf->dma_tx_size;
+
+ addr = dma_alloc_coherent(priv->device, size,
+ &tx_q->dma_tx_phy, GFP_KERNEL);
+@@ -2144,7 +2202,8 @@ static int __alloc_dma_tx_desc_resources
+ return 0;
+ }
+
+-static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv)
++static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ u32 tx_count = priv->plat->tx_queues_to_use;
+ u32 queue;
+@@ -2152,7 +2211,7 @@ static int alloc_dma_tx_desc_resources(s
+
+ /* TX queues buffers and DMA */
+ for (queue = 0; queue < tx_count; queue++) {
+- ret = __alloc_dma_tx_desc_resources(priv, queue);
++ ret = __alloc_dma_tx_desc_resources(priv, dma_conf, queue);
+ if (ret)
+ goto err_dma;
+ }
+@@ -2160,27 +2219,29 @@ static int alloc_dma_tx_desc_resources(s
+ return 0;
+
+ err_dma:
+- free_dma_tx_desc_resources(priv);
++ free_dma_tx_desc_resources(priv, dma_conf);
+ return ret;
+ }
+
+ /**
+ * alloc_dma_desc_resources - alloc TX/RX resources.
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ * Description: according to which descriptor can be used (extend or basic)
+ * this function allocates the resources for TX and RX paths. In case of
+ * reception, for example, it pre-allocated the RX socket buffer in order to
+ * allow zero-copy mechanism.
+ */
+-static int alloc_dma_desc_resources(struct stmmac_priv *priv)
++static int alloc_dma_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ /* RX Allocation */
+- int ret = alloc_dma_rx_desc_resources(priv);
++ int ret = alloc_dma_rx_desc_resources(priv, dma_conf);
+
+ if (ret)
+ return ret;
+
+- ret = alloc_dma_tx_desc_resources(priv);
++ ret = alloc_dma_tx_desc_resources(priv, dma_conf);
+
+ return ret;
+ }
+@@ -2188,16 +2249,18 @@ static int alloc_dma_desc_resources(stru
+ /**
+ * free_dma_desc_resources - free dma desc resources
+ * @priv: private structure
++ * @dma_conf: structure to take the dma data
+ */
+-static void free_dma_desc_resources(struct stmmac_priv *priv)
++static void free_dma_desc_resources(struct stmmac_priv *priv,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ /* Release the DMA TX socket buffers */
+- free_dma_tx_desc_resources(priv);
++ free_dma_tx_desc_resources(priv, dma_conf);
+
+ /* Release the DMA RX socket buffers later
+ * to ensure all pending XDP_TX buffers are returned.
+ */
+- free_dma_rx_desc_resources(priv);
++ free_dma_rx_desc_resources(priv, dma_conf);
+ }
+
+ /**
+@@ -2686,8 +2749,8 @@ static void stmmac_tx_err(struct stmmac_
+ netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan));
+
+ stmmac_stop_tx_dma(priv, chan);
+- dma_free_tx_skbufs(priv, chan);
+- stmmac_clear_tx_descriptors(priv, chan);
++ dma_free_tx_skbufs(priv, &priv->dma_conf, chan);
++ stmmac_clear_tx_descriptors(priv, &priv->dma_conf, chan);
+ stmmac_reset_tx_queue(priv, chan);
+ stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ tx_q->dma_tx_phy, chan);
+@@ -3684,19 +3747,93 @@ static int stmmac_request_irq(struct net
+ }
+
+ /**
+- * stmmac_open - open entry point of the driver
++ * stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue
++ * @priv: driver private structure
++ * @mtu: MTU to setup the dma queue and buf with
++ * Description: Allocate and generate a dma_conf based on the provided MTU.
++ * Allocate the Tx/Rx DMA queue and init them.
++ * Return value:
++ * the dma_conf allocated struct on success and an appropriate ERR_PTR on failure.
++ */
++static struct stmmac_dma_conf *
++stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
++{
++ struct stmmac_dma_conf *dma_conf;
++ int chan, bfsize, ret;
++
++ dma_conf = kzalloc(sizeof(*dma_conf), GFP_KERNEL);
++ if (!dma_conf) {
++ netdev_err(priv->dev, "%s: DMA conf allocation failed\n",
++ __func__);
++ return ERR_PTR(-ENOMEM);
++ }
++
++ bfsize = stmmac_set_16kib_bfsize(priv, mtu);
++ if (bfsize < 0)
++ bfsize = 0;
++
++ if (bfsize < BUF_SIZE_16KiB)
++ bfsize = stmmac_set_bfsize(mtu, 0);
++
++ dma_conf->dma_buf_sz = bfsize;
++ /* Chose the tx/rx size from the already defined one in the
++ * priv struct. (if defined)
++ */
++ dma_conf->dma_tx_size = priv->dma_conf.dma_tx_size;
++ dma_conf->dma_rx_size = priv->dma_conf.dma_rx_size;
++
++ if (!dma_conf->dma_tx_size)
++ dma_conf->dma_tx_size = DMA_DEFAULT_TX_SIZE;
++ if (!dma_conf->dma_rx_size)
++ dma_conf->dma_rx_size = DMA_DEFAULT_RX_SIZE;
++
++ /* Earlier check for TBS */
++ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) {
++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[chan];
++ int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en;
++
++ /* Setup per-TXQ tbs flag before TX descriptor alloc */
++ tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0;
++ }
++
++ ret = alloc_dma_desc_resources(priv, dma_conf);
++ if (ret < 0) {
++ netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n",
++ __func__);
++ goto alloc_error;
++ }
++
++ ret = init_dma_desc_rings(priv->dev, dma_conf, GFP_KERNEL);
++ if (ret < 0) {
++ netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n",
++ __func__);
++ goto init_error;
++ }
++
++ return dma_conf;
++
++init_error:
++ free_dma_desc_resources(priv, dma_conf);
++alloc_error:
++ kfree(dma_conf);
++ return ERR_PTR(ret);
++}
++
++/**
++ * __stmmac_open - open entry point of the driver
+ * @dev : pointer to the device structure.
++ * @dma_conf : structure to take the dma data
+ * Description:
+ * This function is the open entry point of the driver.
+ * Return value:
+ * 0 on success and an appropriate (-)ve integer as defined in errno.h
+ * file on failure.
+ */
+-static int stmmac_open(struct net_device *dev)
++static int __stmmac_open(struct net_device *dev,
++ struct stmmac_dma_conf *dma_conf)
+ {
+ struct stmmac_priv *priv = netdev_priv(dev);
+ int mode = priv->plat->phy_interface;
+- int bfsize = 0;
+ u32 chan;
+ int ret;
+
+@@ -3723,45 +3860,10 @@ static int stmmac_open(struct net_device
+ memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats));
+ priv->xstats.threshold = tc;
+
+- bfsize = stmmac_set_16kib_bfsize(priv, dev->mtu);
+- if (bfsize < 0)
+- bfsize = 0;
+-
+- if (bfsize < BUF_SIZE_16KiB)
+- bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_conf.dma_buf_sz);
+-
+- priv->dma_conf.dma_buf_sz = bfsize;
+- buf_sz = bfsize;
+-
+ priv->rx_copybreak = STMMAC_RX_COPYBREAK;
+
+- if (!priv->dma_conf.dma_tx_size)
+- priv->dma_conf.dma_tx_size = DMA_DEFAULT_TX_SIZE;
+- if (!priv->dma_conf.dma_rx_size)
+- priv->dma_conf.dma_rx_size = DMA_DEFAULT_RX_SIZE;
+-
+- /* Earlier check for TBS */
+- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) {
+- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
+- int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en;
+-
+- /* Setup per-TXQ tbs flag before TX descriptor alloc */
+- tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0;
+- }
+-
+- ret = alloc_dma_desc_resources(priv);
+- if (ret < 0) {
+- netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n",
+- __func__);
+- goto dma_desc_error;
+- }
+-
+- ret = init_dma_desc_rings(dev, GFP_KERNEL);
+- if (ret < 0) {
+- netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n",
+- __func__);
+- goto init_error;
+- }
++ buf_sz = dma_conf->dma_buf_sz;
++ memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf));
+
+ if (priv->plat->serdes_powerup) {
+ ret = priv->plat->serdes_powerup(dev, priv->plat->bsp_priv);
+@@ -3804,14 +3906,28 @@ irq_error:
+
+ stmmac_hw_teardown(dev);
+ init_error:
+- free_dma_desc_resources(priv);
+-dma_desc_error:
++ free_dma_desc_resources(priv, &priv->dma_conf);
+ phylink_disconnect_phy(priv->phylink);
+ init_phy_error:
+ pm_runtime_put(priv->device);
+ return ret;
+ }
+
++static int stmmac_open(struct net_device *dev)
++{
++ struct stmmac_priv *priv = netdev_priv(dev);
++ struct stmmac_dma_conf *dma_conf;
++ int ret;
++
++ dma_conf = stmmac_setup_dma_desc(priv, dev->mtu);
++ if (IS_ERR(dma_conf))
++ return PTR_ERR(dma_conf);
++
++ ret = __stmmac_open(dev, dma_conf);
++ kfree(dma_conf);
++ return ret;
++}
++
+ static void stmmac_fpe_stop_wq(struct stmmac_priv *priv)
+ {
+ set_bit(__FPE_REMOVING, &priv->fpe_task_state);
+@@ -3858,7 +3974,7 @@ static int stmmac_release(struct net_dev
+ stmmac_stop_all_dma(priv);
+
+ /* Release and free the Rx/Tx resources */
+- free_dma_desc_resources(priv);
++ free_dma_desc_resources(priv, &priv->dma_conf);
+
+ /* Disable the MAC Rx/Tx */
+ stmmac_mac_set(priv, priv->ioaddr, false);
+@@ -6403,7 +6519,7 @@ void stmmac_disable_rx_queue(struct stmm
+ spin_unlock_irqrestore(&ch->lock, flags);
+
+ stmmac_stop_rx_dma(priv, queue);
+- __free_dma_rx_desc_resources(priv, queue);
++ __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue);
+ }
+
+ void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue)
+@@ -6414,21 +6530,21 @@ void stmmac_enable_rx_queue(struct stmma
+ u32 buf_size;
+ int ret;
+
+- ret = __alloc_dma_rx_desc_resources(priv, queue);
++ ret = __alloc_dma_rx_desc_resources(priv, &priv->dma_conf, queue);
+ if (ret) {
+ netdev_err(priv->dev, "Failed to alloc RX desc.\n");
+ return;
+ }
+
+- ret = __init_dma_rx_desc_rings(priv, queue, GFP_KERNEL);
++ ret = __init_dma_rx_desc_rings(priv, &priv->dma_conf, queue, GFP_KERNEL);
+ if (ret) {
+- __free_dma_rx_desc_resources(priv, queue);
++ __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue);
+ netdev_err(priv->dev, "Failed to init RX desc.\n");
+ return;
+ }
+
+ stmmac_reset_rx_queue(priv, queue);
+- stmmac_clear_rx_descriptors(priv, queue);
++ stmmac_clear_rx_descriptors(priv, &priv->dma_conf, queue);
+
+ stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ rx_q->dma_rx_phy, rx_q->queue_index);
+@@ -6466,7 +6582,7 @@ void stmmac_disable_tx_queue(struct stmm
+ spin_unlock_irqrestore(&ch->lock, flags);
+
+ stmmac_stop_tx_dma(priv, queue);
+- __free_dma_tx_desc_resources(priv, queue);
++ __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
+ }
+
+ void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue)
+@@ -6476,21 +6592,21 @@ void stmmac_enable_tx_queue(struct stmma
+ unsigned long flags;
+ int ret;
+
+- ret = __alloc_dma_tx_desc_resources(priv, queue);
++ ret = __alloc_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
+ if (ret) {
+ netdev_err(priv->dev, "Failed to alloc TX desc.\n");
+ return;
+ }
+
+- ret = __init_dma_tx_desc_rings(priv, queue);
++ ret = __init_dma_tx_desc_rings(priv, &priv->dma_conf, queue);
+ if (ret) {
+- __free_dma_tx_desc_resources(priv, queue);
++ __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue);
+ netdev_err(priv->dev, "Failed to init TX desc.\n");
+ return;
+ }
+
+ stmmac_reset_tx_queue(priv, queue);
+- stmmac_clear_tx_descriptors(priv, queue);
++ stmmac_clear_tx_descriptors(priv, &priv->dma_conf, queue);
+
+ stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg,
+ tx_q->dma_tx_phy, tx_q->queue_index);
+@@ -6530,7 +6646,7 @@ void stmmac_xdp_release(struct net_devic
+ stmmac_stop_all_dma(priv);
+
+ /* Release and free the Rx/Tx resources */
+- free_dma_desc_resources(priv);
++ free_dma_desc_resources(priv, &priv->dma_conf);
+
+ /* Disable the MAC Rx/Tx */
+ stmmac_mac_set(priv, priv->ioaddr, false);
+@@ -6555,14 +6671,14 @@ int stmmac_xdp_open(struct net_device *d
+ u32 chan;
+ int ret;
+
+- ret = alloc_dma_desc_resources(priv);
++ ret = alloc_dma_desc_resources(priv, &priv->dma_conf);
+ if (ret < 0) {
+ netdev_err(dev, "%s: DMA descriptors allocation failed\n",
+ __func__);
+ goto dma_desc_error;
+ }
+
+- ret = init_dma_desc_rings(dev, GFP_KERNEL);
++ ret = init_dma_desc_rings(dev, &priv->dma_conf, GFP_KERNEL);
+ if (ret < 0) {
+ netdev_err(dev, "%s: DMA descriptors initialization failed\n",
+ __func__);
+@@ -6644,7 +6760,7 @@ irq_error:
+
+ stmmac_hw_teardown(dev);
+ init_error:
+- free_dma_desc_resources(priv);
++ free_dma_desc_resources(priv, &priv->dma_conf);
+ dma_desc_error:
+ return ret;
+ }
+@@ -7509,7 +7625,7 @@ int stmmac_resume(struct device *dev)
+ stmmac_reset_queues_param(priv);
+
+ stmmac_free_tx_skbufs(priv);
+- stmmac_clear_descriptors(priv);
++ stmmac_clear_descriptors(priv, &priv->dma_conf);
+
+ stmmac_hw_setup(ndev, false);
+ stmmac_init_coalesce(priv);
diff --git a/target/linux/generic/backport-6.1/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch b/target/linux/generic/backport-6.1/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch
new file mode 100644
index 0000000000..8fccc71659
--- /dev/null
+++ b/target/linux/generic/backport-6.1/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch
@@ -0,0 +1,73 @@
+From 3470079687448abac42deb62774253be1d6bdef3 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sat, 23 Jul 2022 16:29:33 +0200
+Subject: [PATCH 5/5] net: ethernet: stmicro: stmmac: permit MTU change with
+ interface up
+
+Remove the limitation where the interface needs to be down to change
+MTU by releasing and opening the stmmac driver to set the new MTU.
+Also call the set_filter function to correctly init the port.
+This permits to remove the EBUSY error while the ethernet port is
+running permitting a correct MTU change if for example a DSA request
+a MTU change for a switch CPU port.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ .../net/ethernet/stmicro/stmmac/stmmac_main.c | 30 +++++++++++++++----
+ 1 file changed, 24 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -5626,18 +5626,15 @@ static int stmmac_change_mtu(struct net_
+ {
+ struct stmmac_priv *priv = netdev_priv(dev);
+ int txfifosz = priv->plat->tx_fifo_size;
++ struct stmmac_dma_conf *dma_conf;
+ const int mtu = new_mtu;
++ int ret;
+
+ if (txfifosz == 0)
+ txfifosz = priv->dma_cap.tx_fifo_size;
+
+ txfifosz /= priv->plat->tx_queues_to_use;
+
+- if (netif_running(dev)) {
+- netdev_err(priv->dev, "must be stopped to change its MTU\n");
+- return -EBUSY;
+- }
+-
+ if (stmmac_xdp_is_enabled(priv) && new_mtu > ETH_DATA_LEN) {
+ netdev_dbg(priv->dev, "Jumbo frames not supported for XDP\n");
+ return -EINVAL;
+@@ -5649,8 +5646,29 @@ static int stmmac_change_mtu(struct net_
+ if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB))
+ return -EINVAL;
+
+- dev->mtu = mtu;
++ if (netif_running(dev)) {
++ netdev_dbg(priv->dev, "restarting interface to change its MTU\n");
++ /* Try to allocate the new DMA conf with the new mtu */
++ dma_conf = stmmac_setup_dma_desc(priv, mtu);
++ if (IS_ERR(dma_conf)) {
++ netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n",
++ mtu);
++ return PTR_ERR(dma_conf);
++ }
++
++ stmmac_release(dev);
++
++ ret = __stmmac_open(dev, dma_conf);
++ kfree(dma_conf);
++ if (ret) {
++ netdev_err(priv->dev, "failed reopening the interface after MTU change\n");
++ return ret;
++ }
++
++ stmmac_set_rx_mode(dev);
++ }
+
++ dev->mtu = mtu;
+ netdev_update_features(dev);
+
+ return 0;
diff --git a/target/linux/generic/backport-6.1/776-v6.1-01-net-dsa-qca8k-fix-inband-mgmt-for-big-endian-systems.patch b/target/linux/generic/backport-6.1/776-v6.1-01-net-dsa-qca8k-fix-inband-mgmt-for-big-endian-systems.patch
new file mode 100644
index 0000000000..4f0d95f456
--- /dev/null
+++ b/target/linux/generic/backport-6.1/776-v6.1-01-net-dsa-qca8k-fix-inband-mgmt-for-big-endian-systems.patch
@@ -0,0 +1,156 @@
+From a2550d3ce53c68f54042bc5e468c4d07491ffe0e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 12 Oct 2022 19:18:36 +0200
+Subject: [PATCH 1/2] net: dsa: qca8k: fix inband mgmt for big-endian systems
+
+The header and the data of the skb for the inband mgmt requires
+to be in little-endian. This is problematic for big-endian system
+as the mgmt header is written in the cpu byte order.
+
+Fix this by converting each value for the mgmt header and data to
+little-endian, and convert to cpu byte order the mgmt header and
+data sent by the switch.
+
+Fixes: 5950c7c0a68c ("net: dsa: qca8k: add support for mgmt read/write in Ethernet packet")
+Tested-by: Pawel Dembicki <paweldembicki@gmail.com>
+Tested-by: Lech Perczak <lech.perczak@gmail.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Lech Perczak <lech.perczak@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 63 ++++++++++++++++++++++++--------
+ include/linux/dsa/tag_qca.h | 6 +--
+ 2 files changed, 50 insertions(+), 19 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -137,27 +137,42 @@ static void qca8k_rw_reg_ack_handler(str
+ struct qca8k_mgmt_eth_data *mgmt_eth_data;
+ struct qca8k_priv *priv = ds->priv;
+ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++ u32 command;
+ u8 len, cmd;
++ int i;
+
+ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
+ mgmt_eth_data = &priv->mgmt_eth_data;
+
+- cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
+- len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
++ command = get_unaligned_le32(&mgmt_ethhdr->command);
++ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command);
++ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command);
+
+ /* Make sure the seq match the requested packet */
+- if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
++ if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq)
+ mgmt_eth_data->ack = true;
+
+ if (cmd == MDIO_READ) {
+- mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
++ u32 *val = mgmt_eth_data->data;
++
++ *val = get_unaligned_le32(&mgmt_ethhdr->mdio_data);
+
+ /* Get the rest of the 12 byte of data.
+ * The read/write function will extract the requested data.
+ */
+- if (len > QCA_HDR_MGMT_DATA1_LEN)
+- memcpy(mgmt_eth_data->data + 1, skb->data,
+- QCA_HDR_MGMT_DATA2_LEN);
++ if (len > QCA_HDR_MGMT_DATA1_LEN) {
++ __le32 *data2 = (__le32 *)skb->data;
++ int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
++ len - QCA_HDR_MGMT_DATA1_LEN);
++
++ val++;
++
++ for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
++ *val = get_unaligned_le32(data2);
++ val++;
++ data2++;
++ }
++ }
+ }
+
+ complete(&mgmt_eth_data->rw_done);
+@@ -169,8 +184,10 @@ static struct sk_buff *qca8k_alloc_mdio_
+ struct qca_mgmt_ethhdr *mgmt_ethhdr;
+ unsigned int real_len;
+ struct sk_buff *skb;
+- u32 *data2;
++ __le32 *data2;
++ u32 command;
+ u16 hdr;
++ int i;
+
+ skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
+ if (!skb)
+@@ -199,20 +216,32 @@ static struct sk_buff *qca8k_alloc_mdio_
+ hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
+ hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
+
+- mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
+- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
++ command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
++ command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
++ command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
++ command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
+ QCA_HDR_MGMT_CHECK_CODE_VAL);
+
++ put_unaligned_le32(command, &mgmt_ethhdr->command);
++
+ if (cmd == MDIO_WRITE)
+- mgmt_ethhdr->mdio_data = *val;
++ put_unaligned_le32(*val, &mgmt_ethhdr->mdio_data);
+
+ mgmt_ethhdr->hdr = htons(hdr);
+
+ data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
+- if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN)
+- memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
++ if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) {
++ int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
++ len - QCA_HDR_MGMT_DATA1_LEN);
++
++ val++;
++
++ for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
++ put_unaligned_le32(*val, data2);
++ data2++;
++ val++;
++ }
++ }
+
+ return skb;
+ }
+@@ -220,9 +249,11 @@ static struct sk_buff *qca8k_alloc_mdio_
+ static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
+ {
+ struct qca_mgmt_ethhdr *mgmt_ethhdr;
++ u32 seq;
+
++ seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
+ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
+- mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
++ put_unaligned_le32(seq, &mgmt_ethhdr->seq);
+ }
+
+ static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
+--- a/include/linux/dsa/tag_qca.h
++++ b/include/linux/dsa/tag_qca.h
+@@ -56,9 +56,9 @@
+
+ /* Special struct emulating a Ethernet header */
+ struct qca_mgmt_ethhdr {
+- u32 command; /* command bit 31:0 */
+- u32 seq; /* seq 63:32 */
+- u32 mdio_data; /* first 4byte mdio */
++ __le32 command; /* command bit 31:0 */
++ __le32 seq; /* seq 63:32 */
++ __le32 mdio_data; /* first 4byte mdio */
+ __be16 hdr; /* qca hdr */
+ } __packed;
+
diff --git a/target/linux/generic/backport-6.1/776-v6.1-02-net-dsa-qca8k-fix-ethtool-autocast-mib-for-big-endia.patch b/target/linux/generic/backport-6.1/776-v6.1-02-net-dsa-qca8k-fix-ethtool-autocast-mib-for-big-endia.patch
new file mode 100644
index 0000000000..d13014bf93
--- /dev/null
+++ b/target/linux/generic/backport-6.1/776-v6.1-02-net-dsa-qca8k-fix-ethtool-autocast-mib-for-big-endia.patch
@@ -0,0 +1,81 @@
+From 0d4636f7d72df3179b20a2d32b647881917a5e2a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 12 Oct 2022 19:18:37 +0200
+Subject: [PATCH 2/2] net: dsa: qca8k: fix ethtool autocast mib for big-endian
+ systems
+
+The switch sends autocast mib in little-endian. This is problematic for
+big-endian system as the values needs to be converted.
+
+Fix this by converting each mib value to cpu byte order.
+
+Fixes: 5c957c7ca78c ("net: dsa: qca8k: add support for mib autocast in Ethernet packet")
+Tested-by: Pawel Dembicki <paweldembicki@gmail.com>
+Tested-by: Lech Perczak <lech.perczak@gmail.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 20 ++++++++------------
+ include/linux/dsa/tag_qca.h | 2 +-
+ 2 files changed, 9 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1668,9 +1668,9 @@ static void qca8k_mib_autocast_handler(s
+ struct qca8k_priv *priv = ds->priv;
+ const struct qca8k_mib_desc *mib;
+ struct mib_ethhdr *mib_ethhdr;
+- int i, mib_len, offset = 0;
+- u64 *data;
++ __le32 *data2;
+ u8 port;
++ int i;
+
+ mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb);
+ mib_eth_data = &priv->mib_eth_data;
+@@ -1682,28 +1682,24 @@ static void qca8k_mib_autocast_handler(s
+ if (port != mib_eth_data->req_port)
+ goto exit;
+
+- data = mib_eth_data->data;
++ data2 = (__le32 *)skb->data;
+
+ for (i = 0; i < priv->info->mib_count; i++) {
+ mib = &ar8327_mib[i];
+
+ /* First 3 mib are present in the skb head */
+ if (i < 3) {
+- data[i] = mib_ethhdr->data[i];
++ mib_eth_data->data[i] = get_unaligned_le32(mib_ethhdr->data + i);
+ continue;
+ }
+
+- mib_len = sizeof(uint32_t);
+-
+ /* Some mib are 64 bit wide */
+ if (mib->size == 2)
+- mib_len = sizeof(uint64_t);
+-
+- /* Copy the mib value from packet to the */
+- memcpy(data + i, skb->data + offset, mib_len);
++ mib_eth_data->data[i] = get_unaligned_le64((__le64 *)data2);
++ else
++ mib_eth_data->data[i] = get_unaligned_le32(data2);
+
+- /* Set the offset for the next mib */
+- offset += mib_len;
++ data2 += mib->size;
+ }
+
+ exit:
+--- a/include/linux/dsa/tag_qca.h
++++ b/include/linux/dsa/tag_qca.h
+@@ -68,7 +68,7 @@ enum mdio_cmd {
+ };
+
+ struct mib_ethhdr {
+- u32 data[3]; /* first 3 mib counter */
++ __le32 data[3]; /* first 3 mib counter */
+ __be16 hdr; /* qca hdr */
+ } __packed;
+
diff --git a/target/linux/generic/backport-6.1/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch b/target/linux/generic/backport-6.1/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch
new file mode 100644
index 0000000000..b61e7ede49
--- /dev/null
+++ b/target/linux/generic/backport-6.1/777-v6.2-01-net-dsa-qca8k-fix-wrong-length-value-for-mgmt-eth-pa.patch
@@ -0,0 +1,102 @@
+From 9807ae69746196ee4bbffe7d22d22ab2b61c6ed0 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 29 Dec 2022 17:33:32 +0100
+Subject: [PATCH 1/5] net: dsa: qca8k: fix wrong length value for mgmt eth
+ packet
+
+The assumption that Documentation was right about how this value work was
+wrong. It was discovered that the length value of the mgmt header is in
+step of word size.
+
+As an example to process 4 byte of data the correct length to set is 2.
+To process 8 byte 4, 12 byte 6, 16 byte 8...
+
+Odd values will always return the next size on the ack packet.
+(length of 3 (6 byte) will always return 8 bytes of data)
+
+This means that a value of 15 (0xf) actually means reading/writing 32 bytes
+of data instead of 16 bytes. This behaviour is totally absent and not
+documented in the switch Documentation.
+
+In fact from Documentation the max value that mgmt eth can process is
+16 byte of data while in reality it can process 32 bytes at once.
+
+To handle this we always round up the length after deviding it for word
+size. We check if the result is odd and we round another time to align
+to what the switch will provide in the ack packet.
+The workaround for the length limit of 15 is still needed as the length
+reg max value is 0xf(15)
+
+Reported-by: Ronald Wahl <ronald.wahl@raritan.com>
+Tested-by: Ronald Wahl <ronald.wahl@raritan.com>
+Fixes: 90386223f44e ("net: dsa: qca8k: add support for larger read/write size with mgmt Ethernet")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Cc: stable@vger.kernel.org # v5.18+
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 45 +++++++++++++++++++++++++-------
+ 1 file changed, 35 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -146,7 +146,16 @@ static void qca8k_rw_reg_ack_handler(str
+
+ command = get_unaligned_le32(&mgmt_ethhdr->command);
+ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command);
++
+ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command);
++ /* Special case for len of 15 as this is the max value for len and needs to
++ * be increased before converting it from word to dword.
++ */
++ if (len == 15)
++ len++;
++
++ /* We can ignore odd value, we always round up them in the alloc function. */
++ len *= sizeof(u16);
+
+ /* Make sure the seq match the requested packet */
+ if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq)
+@@ -193,17 +202,33 @@ static struct sk_buff *qca8k_alloc_mdio_
+ if (!skb)
+ return NULL;
+
+- /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte
+- * Actually for some reason the steps are:
+- * 0: nothing
+- * 1-4: first 4 byte
+- * 5-6: first 12 byte
+- * 7-15: all 16 byte
++ /* Hdr mgmt length value is in step of word size.
++ * As an example to process 4 byte of data the correct length to set is 2.
++ * To process 8 byte 4, 12 byte 6, 16 byte 8...
++ *
++ * Odd values will always return the next size on the ack packet.
++ * (length of 3 (6 byte) will always return 8 bytes of data)
++ *
++ * This means that a value of 15 (0xf) actually means reading/writing 32 bytes
++ * of data.
++ *
++ * To correctly calculate the length we devide the requested len by word and
++ * round up.
++ * On the ack function we can skip the odd check as we already handle the
++ * case here.
+ */
+- if (len == 16)
+- real_len = 15;
+- else
+- real_len = len;
++ real_len = DIV_ROUND_UP(len, sizeof(u16));
++
++ /* We check if the result len is odd and we round up another time to
++ * the next size. (length of 3 will be increased to 4 as switch will always
++ * return 8 bytes)
++ */
++ if (real_len % sizeof(u16) != 0)
++ real_len++;
++
++ /* Max reg value is 0xf(15) but switch will always return the next size (32 byte) */
++ if (real_len == 16)
++ real_len--;
+
+ skb_reset_mac_header(skb);
+ skb_set_network_header(skb, skb->len);
diff --git a/target/linux/generic/backport-6.1/777-v6.2-02-net-dsa-tag_qca-fix-wrong-MGMT_DATA2-size.patch b/target/linux/generic/backport-6.1/777-v6.2-02-net-dsa-tag_qca-fix-wrong-MGMT_DATA2-size.patch
new file mode 100644
index 0000000000..55ecb1eb42
--- /dev/null
+++ b/target/linux/generic/backport-6.1/777-v6.2-02-net-dsa-tag_qca-fix-wrong-MGMT_DATA2-size.patch
@@ -0,0 +1,34 @@
+From d9dba91be71f03cc75bcf39fc0d5d99ff33f1ae0 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 29 Dec 2022 17:33:33 +0100
+Subject: [PATCH 2/5] net: dsa: tag_qca: fix wrong MGMT_DATA2 size
+
+It was discovered that MGMT_DATA2 can contain up to 28 bytes of data
+instead of the 12 bytes written in the Documentation by accounting the
+limit of 16 bytes declared in Documentation subtracting the first 4 byte
+in the packet header.
+
+Update the define with the real world value.
+
+Tested-by: Ronald Wahl <ronald.wahl@raritan.com>
+Fixes: c2ee8181fddb ("net: dsa: tag_qca: add define for handling mgmt Ethernet packet")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Cc: stable@vger.kernel.org # v5.18+
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/dsa/tag_qca.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/include/linux/dsa/tag_qca.h
++++ b/include/linux/dsa/tag_qca.h
+@@ -40,8 +40,8 @@
+ QCA_HDR_MGMT_COMMAND_LEN + \
+ QCA_HDR_MGMT_DATA1_LEN)
+
+-#define QCA_HDR_MGMT_DATA2_LEN 12 /* Other 12 byte for the mdio data */
+-#define QCA_HDR_MGMT_PADDING_LEN 34 /* Padding to reach the min Ethernet packet */
++#define QCA_HDR_MGMT_DATA2_LEN 28 /* Other 28 byte for the mdio data */
++#define QCA_HDR_MGMT_PADDING_LEN 18 /* Padding to reach the min Ethernet packet */
+
+ #define QCA_HDR_MGMT_PKT_LEN (QCA_HDR_MGMT_HEADER_LEN + \
+ QCA_HDR_LEN + \
diff --git a/target/linux/generic/backport-6.1/777-v6.2-03-Revert-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch b/target/linux/generic/backport-6.1/777-v6.2-03-Revert-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch
new file mode 100644
index 0000000000..c8e22fd1b8
--- /dev/null
+++ b/target/linux/generic/backport-6.1/777-v6.2-03-Revert-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch
@@ -0,0 +1,172 @@
+From 03cb9e6d0b32b768e3d9d473c5c4ca1100877664 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 29 Dec 2022 17:33:34 +0100
+Subject: [PATCH 3/5] Revert "net: dsa: qca8k: cache lo and hi for mdio write"
+
+This reverts commit 2481d206fae7884cd07014fd1318e63af35e99eb.
+
+The Documentation is very confusing about the topic.
+The cache logic for hi and lo is wrong and actually miss some regs to be
+actually written.
+
+What the Documentation actually intended was that it's possible to skip
+writing hi OR lo if half of the reg is not needed to be written or read.
+
+Revert the change in favor of a better and correct implementation.
+
+Reported-by: Ronald Wahl <ronald.wahl@raritan.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Cc: stable@vger.kernel.org # v5.18+
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 61 +++++++-------------------------
+ drivers/net/dsa/qca/qca8k.h | 5 ---
+ 2 files changed, 12 insertions(+), 54 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -37,44 +37,6 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u
+ }
+
+ static int
+-qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo)
+-{
+- u16 *cached_lo = &priv->mdio_cache.lo;
+- struct mii_bus *bus = priv->bus;
+- int ret;
+-
+- if (lo == *cached_lo)
+- return 0;
+-
+- ret = bus->write(bus, phy_id, regnum, lo);
+- if (ret < 0)
+- dev_err_ratelimited(&bus->dev,
+- "failed to write qca8k 32bit lo register\n");
+-
+- *cached_lo = lo;
+- return 0;
+-}
+-
+-static int
+-qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi)
+-{
+- u16 *cached_hi = &priv->mdio_cache.hi;
+- struct mii_bus *bus = priv->bus;
+- int ret;
+-
+- if (hi == *cached_hi)
+- return 0;
+-
+- ret = bus->write(bus, phy_id, regnum, hi);
+- if (ret < 0)
+- dev_err_ratelimited(&bus->dev,
+- "failed to write qca8k 32bit hi register\n");
+-
+- *cached_hi = hi;
+- return 0;
+-}
+-
+-static int
+ qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
+ {
+ int ret;
+@@ -97,7 +59,7 @@ qca8k_mii_read32(struct mii_bus *bus, in
+ }
+
+ static void
+-qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val)
++qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
+ {
+ u16 lo, hi;
+ int ret;
+@@ -105,9 +67,12 @@ qca8k_mii_write32(struct qca8k_priv *pri
+ lo = val & 0xffff;
+ hi = (u16)(val >> 16);
+
+- ret = qca8k_set_lo(priv, phy_id, regnum, lo);
++ ret = bus->write(bus, phy_id, regnum, lo);
+ if (ret >= 0)
+- ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi);
++ ret = bus->write(bus, phy_id, regnum + 1, hi);
++ if (ret < 0)
++ dev_err_ratelimited(&bus->dev,
++ "failed to write qca8k 32bit register\n");
+ }
+
+ static int
+@@ -442,7 +407,7 @@ qca8k_regmap_write(void *ctx, uint32_t r
+ if (ret < 0)
+ goto exit;
+
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+
+ exit:
+ mutex_unlock(&bus->mdio_lock);
+@@ -475,7 +440,7 @@ qca8k_regmap_update_bits(void *ctx, uint
+
+ val &= ~mask;
+ val |= write_val;
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+
+ exit:
+ mutex_unlock(&bus->mdio_lock);
+@@ -750,14 +715,14 @@ qca8k_mdio_write(struct qca8k_priv *priv
+ if (ret)
+ goto exit;
+
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+
+ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+
+ exit:
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
+
+ mutex_unlock(&bus->mdio_lock);
+
+@@ -787,7 +752,7 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ if (ret)
+ goto exit;
+
+- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
+
+ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+@@ -798,7 +763,7 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+
+ exit:
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
++ qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
+
+ mutex_unlock(&bus->mdio_lock);
+
+@@ -1914,8 +1879,6 @@ qca8k_sw_probe(struct mdio_device *mdiod
+ }
+
+ priv->mdio_cache.page = 0xffff;
+- priv->mdio_cache.lo = 0xffff;
+- priv->mdio_cache.hi = 0xffff;
+
+ /* Check the detected switch id */
+ ret = qca8k_read_switch_id(priv);
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -375,11 +375,6 @@ struct qca8k_mdio_cache {
+ * mdio writes
+ */
+ u16 page;
+-/* lo and hi can also be cached and from Documentation we can skip one
+- * extra mdio write if lo or hi is didn't change.
+- */
+- u16 lo;
+- u16 hi;
+ };
+
+ struct qca8k_priv {
diff --git a/target/linux/generic/backport-6.1/777-v6.2-04-net-dsa-qca8k-introduce-single-mii-read-write-lo-hi.patch b/target/linux/generic/backport-6.1/777-v6.2-04-net-dsa-qca8k-introduce-single-mii-read-write-lo-hi.patch
new file mode 100644
index 0000000000..c0320ad6f9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/777-v6.2-04-net-dsa-qca8k-introduce-single-mii-read-write-lo-hi.patch
@@ -0,0 +1,150 @@
+From cfbd6de588ef659c198083205dc954a6d3ed2aec Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 29 Dec 2022 17:33:35 +0100
+Subject: [PATCH 4/5] net: dsa: qca8k: introduce single mii read/write lo/hi
+
+It may be useful to read/write just the lo or hi half of a reg.
+
+This is especially useful for phy poll with the use of mdio master.
+The mdio master reg is composed by the first 16 bit related to setup and
+the other half with the returned data or data to write.
+
+Refactor the mii function to permit single mii read/write of lo or hi
+half of the reg.
+
+Tested-by: Ronald Wahl <ronald.wahl@raritan.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 106 ++++++++++++++++++++++++-------
+ 1 file changed, 84 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -37,42 +37,104 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u
+ }
+
+ static int
+-qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
++qca8k_mii_write_lo(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
+ {
+ int ret;
++ u16 lo;
+
+- ret = bus->read(bus, phy_id, regnum);
+- if (ret >= 0) {
+- *val = ret;
+- ret = bus->read(bus, phy_id, regnum + 1);
+- *val |= ret << 16;
+- }
++ lo = val & 0xffff;
++ ret = bus->write(bus, phy_id, regnum, lo);
++ if (ret < 0)
++ dev_err_ratelimited(&bus->dev,
++ "failed to write qca8k 32bit lo register\n");
++
++ return ret;
++}
+
+- if (ret < 0) {
++static int
++qca8k_mii_write_hi(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
++{
++ int ret;
++ u16 hi;
++
++ hi = (u16)(val >> 16);
++ ret = bus->write(bus, phy_id, regnum, hi);
++ if (ret < 0)
+ dev_err_ratelimited(&bus->dev,
+- "failed to read qca8k 32bit register\n");
+- *val = 0;
+- return ret;
+- }
++ "failed to write qca8k 32bit hi register\n");
+
++ return ret;
++}
++
++static int
++qca8k_mii_read_lo(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
++{
++ int ret;
++
++ ret = bus->read(bus, phy_id, regnum);
++ if (ret < 0)
++ goto err;
++
++ *val = ret & 0xffff;
+ return 0;
++
++err:
++ dev_err_ratelimited(&bus->dev,
++ "failed to read qca8k 32bit lo register\n");
++ *val = 0;
++
++ return ret;
+ }
+
+-static void
+-qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
++static int
++qca8k_mii_read_hi(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
+ {
+- u16 lo, hi;
+ int ret;
+
+- lo = val & 0xffff;
+- hi = (u16)(val >> 16);
++ ret = bus->read(bus, phy_id, regnum);
++ if (ret < 0)
++ goto err;
+
+- ret = bus->write(bus, phy_id, regnum, lo);
+- if (ret >= 0)
+- ret = bus->write(bus, phy_id, regnum + 1, hi);
++ *val = ret << 16;
++ return 0;
++
++err:
++ dev_err_ratelimited(&bus->dev,
++ "failed to read qca8k 32bit hi register\n");
++ *val = 0;
++
++ return ret;
++}
++
++static int
++qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
++{
++ u32 hi, lo;
++ int ret;
++
++ *val = 0;
++
++ ret = qca8k_mii_read_lo(bus, phy_id, regnum, &lo);
+ if (ret < 0)
+- dev_err_ratelimited(&bus->dev,
+- "failed to write qca8k 32bit register\n");
++ goto err;
++
++ ret = qca8k_mii_read_hi(bus, phy_id, regnum + 1, &hi);
++ if (ret < 0)
++ goto err;
++
++ *val = lo | hi;
++
++err:
++ return ret;
++}
++
++static void
++qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
++{
++ if (qca8k_mii_write_lo(bus, phy_id, regnum, val) < 0)
++ return;
++
++ qca8k_mii_write_hi(bus, phy_id, regnum + 1, val);
+ }
+
+ static int
diff --git a/target/linux/generic/backport-6.1/777-v6.2-05-net-dsa-qca8k-improve-mdio-master-read-write-by-usin.patch b/target/linux/generic/backport-6.1/777-v6.2-05-net-dsa-qca8k-improve-mdio-master-read-write-by-usin.patch
new file mode 100644
index 0000000000..4cbb66cf35
--- /dev/null
+++ b/target/linux/generic/backport-6.1/777-v6.2-05-net-dsa-qca8k-improve-mdio-master-read-write-by-usin.patch
@@ -0,0 +1,73 @@
+From a4165830ca237f2b3318faf62562bce8ce12a389 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 29 Dec 2022 17:33:36 +0100
+Subject: [PATCH 5/5] net: dsa: qca8k: improve mdio master read/write by using
+ single lo/hi
+
+Improve mdio master read/write by using singe mii read/write lo/hi.
+
+In a read and write we need to poll the mdio master regs in a busy loop
+to check for a specific bit present in the upper half of the reg. We can
+ignore the other half since it won't contain useful data. This will save
+an additional useless read for each read and write operation.
+
+In a read operation the returned data is present in the mdio master reg
+lower half. We can ignore the other half since it won't contain useful
+data. This will save an additional useless read for each read operation.
+
+In a read operation it's needed to just set the hi half of the mdio
+master reg as the lo half will be replaced by the result. This will save
+an additional useless write for each read operation.
+
+Tested-by: Ronald Wahl <ronald.wahl@raritan.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -740,9 +740,9 @@ qca8k_mdio_busy_wait(struct mii_bus *bus
+
+ qca8k_split_addr(reg, &r1, &r2, &page);
+
+- ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0,
++ ret = read_poll_timeout(qca8k_mii_read_hi, ret1, !(val & mask), 0,
+ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
+- bus, 0x10 | r2, r1, &val);
++ bus, 0x10 | r2, r1 + 1, &val);
+
+ /* Check if qca8k_read has failed for a different reason
+ * before returnting -ETIMEDOUT
+@@ -784,7 +784,7 @@ qca8k_mdio_write(struct qca8k_priv *priv
+
+ exit:
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
++ qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0);
+
+ mutex_unlock(&bus->mdio_lock);
+
+@@ -814,18 +814,18 @@ qca8k_mdio_read(struct qca8k_priv *priv,
+ if (ret)
+ goto exit;
+
+- qca8k_mii_write32(bus, 0x10 | r2, r1, val);
++ qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, val);
+
+ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
+ QCA8K_MDIO_MASTER_BUSY);
+ if (ret)
+ goto exit;
+
+- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
++ ret = qca8k_mii_read_lo(bus, 0x10 | r2, r1, &val);
+
+ exit:
+ /* even if the busy_wait timeouts try to clear the MASTER_EN */
+- qca8k_mii_write32(bus, 0x10 | r2, r1, 0);
++ qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0);
+
+ mutex_unlock(&bus->mdio_lock);
+
diff --git a/target/linux/generic/backport-6.1/778-v5.18-01-net-phy-at803x-add-fiber-support.patch b/target/linux/generic/backport-6.1/778-v5.18-01-net-phy-at803x-add-fiber-support.patch
new file mode 100644
index 0000000000..7cb21ed00d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/778-v5.18-01-net-phy-at803x-add-fiber-support.patch
@@ -0,0 +1,193 @@
+From 3265f421887847db9ae2c01a00645e33608556d8 Mon Sep 17 00:00:00 2001
+From: Robert Hancock <robert.hancock@calian.com>
+Date: Tue, 25 Jan 2022 10:54:09 -0600
+Subject: [PATCH] net: phy: at803x: add fiber support
+
+Previously this driver always forced the copper page to be selected,
+however for AR8031 in 100Base-FX or 1000Base-X modes, the fiber page
+needs to be selected. Set the appropriate mode based on the hardware
+mode_cfg strap selection.
+
+Enable the appropriate interrupt bits to detect fiber-side link up
+or down events.
+
+Update config_aneg and read_status methods to use the appropriate
+Clause 37 calls when fiber mode is in use.
+
+Signed-off-by: Robert Hancock <robert.hancock@calian.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 76 +++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 67 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -48,6 +48,8 @@
+ #define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12)
+ #define AT803X_INTR_ENABLE_LINK_FAIL BIT(11)
+ #define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10)
++#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8)
++#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7)
+ #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5)
+ #define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1)
+ #define AT803X_INTR_ENABLE_WOL BIT(0)
+@@ -82,6 +84,17 @@
+
+ #define AT803X_MODE_CFG_MASK 0x0F
+ #define AT803X_MODE_CFG_SGMII 0x01
++#define AT803X_MODE_CFG_BASET_RGMII 0x00
++#define AT803X_MODE_CFG_BASET_SGMII 0x01
++#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02
++#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03
++#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04
++#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05
++#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06
++#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07
++#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B
++#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E
++#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F
+
+ #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
+ #define AT803X_PSSR_MR_AN_COMPLETE 0x0200
+@@ -199,6 +212,8 @@ struct at803x_priv {
+ u16 clk_25m_mask;
+ u8 smarteee_lpi_tw_1g;
+ u8 smarteee_lpi_tw_100m;
++ bool is_fiber;
++ bool is_1000basex;
+ struct regulator_dev *vddio_rdev;
+ struct regulator_dev *vddh_rdev;
+ struct regulator *vddio;
+@@ -674,7 +689,33 @@ static int at803x_probe(struct phy_devic
+ return ret;
+ }
+
++ if (phydev->drv->phy_id == ATH8031_PHY_ID) {
++ int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
++ int mode_cfg;
++
++ if (ccr < 0)
++ goto err;
++ mode_cfg = ccr & AT803X_MODE_CFG_MASK;
++
++ switch (mode_cfg) {
++ case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
++ case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
++ priv->is_1000basex = true;
++ fallthrough;
++ case AT803X_MODE_CFG_FX100_RGMII_50OHM:
++ case AT803X_MODE_CFG_FX100_RGMII_75OHM:
++ priv->is_fiber = true;
++ break;
++ }
++ }
++
+ return 0;
++
++err:
++ if (priv->vddio)
++ regulator_disable(priv->vddio);
++
++ return ret;
+ }
+
+ static void at803x_remove(struct phy_device *phydev)
+@@ -687,6 +728,7 @@ static void at803x_remove(struct phy_dev
+
+ static int at803x_get_features(struct phy_device *phydev)
+ {
++ struct at803x_priv *priv = phydev->priv;
+ int err;
+
+ err = genphy_read_abilities(phydev);
+@@ -704,12 +746,13 @@ static int at803x_get_features(struct ph
+ * As a result of that, ESTATUS_1000_XFULL is set
+ * to 1 even when operating in copper TP mode.
+ *
+- * Remove this mode from the supported link modes,
+- * as this driver currently only supports copper
+- * operation.
++ * Remove this mode from the supported link modes
++ * when not operating in 1000BaseX mode.
+ */
+- linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+- phydev->supported);
++ if (!priv->is_1000basex)
++ linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
++ phydev->supported);
++
+ return 0;
+ }
+
+@@ -773,15 +816,18 @@ static int at8031_pll_config(struct phy_
+
+ static int at803x_config_init(struct phy_device *phydev)
+ {
++ struct at803x_priv *priv = phydev->priv;
+ int ret;
+
+ if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+ /* Some bootloaders leave the fiber page selected.
+- * Switch to the copper page, as otherwise we read
+- * the PHY capabilities from the fiber side.
++ * Switch to the appropriate page (fiber or copper), as otherwise we
++ * read the PHY capabilities from the wrong page.
+ */
+ phy_lock_mdio_bus(phydev);
+- ret = at803x_write_page(phydev, AT803X_PAGE_COPPER);
++ ret = at803x_write_page(phydev,
++ priv->is_fiber ? AT803X_PAGE_FIBER :
++ AT803X_PAGE_COPPER);
+ phy_unlock_mdio_bus(phydev);
+ if (ret)
+ return ret;
+@@ -840,6 +886,7 @@ static int at803x_ack_interrupt(struct p
+
+ static int at803x_config_intr(struct phy_device *phydev)
+ {
++ struct at803x_priv *priv = phydev->priv;
+ int err;
+ int value;
+
+@@ -856,6 +903,10 @@ static int at803x_config_intr(struct phy
+ value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
+ value |= AT803X_INTR_ENABLE_LINK_FAIL;
+ value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
++ if (priv->is_fiber) {
++ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
++ }
+
+ err = phy_write(phydev, AT803X_INTR_ENABLE, value);
+ } else {
+@@ -923,8 +974,12 @@ static void at803x_link_change_notify(st
+
+ static int at803x_read_status(struct phy_device *phydev)
+ {
++ struct at803x_priv *priv = phydev->priv;
+ int ss, err, old_link = phydev->link;
+
++ if (priv->is_1000basex)
++ return genphy_c37_read_status(phydev);
++
+ /* Update the link, but return if there was an error */
+ err = genphy_update_link(phydev);
+ if (err)
+@@ -1023,6 +1078,7 @@ static int at803x_config_mdix(struct phy
+
+ static int at803x_config_aneg(struct phy_device *phydev)
+ {
++ struct at803x_priv *priv = phydev->priv;
+ int ret;
+
+ ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
+@@ -1039,6 +1095,9 @@ static int at803x_config_aneg(struct phy
+ return ret;
+ }
+
++ if (priv->is_1000basex)
++ return genphy_c37_config_aneg(phydev);
++
+ return genphy_config_aneg(phydev);
+ }
+
diff --git a/target/linux/generic/backport-6.1/778-v5.18-02-net-phy-at803x-support-downstream-SFP-cage.patch b/target/linux/generic/backport-6.1/778-v5.18-02-net-phy-at803x-support-downstream-SFP-cage.patch
new file mode 100644
index 0000000000..8393cb32e8
--- /dev/null
+++ b/target/linux/generic/backport-6.1/778-v5.18-02-net-phy-at803x-support-downstream-SFP-cage.patch
@@ -0,0 +1,95 @@
+From dc4d5fcc5d365c9f70ea3f5c09bdf70e988fad50 Mon Sep 17 00:00:00 2001
+From: Robert Hancock <robert.hancock@calian.com>
+Date: Tue, 25 Jan 2022 10:54:10 -0600
+Subject: [PATCH] net: phy: at803x: Support downstream SFP cage
+
+Add support for downstream SFP cages for AR8031 and AR8033. This is
+primarily intended for fiber modules or direct-attach cables, however
+copper modules which work in 1000Base-X mode may also function. Such
+modules are allowed with a warning.
+
+Signed-off-by: Robert Hancock <robert.hancock@calian.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 56 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 56 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -19,6 +19,8 @@
+ #include <linux/regulator/of_regulator.h>
+ #include <linux/regulator/driver.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/phylink.h>
++#include <linux/sfp.h>
+ #include <dt-bindings/net/qca-ar803x.h>
+
+ #define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10
+@@ -555,6 +557,55 @@ static int at8031_register_regulators(st
+ return 0;
+ }
+
++static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++{
++ struct phy_device *phydev = upstream;
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
++ phy_interface_t iface;
++
++ linkmode_zero(phy_support);
++ phylink_set(phy_support, 1000baseX_Full);
++ phylink_set(phy_support, 1000baseT_Full);
++ phylink_set(phy_support, Autoneg);
++ phylink_set(phy_support, Pause);
++ phylink_set(phy_support, Asym_Pause);
++
++ linkmode_zero(sfp_support);
++ sfp_parse_support(phydev->sfp_bus, id, sfp_support);
++ /* Some modules support 10G modes as well as others we support.
++ * Mask out non-supported modes so the correct interface is picked.
++ */
++ linkmode_and(sfp_support, phy_support, sfp_support);
++
++ if (linkmode_empty(sfp_support)) {
++ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
++ return -EINVAL;
++ }
++
++ iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
++
++ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
++ * interface for use with SFP modules.
++ * However, some copper modules detected as having a preferred SGMII
++ * interface do default to and function in 1000Base-X mode, so just
++ * print a warning and allow such modules, as they may have some chance
++ * of working.
++ */
++ if (iface == PHY_INTERFACE_MODE_SGMII)
++ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
++ else if (iface != PHY_INTERFACE_MODE_1000BASEX)
++ return -EINVAL;
++
++ return 0;
++}
++
++static const struct sfp_upstream_ops at803x_sfp_ops = {
++ .attach = phy_sfp_attach,
++ .detach = phy_sfp_detach,
++ .module_insert = at803x_sfp_insert,
++};
++
+ static int at803x_parse_dt(struct phy_device *phydev)
+ {
+ struct device_node *node = phydev->mdio.dev.of_node;
+@@ -662,6 +713,11 @@ static int at803x_parse_dt(struct phy_de
+ phydev_err(phydev, "failed to get VDDIO regulator\n");
+ return PTR_ERR(priv->vddio);
+ }
++
++ /* Only AR8031/8033 support 1000Base-X for SFP modules */
++ ret = phy_sfp_probe(phydev, &at803x_sfp_ops);
++ if (ret < 0)
++ return ret;
+ }
+
+ return 0;
diff --git a/target/linux/generic/backport-6.1/778-v5.18-03-net-phy-at803x-fix-NULL-pointer-dereference-on-AR9331-PHY.patch b/target/linux/generic/backport-6.1/778-v5.18-03-net-phy-at803x-fix-NULL-pointer-dereference-on-AR9331-PHY.patch
new file mode 100644
index 0000000000..de8951db10
--- /dev/null
+++ b/target/linux/generic/backport-6.1/778-v5.18-03-net-phy-at803x-fix-NULL-pointer-dereference-on-AR9331-PHY.patch
@@ -0,0 +1,56 @@
+From 9926de7315be3d606cc011a305ad9adb9e8e14c9 Mon Sep 17 00:00:00 2001
+From: Oleksij Rempel <o.rempel@pengutronix.de>
+Date: Sat, 18 Jun 2022 14:23:33 +0200
+Subject: [PATCH] net: phy: at803x: fix NULL pointer dereference on AR9331 PHY
+
+Latest kernel will explode on the PHY interrupt config, since it depends
+now on allocated priv. So, run probe to allocate priv to fix it.
+
+ ar9331_switch ethernet.1:10 lan0 (uninitialized): PHY [!ahb!ethernet@1a000000!mdio!switch@10:00] driver [Qualcomm Atheros AR9331 built-in PHY] (irq=13)
+ CPU 0 Unable to handle kernel paging request at virtual address 0000000a, epc == 8050e8a8, ra == 80504b34
+ ...
+ Call Trace:
+ [<8050e8a8>] at803x_config_intr+0x5c/0xd0
+ [<80504b34>] phy_request_interrupt+0xa8/0xd0
+ [<8050289c>] phylink_bringup_phy+0x2d8/0x3ac
+ [<80502b68>] phylink_fwnode_phy_connect+0x118/0x130
+ [<8074d8ec>] dsa_slave_create+0x270/0x420
+ [<80743b04>] dsa_port_setup+0x12c/0x148
+ [<8074580c>] dsa_register_switch+0xaf0/0xcc0
+ [<80511344>] ar9331_sw_probe+0x370/0x388
+ [<8050cb78>] mdio_probe+0x44/0x70
+ [<804df300>] really_probe+0x200/0x424
+ [<804df7b4>] __driver_probe_device+0x290/0x298
+ [<804df810>] driver_probe_device+0x54/0xe4
+ [<804dfd50>] __device_attach_driver+0xe4/0x130
+ [<804dcb00>] bus_for_each_drv+0xb4/0xd8
+ [<804dfac4>] __device_attach+0x104/0x1a4
+ [<804ddd24>] bus_probe_device+0x48/0xc4
+ [<804deb44>] deferred_probe_work_func+0xf0/0x10c
+ [<800a0ffc>] process_one_work+0x314/0x4d4
+ [<800a17fc>] worker_thread+0x2a4/0x354
+ [<800a9a54>] kthread+0x134/0x13c
+ [<8006306c>] ret_from_kernel_thread+0x14/0x1c
+
+Same Issue would affect some other PHYs (QCA8081, QCA9561), so fix it
+too.
+
+Fixes: 3265f4218878 ("net: phy: at803x: add fiber support")
+Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1594,6 +1594,8 @@ static struct phy_driver at803x_driver[]
+ /* ATHEROS AR9331 */
+ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
+ .name = "Qualcomm Atheros AR9331 built-in PHY",
++ .probe = at803x_probe,
++ .remove = at803x_remove,
+ .suspend = at803x_suspend,
+ .resume = at803x_resume,
+ .flags = PHY_POLL_CABLE_TEST,
diff --git a/target/linux/generic/backport-6.1/778-v5.18-04-net-phy-at803x-fix-error-return-code-in-at803x_probe.patch b/target/linux/generic/backport-6.1/778-v5.18-04-net-phy-at803x-fix-error-return-code-in-at803x_probe.patch
new file mode 100644
index 0000000000..cdae5b4ca4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/778-v5.18-04-net-phy-at803x-fix-error-return-code-in-at803x_probe.patch
@@ -0,0 +1,31 @@
+From 1f0dd412e34e177621769866bef347f0b22364df Mon Sep 17 00:00:00 2001
+From: Wei Yongjun <weiyongjun1@huawei.com>
+Date: Fri, 18 Nov 2022 10:36:35 +0000
+Subject: [PATCH] net: phy: at803x: fix error return code in at803x_probe()
+
+Fix to return a negative error code from the ccr read error handling
+case instead of 0, as done elsewhere in this function.
+
+Fixes: 3265f4218878 ("net: phy: at803x: add fiber support")
+Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20221118103635.254256-1-weiyongjun@huaweicloud.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/at803x.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -749,8 +749,10 @@ static int at803x_probe(struct phy_devic
+ int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
+ int mode_cfg;
+
+- if (ccr < 0)
++ if (ccr < 0) {
++ ret = ccr;
+ goto err;
++ }
+ mode_cfg = ccr & AT803X_MODE_CFG_MASK;
+
+ switch (mode_cfg) {
diff --git a/target/linux/generic/backport-6.1/780-v5.16-bus-mhi-pci_generic-Introduce-Sierra-EM919X-support.patch b/target/linux/generic/backport-6.1/780-v5.16-bus-mhi-pci_generic-Introduce-Sierra-EM919X-support.patch
new file mode 100644
index 0000000000..44f0864e9a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/780-v5.16-bus-mhi-pci_generic-Introduce-Sierra-EM919X-support.patch
@@ -0,0 +1,80 @@
+From 789d3eeb2367f92193a0882f7cdab03f0f9d6930 Mon Sep 17 00:00:00 2001
+From: Thomas Perrot <thomas.perrot@bootlin.com>
+Date: Thu, 16 Dec 2021 13:42:27 +0530
+Subject: [PATCH] bus: mhi: pci_generic: Introduce Sierra EM919X support
+
+Add support for EM919X modems, this modem series is based on SDX55
+qcom chip.
+
+It is mandatory to use the same ring for control+data and diag events.
+
+Link: https://lore.kernel.org/r/20211123081541.648426-1-thomas.perrot@bootlin.com
+Tested-by: Aleksander Morgado <aleksander@aleksander.es>
+Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
+Signed-off-by: Thomas Perrot <thomas.perrot@bootlin.com>
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Link: https://lore.kernel.org/r/20211216081227.237749-11-manivannan.sadhasivam@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/bus/mhi/host/pci_generic.c | 43 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 43 insertions(+)
+
+--- a/drivers/bus/mhi/host/pci_generic.c
++++ b/drivers/bus/mhi/host/pci_generic.c
+@@ -406,6 +406,46 @@ static const struct mhi_pci_dev_info mhi
+ .mru_default = 32768,
+ };
+
++static const struct mhi_channel_config mhi_sierra_em919x_channels[] = {
++ MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0),
++ MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 256, 0),
++ MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 0),
++ MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 0),
++ MHI_CHANNEL_CONFIG_UL(12, "MBIM", 128, 0),
++ MHI_CHANNEL_CONFIG_DL(13, "MBIM", 128, 0),
++ MHI_CHANNEL_CONFIG_UL(14, "QMI", 32, 0),
++ MHI_CHANNEL_CONFIG_DL(15, "QMI", 32, 0),
++ MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0),
++ MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0),
++ MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 512, 1),
++ MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 512, 2),
++};
++
++static struct mhi_event_config modem_sierra_em919x_mhi_events[] = {
++ /* first ring is control+data and DIAG ring */
++ MHI_EVENT_CONFIG_CTRL(0, 2048),
++ /* Hardware channels request dedicated hardware event rings */
++ MHI_EVENT_CONFIG_HW_DATA(1, 2048, 100),
++ MHI_EVENT_CONFIG_HW_DATA(2, 2048, 101)
++};
++
++static const struct mhi_controller_config modem_sierra_em919x_config = {
++ .max_channels = 128,
++ .timeout_ms = 24000,
++ .num_channels = ARRAY_SIZE(mhi_sierra_em919x_channels),
++ .ch_cfg = mhi_sierra_em919x_channels,
++ .num_events = ARRAY_SIZE(modem_sierra_em919x_mhi_events),
++ .event_cfg = modem_sierra_em919x_mhi_events,
++};
++
++static const struct mhi_pci_dev_info mhi_sierra_em919x_info = {
++ .name = "sierra-em919x",
++ .config = &modem_sierra_em919x_config,
++ .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
++ .dma_data_width = 32,
++ .sideband_wake = false,
++};
++
+ static const struct mhi_channel_config mhi_telit_fn980_hw_v1_channels[] = {
+ MHI_CHANNEL_CONFIG_UL(14, "QMI", 32, 0),
+ MHI_CHANNEL_CONFIG_DL(15, "QMI", 32, 0),
+@@ -480,6 +520,9 @@ static const struct mhi_pci_dev_info mhi
+ };
+
+ static const struct pci_device_id mhi_pci_id_table[] = {
++ /* EM919x (sdx55), use the same vid:pid as qcom-sdx55m */
++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x18d7, 0x0200),
++ .driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info },
+ /* Telit FN980 hardware revision v1 */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x1C5D, 0x2000),
+ .driver_data = (kernel_ulong_t) &mhi_telit_fn980_hw_v1_info },
diff --git a/target/linux/generic/backport-6.1/781-v6.1-bus-mhi-host-always-print-detected-modem-name.patch b/target/linux/generic/backport-6.1/781-v6.1-bus-mhi-host-always-print-detected-modem-name.patch
new file mode 100644
index 0000000000..3c71a27b79
--- /dev/null
+++ b/target/linux/generic/backport-6.1/781-v6.1-bus-mhi-host-always-print-detected-modem-name.patch
@@ -0,0 +1,37 @@
+From f369e9ad52ec9361827e21a631b7198c9fca438e Mon Sep 17 00:00:00 2001
+From: Koen Vandeputte <koen.vandeputte@citymesh.com>
+Date: Wed, 31 Aug 2022 12:03:49 +0200
+Subject: [PATCH] bus: mhi: host: always print detected modem name
+
+This harmless print provides a very easy way of knowing
+if the modem is detected properly during probing.
+
+Promote it to an informational print so no hassle is required
+enabling kernel debugging info to obtain it.
+
+The rationale here is that:
+On a lot of low-storage embedded devices, extensive kernel
+debugging info is not always present as this would
+increase it's size to much causing partition size issues.
+
+Signed-off-by: Koen Vandeputte <koen.vandeputte@citymesh.com>
+Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Reviewed-by: Loic Poulain <loic.poulain@linaro.org>
+Link: https://lore.kernel.org/r/20220831100349.1488762-1-koen.vandeputte@citymesh.com
+[mani: added missing review tags]
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+---
+ drivers/bus/mhi/host/pci_generic.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/bus/mhi/host/pci_generic.c
++++ b/drivers/bus/mhi/host/pci_generic.c
+@@ -806,7 +806,7 @@ static int mhi_pci_probe(struct pci_dev
+ struct mhi_controller *mhi_cntrl;
+ int err;
+
+- dev_dbg(&pdev->dev, "MHI PCI device found: %s\n", info->name);
++ dev_info(&pdev->dev, "MHI PCI device found: %s\n", info->name);
+
+ /* mhi_pdev.mhi_cntrl must be zero-initialized */
+ mhi_pdev = devm_kzalloc(&pdev->dev, sizeof(*mhi_pdev), GFP_KERNEL);
diff --git a/target/linux/generic/backport-6.1/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch b/target/linux/generic/backport-6.1/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch
new file mode 100644
index 0000000000..ebfbaccc9d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch
@@ -0,0 +1,130 @@
+From e19de30d20809af3221ef8a2648b8a8a52e02d90 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 21 Sep 2022 01:23:14 +0100
+Subject: [PATCH 1/1] net: dsa: mt7530: add support for in-band link status
+
+Read link status from SGMII PCS for in-band managed 2500Base-X and
+1000Base-X connection on a MAC port of the MT7531. This is needed to
+get the SFP cage working which is connected to SGMII interface of
+port 5 of the MT7531 switch IC on the Bananapi BPi-R3 board.
+While at it also handle an_complete for both the autoneg and the
+non-autoneg codepath.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 50 +++++++++++++++++++++++++++++-----------
+ drivers/net/dsa/mt7530.h | 1 +
+ 2 files changed, 38 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2721,9 +2721,6 @@ mt7531_mac_config(struct dsa_switch *ds,
+ case PHY_INTERFACE_MODE_NA:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+- if (phylink_autoneg_inband(mode))
+- return -EINVAL;
+-
+ return mt7531_sgmii_setup_mode_force(priv, port, interface);
+ default:
+ return -EINVAL;
+@@ -2799,13 +2796,6 @@ unsupported:
+ return;
+ }
+
+- if (phylink_autoneg_inband(mode) &&
+- state->interface != PHY_INTERFACE_MODE_SGMII) {
+- dev_err(ds->dev, "%s: in-band negotiation unsupported\n",
+- __func__);
+- return;
+- }
+-
+ mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
+ mcr_new = mcr_cur;
+ mcr_new &= ~PMCR_LINK_SETTINGS_MASK;
+@@ -2942,6 +2932,9 @@ static void mt753x_phylink_get_caps(stru
+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000FD;
+
++ if ((priv->id == ID_MT7531) && mt753x_is_mac_port(port))
++ config->mac_capabilities |= MAC_2500FD;
++
+ /* This driver does not make use of the speed, duplex, pause or the
+ * advertisement in its mac_config, so it is safe to mark this driver
+ * as non-legacy.
+@@ -3007,6 +3000,7 @@ mt7531_sgmii_pcs_get_state_an(struct mt7
+
+ status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
+ state->link = !!(status & MT7531_SGMII_LINK_STATUS);
++ state->an_complete = !!(status & MT7531_SGMII_AN_COMPLETE);
+ if (state->interface == PHY_INTERFACE_MODE_SGMII &&
+ (status & MT7531_SGMII_AN_ENABLE)) {
+ val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port));
+@@ -3037,16 +3031,44 @@ mt7531_sgmii_pcs_get_state_an(struct mt7
+ return 0;
+ }
+
++static void
++mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port,
++ struct phylink_link_state *state)
++{
++ unsigned int val;
++
++ val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
++ state->link = !!(val & MT7531_SGMII_LINK_STATUS);
++ if (!state->link)
++ return;
++
++ state->an_complete = state->link;
++
++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
++ state->speed = SPEED_2500;
++ else
++ state->speed = SPEED_1000;
++
++ state->duplex = DUPLEX_FULL;
++ state->pause = MLO_PAUSE_NONE;
++}
++
+ static void mt7531_pcs_get_state(struct phylink_pcs *pcs,
+ struct phylink_link_state *state)
+ {
+ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
+ int port = pcs_to_mt753x_pcs(pcs)->port;
+
+- if (state->interface == PHY_INTERFACE_MODE_SGMII)
++ if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+ mt7531_sgmii_pcs_get_state_an(priv, port, state);
+- else
+- state->link = false;
++ return;
++ } else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) ||
++ (state->interface == PHY_INTERFACE_MODE_2500BASEX)) {
++ mt7531_sgmii_pcs_get_state_inband(priv, port, state);
++ return;
++ }
++
++ state->link = false;
+ }
+
+ static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+@@ -3087,6 +3109,8 @@ mt753x_setup(struct dsa_switch *ds)
+ priv->pcs[i].pcs.ops = priv->info->pcs_ops;
+ priv->pcs[i].priv = priv;
+ priv->pcs[i].port = i;
++ if (mt753x_is_mac_port(i))
++ priv->pcs[i].pcs.poll = 1;
+ }
+
+ ret = priv->info->sw_setup(ds);
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -373,6 +373,7 @@ enum mt7530_vlan_port_acc_frm {
+ #define MT7531_SGMII_LINK_STATUS BIT(18)
+ #define MT7531_SGMII_AN_ENABLE BIT(12)
+ #define MT7531_SGMII_AN_RESTART BIT(9)
++#define MT7531_SGMII_AN_COMPLETE BIT(21)
+
+ /* Register for SGMII PCS_SPPED_ABILITY */
+ #define MT7531_PCS_SPEED_ABILITY(p) MT7531_SGMII_REG(p, 0x08)
diff --git a/target/linux/generic/backport-6.1/783-v6.1-net-sfp-re-implement-soft-state-polling-setup.patch b/target/linux/generic/backport-6.1/783-v6.1-net-sfp-re-implement-soft-state-polling-setup.patch
new file mode 100644
index 0000000000..77cd336d36
--- /dev/null
+++ b/target/linux/generic/backport-6.1/783-v6.1-net-sfp-re-implement-soft-state-polling-setup.patch
@@ -0,0 +1,98 @@
+From 8475c4b70b040f9d8cbc308100f2c4d865f810b3 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:27 +0100
+Subject: [PATCH 1/1] net: sfp: re-implement soft state polling setup
+
+Re-implement the decision making for soft state polling. Instead of
+generating the soft state mask in sfp_soft_start_poll() by looking at
+which GPIOs are available, record their availability in
+sfp_sm_mod_probe() in sfp->state_hw_mask.
+
+This will then allow us to clear bits in sfp->state_hw_mask in module
+specific quirks when the hardware signals should not be used, thereby
+allowing us to switch to using the software state polling.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp.c | 38 ++++++++++++++++++++++++++------------
+ 1 file changed, 26 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -240,6 +240,7 @@ struct sfp {
+ bool need_poll;
+
+ struct mutex st_mutex; /* Protects state */
++ unsigned int state_hw_mask;
+ unsigned int state_soft_mask;
+ unsigned int state;
+ struct delayed_work poll;
+@@ -505,17 +506,18 @@ static void sfp_soft_set_state(struct sf
+ static void sfp_soft_start_poll(struct sfp *sfp)
+ {
+ const struct sfp_eeprom_id *id = &sfp->id;
++ unsigned int mask = 0;
+
+ sfp->state_soft_mask = 0;
+- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE &&
+- !sfp->gpio[GPIO_TX_DISABLE])
+- sfp->state_soft_mask |= SFP_F_TX_DISABLE;
+- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT &&
+- !sfp->gpio[GPIO_TX_FAULT])
+- sfp->state_soft_mask |= SFP_F_TX_FAULT;
+- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS &&
+- !sfp->gpio[GPIO_LOS])
+- sfp->state_soft_mask |= SFP_F_LOS;
++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE)
++ mask |= SFP_F_TX_DISABLE;
++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT)
++ mask |= SFP_F_TX_FAULT;
++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS)
++ mask |= SFP_F_LOS;
++
++ // Poll the soft state for hardware pins we want to ignore
++ sfp->state_soft_mask = ~sfp->state_hw_mask & mask;
+
+ if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
+ !sfp->need_poll)
+@@ -529,10 +531,11 @@ static void sfp_soft_stop_poll(struct sf
+
+ static unsigned int sfp_get_state(struct sfp *sfp)
+ {
+- unsigned int state = sfp->get_state(sfp);
++ unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT);
++ unsigned int state;
+
+- if (state & SFP_F_PRESENT &&
+- sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT))
++ state = sfp->get_state(sfp) & sfp->state_hw_mask;
++ if (state & SFP_F_PRESENT && soft)
+ state |= sfp_soft_get_state(sfp);
+
+ return state;
+@@ -1942,6 +1945,15 @@ static int sfp_sm_mod_probe(struct sfp *
+ if (ret < 0)
+ return ret;
+
++ /* Initialise state bits to use from hardware */
++ sfp->state_hw_mask = SFP_F_PRESENT;
++ if (sfp->gpio[GPIO_TX_DISABLE])
++ sfp->state_hw_mask |= SFP_F_TX_DISABLE;
++ if (sfp->gpio[GPIO_TX_FAULT])
++ sfp->state_hw_mask |= SFP_F_TX_FAULT;
++ if (sfp->gpio[GPIO_LOS])
++ sfp->state_hw_mask |= SFP_F_LOS;
++
+ if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) &&
+ !memcmp(id.base.vendor_pn, "3FE46541AA ", 16))
+ sfp->module_t_start_up = T_START_UP_BAD_GPON;
+@@ -2568,6 +2580,8 @@ static int sfp_probe(struct platform_dev
+ return PTR_ERR(sfp->gpio[i]);
+ }
+
++ sfp->state_hw_mask = SFP_F_PRESENT;
++
+ sfp->get_state = sfp_gpio_get_state;
+ sfp->set_state = sfp_gpio_set_state;
+
diff --git a/target/linux/generic/backport-6.1/784-v6.1-net-sfp-move-quirk-handling-into-sfp.c.patch b/target/linux/generic/backport-6.1/784-v6.1-net-sfp-move-quirk-handling-into-sfp.c.patch
new file mode 100644
index 0000000000..02fa28c5af
--- /dev/null
+++ b/target/linux/generic/backport-6.1/784-v6.1-net-sfp-move-quirk-handling-into-sfp.c.patch
@@ -0,0 +1,291 @@
+From 23571c7b96437483d28a990c906cc81f5f66374e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:32 +0100
+Subject: [PATCH 1/1] net: sfp: move quirk handling into sfp.c
+
+We need to handle more quirks than just those which affect the link
+modes of the module. Move the quirk lookup into sfp.c, and pass the
+quirk to sfp-bus.c
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp-bus.c | 98 ++-------------------------------------
+ drivers/net/phy/sfp.c | 94 ++++++++++++++++++++++++++++++++++++-
+ drivers/net/phy/sfp.h | 9 +++-
+ 3 files changed, 104 insertions(+), 97 deletions(-)
+
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -10,12 +10,6 @@
+
+ #include "sfp.h"
+
+-struct sfp_quirk {
+- const char *vendor;
+- const char *part;
+- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
+-};
+-
+ /**
+ * struct sfp_bus - internal representation of a sfp bus
+ */
+@@ -38,93 +32,6 @@ struct sfp_bus {
+ bool started;
+ };
+
+-static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
+- unsigned long *modes)
+-{
+- phylink_set(modes, 2500baseX_Full);
+-}
+-
+-static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
+- unsigned long *modes)
+-{
+- /* Ubiquiti U-Fiber Instant module claims that support all transceiver
+- * types including 10G Ethernet which is not truth. So clear all claimed
+- * modes and set only one mode which module supports: 1000baseX_Full.
+- */
+- phylink_zero(modes);
+- phylink_set(modes, 1000baseX_Full);
+-}
+-
+-static const struct sfp_quirk sfp_quirks[] = {
+- {
+- // Alcatel Lucent G-010S-P can operate at 2500base-X, but
+- // incorrectly report 2500MBd NRZ in their EEPROM
+- .vendor = "ALCATELLUCENT",
+- .part = "G010SP",
+- .modes = sfp_quirk_2500basex,
+- }, {
+- // Alcatel Lucent G-010S-A can operate at 2500base-X, but
+- // report 3.2GBd NRZ in their EEPROM
+- .vendor = "ALCATELLUCENT",
+- .part = "3FE46541AA",
+- .modes = sfp_quirk_2500basex,
+- }, {
+- // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
+- // NRZ in their EEPROM
+- .vendor = "HUAWEI",
+- .part = "MA5671A",
+- .modes = sfp_quirk_2500basex,
+- }, {
+- // Lantech 8330-262D-E can operate at 2500base-X, but
+- // incorrectly report 2500MBd NRZ in their EEPROM
+- .vendor = "Lantech",
+- .part = "8330-262D-E",
+- .modes = sfp_quirk_2500basex,
+- }, {
+- .vendor = "UBNT",
+- .part = "UF-INSTANT",
+- .modes = sfp_quirk_ubnt_uf_instant,
+- },
+-};
+-
+-static size_t sfp_strlen(const char *str, size_t maxlen)
+-{
+- size_t size, i;
+-
+- /* Trailing characters should be filled with space chars */
+- for (i = 0, size = 0; i < maxlen; i++)
+- if (str[i] != ' ')
+- size = i + 1;
+-
+- return size;
+-}
+-
+-static bool sfp_match(const char *qs, const char *str, size_t len)
+-{
+- if (!qs)
+- return true;
+- if (strlen(qs) != len)
+- return false;
+- return !strncmp(qs, str, len);
+-}
+-
+-static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
+-{
+- const struct sfp_quirk *q;
+- unsigned int i;
+- size_t vs, ps;
+-
+- vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name));
+- ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn));
+-
+- for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++)
+- if (sfp_match(q->vendor, id->base.vendor_name, vs) &&
+- sfp_match(q->part, id->base.vendor_pn, ps))
+- return q;
+-
+- return NULL;
+-}
+-
+ /**
+ * sfp_parse_port() - Parse the EEPROM base ID, setting the port type
+ * @bus: a pointer to the &struct sfp_bus structure for the sfp module
+@@ -786,12 +693,13 @@ void sfp_link_down(struct sfp_bus *bus)
+ }
+ EXPORT_SYMBOL_GPL(sfp_link_down);
+
+-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id)
++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
++ const struct sfp_quirk *quirk)
+ {
+ const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
+ int ret = 0;
+
+- bus->sfp_quirk = sfp_lookup_quirk(id);
++ bus->sfp_quirk = quirk;
+
+ if (ops && ops->module_insert)
+ ret = ops->module_insert(bus->upstream, id);
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -259,6 +259,8 @@ struct sfp {
+ unsigned int module_t_start_up;
+ bool tx_fault_ignore;
+
++ const struct sfp_quirk *quirk;
++
+ #if IS_ENABLED(CONFIG_HWMON)
+ struct sfp_diag diag;
+ struct delayed_work hwmon_probe;
+@@ -315,6 +317,93 @@ static const struct of_device_id sfp_of_
+ };
+ MODULE_DEVICE_TABLE(of, sfp_of_match);
+
++static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
++ unsigned long *modes)
++{
++ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes);
++}
++
++static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
++ unsigned long *modes)
++{
++ /* Ubiquiti U-Fiber Instant module claims that support all transceiver
++ * types including 10G Ethernet which is not truth. So clear all claimed
++ * modes and set only one mode which module supports: 1000baseX_Full.
++ */
++ linkmode_zero(modes);
++ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes);
++}
++
++static const struct sfp_quirk sfp_quirks[] = {
++ {
++ // Alcatel Lucent G-010S-P can operate at 2500base-X, but
++ // incorrectly report 2500MBd NRZ in their EEPROM
++ .vendor = "ALCATELLUCENT",
++ .part = "G010SP",
++ .modes = sfp_quirk_2500basex,
++ }, {
++ // Alcatel Lucent G-010S-A can operate at 2500base-X, but
++ // report 3.2GBd NRZ in their EEPROM
++ .vendor = "ALCATELLUCENT",
++ .part = "3FE46541AA",
++ .modes = sfp_quirk_2500basex,
++ }, {
++ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
++ // NRZ in their EEPROM
++ .vendor = "HUAWEI",
++ .part = "MA5671A",
++ .modes = sfp_quirk_2500basex,
++ }, {
++ // Lantech 8330-262D-E can operate at 2500base-X, but
++ // incorrectly report 2500MBd NRZ in their EEPROM
++ .vendor = "Lantech",
++ .part = "8330-262D-E",
++ .modes = sfp_quirk_2500basex,
++ }, {
++ .vendor = "UBNT",
++ .part = "UF-INSTANT",
++ .modes = sfp_quirk_ubnt_uf_instant,
++ },
++};
++
++static size_t sfp_strlen(const char *str, size_t maxlen)
++{
++ size_t size, i;
++
++ /* Trailing characters should be filled with space chars */
++ for (i = 0, size = 0; i < maxlen; i++)
++ if (str[i] != ' ')
++ size = i + 1;
++
++ return size;
++}
++
++static bool sfp_match(const char *qs, const char *str, size_t len)
++{
++ if (!qs)
++ return true;
++ if (strlen(qs) != len)
++ return false;
++ return !strncmp(qs, str, len);
++}
++
++static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
++{
++ const struct sfp_quirk *q;
++ unsigned int i;
++ size_t vs, ps;
++
++ vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name));
++ ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn));
++
++ for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++)
++ if (sfp_match(q->vendor, id->base.vendor_name, vs) &&
++ sfp_match(q->part, id->base.vendor_pn, ps))
++ return q;
++
++ return NULL;
++}
++
+ static unsigned long poll_jiffies;
+
+ static unsigned int sfp_gpio_get_state(struct sfp *sfp)
+@@ -1966,6 +2055,8 @@ static int sfp_sm_mod_probe(struct sfp *
+ else
+ sfp->tx_fault_ignore = false;
+
++ sfp->quirk = sfp_lookup_quirk(&id);
++
+ return 0;
+ }
+
+@@ -2077,7 +2168,8 @@ static void sfp_sm_module(struct sfp *sf
+ break;
+
+ /* Report the module insertion to the upstream device */
+- err = sfp_module_insert(sfp->sfp_bus, &sfp->id);
++ err = sfp_module_insert(sfp->sfp_bus, &sfp->id,
++ sfp->quirk);
+ if (err < 0) {
+ sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
+ break;
+--- a/drivers/net/phy/sfp.h
++++ b/drivers/net/phy/sfp.h
+@@ -6,6 +6,12 @@
+
+ struct sfp;
+
++struct sfp_quirk {
++ const char *vendor;
++ const char *part;
++ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
++};
++
+ struct sfp_socket_ops {
+ void (*attach)(struct sfp *sfp);
+ void (*detach)(struct sfp *sfp);
+@@ -23,7 +29,8 @@ int sfp_add_phy(struct sfp_bus *bus, str
+ void sfp_remove_phy(struct sfp_bus *bus);
+ void sfp_link_up(struct sfp_bus *bus);
+ void sfp_link_down(struct sfp_bus *bus);
+-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id);
++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
++ const struct sfp_quirk *quirk);
+ void sfp_module_remove(struct sfp_bus *bus);
+ int sfp_module_start(struct sfp_bus *bus);
+ void sfp_module_stop(struct sfp_bus *bus);
diff --git a/target/linux/generic/backport-6.1/785-v6.1-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch b/target/linux/generic/backport-6.1/785-v6.1-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch
new file mode 100644
index 0000000000..b076676cff
--- /dev/null
+++ b/target/linux/generic/backport-6.1/785-v6.1-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch
@@ -0,0 +1,69 @@
+From 275416754e9a262c97a1ad6f806a4bc6e0464aa2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:37 +0100
+Subject: [PATCH 1/1] net: sfp: move Alcatel Lucent 3FE46541AA fixup
+
+Add a new fixup mechanism to the SFP quirks, and use it for this
+module.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp.c | 14 +++++++++-----
+ drivers/net/phy/sfp.h | 1 +
+ 2 files changed, 10 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -317,6 +317,11 @@ static const struct of_device_id sfp_of_
+ };
+ MODULE_DEVICE_TABLE(of, sfp_of_match);
+
++static void sfp_fixup_long_startup(struct sfp *sfp)
++{
++ sfp->module_t_start_up = T_START_UP_BAD_GPON;
++}
++
+ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
+ unsigned long *modes)
+ {
+@@ -347,6 +352,7 @@ static const struct sfp_quirk sfp_quirks
+ .vendor = "ALCATELLUCENT",
+ .part = "3FE46541AA",
+ .modes = sfp_quirk_2500basex,
++ .fixup = sfp_fixup_long_startup,
+ }, {
+ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
+ // NRZ in their EEPROM
+@@ -2043,11 +2049,7 @@ static int sfp_sm_mod_probe(struct sfp *
+ if (sfp->gpio[GPIO_LOS])
+ sfp->state_hw_mask |= SFP_F_LOS;
+
+- if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) &&
+- !memcmp(id.base.vendor_pn, "3FE46541AA ", 16))
+- sfp->module_t_start_up = T_START_UP_BAD_GPON;
+- else
+- sfp->module_t_start_up = T_START_UP;
++ sfp->module_t_start_up = T_START_UP;
+
+ if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) &&
+ !memcmp(id.base.vendor_pn, "MA5671A ", 16))
+@@ -2056,6 +2058,8 @@ static int sfp_sm_mod_probe(struct sfp *
+ sfp->tx_fault_ignore = false;
+
+ sfp->quirk = sfp_lookup_quirk(&id);
++ if (sfp->quirk && sfp->quirk->fixup)
++ sfp->quirk->fixup(sfp);
+
+ return 0;
+ }
+--- a/drivers/net/phy/sfp.h
++++ b/drivers/net/phy/sfp.h
+@@ -10,6 +10,7 @@ struct sfp_quirk {
+ const char *vendor;
+ const char *part;
+ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
++ void (*fixup)(struct sfp *sfp);
+ };
+
+ struct sfp_socket_ops {
diff --git a/target/linux/generic/backport-6.1/786-v6.1-net-sfp-move-Huawei-MA5671A-fixup.patch b/target/linux/generic/backport-6.1/786-v6.1-net-sfp-move-Huawei-MA5671A-fixup.patch
new file mode 100644
index 0000000000..7f856e5b5b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/786-v6.1-net-sfp-move-Huawei-MA5671A-fixup.patch
@@ -0,0 +1,48 @@
+From 5029be761161374a3624aa7b4670174c35449bf5 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:42 +0100
+Subject: [PATCH 1/1] net: sfp: move Huawei MA5671A fixup
+
+Move this module over to the new fixup mechanism.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -322,6 +322,11 @@ static void sfp_fixup_long_startup(struc
+ sfp->module_t_start_up = T_START_UP_BAD_GPON;
+ }
+
++static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
++{
++ sfp->tx_fault_ignore = true;
++}
++
+ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
+ unsigned long *modes)
+ {
+@@ -359,6 +364,7 @@ static const struct sfp_quirk sfp_quirks
+ .vendor = "HUAWEI",
+ .part = "MA5671A",
+ .modes = sfp_quirk_2500basex,
++ .fixup = sfp_fixup_ignore_tx_fault,
+ }, {
+ // Lantech 8330-262D-E can operate at 2500base-X, but
+ // incorrectly report 2500MBd NRZ in their EEPROM
+@@ -2051,11 +2057,7 @@ static int sfp_sm_mod_probe(struct sfp *
+
+ sfp->module_t_start_up = T_START_UP;
+
+- if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) &&
+- !memcmp(id.base.vendor_pn, "MA5671A ", 16))
+- sfp->tx_fault_ignore = true;
+- else
+- sfp->tx_fault_ignore = false;
++ sfp->tx_fault_ignore = false;
+
+ sfp->quirk = sfp_lookup_quirk(&id);
+ if (sfp->quirk && sfp->quirk->fixup)
diff --git a/target/linux/generic/backport-6.1/787-v6.1-net-sfp-add-support-for-HALNy-GPON-SFP.patch b/target/linux/generic/backport-6.1/787-v6.1-net-sfp-add-support-for-HALNy-GPON-SFP.patch
new file mode 100644
index 0000000000..81108e19c1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/787-v6.1-net-sfp-add-support-for-HALNy-GPON-SFP.patch
@@ -0,0 +1,79 @@
+From 73472c830eae5fce2107f7f086f1e6827d215caf Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:48 +0100
+Subject: [PATCH 1/1] net: sfp: add support for HALNy GPON SFP
+
+Add a quirk for the HALNy HL-GSFP module, which appears to have an
+inverted RX_LOS signal, and maybe uses TX_FAULT as a serial port
+transmit pin. Rather than use these hardware signals, switch to
+using software polling for these status signals.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp-bus.c | 2 +-
+ drivers/net/phy/sfp.c | 21 ++++++++++++++++++---
+ 2 files changed, 19 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -283,7 +283,7 @@ void sfp_parse_support(struct sfp_bus *b
+ phylink_set(modes, 2500baseX_Full);
+ }
+
+- if (bus->sfp_quirk)
++ if (bus->sfp_quirk && bus->sfp_quirk->modes)
+ bus->sfp_quirk->modes(id, modes);
+
+ linkmode_or(support, support, modes);
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -327,6 +327,15 @@ static void sfp_fixup_ignore_tx_fault(st
+ sfp->tx_fault_ignore = true;
+ }
+
++static void sfp_fixup_halny_gsfp(struct sfp *sfp)
++{
++ /* Ignore the TX_FAULT and LOS signals on this module.
++ * these are possibly used for other purposes on this
++ * module, e.g. a serial port.
++ */
++ sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS);
++}
++
+ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
+ unsigned long *modes)
+ {
+@@ -359,6 +368,10 @@ static const struct sfp_quirk sfp_quirks
+ .modes = sfp_quirk_2500basex,
+ .fixup = sfp_fixup_long_startup,
+ }, {
++ .vendor = "HALNy",
++ .part = "HL-GSFP",
++ .fixup = sfp_fixup_halny_gsfp,
++ }, {
+ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
+ // NRZ in their EEPROM
+ .vendor = "HUAWEI",
+@@ -375,16 +388,18 @@ static const struct sfp_quirk sfp_quirks
+ .vendor = "UBNT",
+ .part = "UF-INSTANT",
+ .modes = sfp_quirk_ubnt_uf_instant,
+- },
++ }
+ };
+
+ static size_t sfp_strlen(const char *str, size_t maxlen)
+ {
+ size_t size, i;
+
+- /* Trailing characters should be filled with space chars */
++ /* Trailing characters should be filled with space chars, but
++ * some manufacturers can't read SFF-8472 and use NUL.
++ */
+ for (i = 0, size = 0; i < maxlen; i++)
+- if (str[i] != ' ')
++ if (str[i] != ' ' && str[i] != '\0')
+ size = i + 1;
+
+ return size;
diff --git a/target/linux/generic/backport-6.1/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch b/target/linux/generic/backport-6.1/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch
new file mode 100644
index 0000000000..ee12399140
--- /dev/null
+++ b/target/linux/generic/backport-6.1/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch
@@ -0,0 +1,514 @@
+From patchwork Thu Mar 9 10:57:44 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
+X-Patchwork-Id: 13167235
+X-Patchwork-Delegate: kuba@kernel.org
+Return-Path: <netdev-owner@vger.kernel.org>
+Date: Thu, 9 Mar 2023 10:57:44 +0000
+From: Daniel Golle <daniel@makrotopia.org>
+To: netdev@vger.kernel.org, linux-mediatek@lists.infradead.org,
+ linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,
+ Russell King <linux@armlinux.org.uk>,
+ Heiner Kallweit <hkallweit1@gmail.com>,
+ Lorenzo Bianconi <lorenzo@kernel.org>,
+ Mark Lee <Mark-MC.Lee@mediatek.com>,
+ John Crispin <john@phrozen.org>, Felix Fietkau <nbd@nbd.name>,
+ AngeloGioacchino Del Regno
+ <angelogioacchino.delregno@collabora.com>,
+ Matthias Brugger <matthias.bgg@gmail.com>,
+ DENG Qingfang <dqfext@gmail.com>,
+ Landen Chao <Landen.Chao@mediatek.com>,
+ Sean Wang <sean.wang@mediatek.com>,
+ Paolo Abeni <pabeni@redhat.com>,
+ Jakub Kicinski <kuba@kernel.org>,
+ Eric Dumazet <edumazet@google.com>,
+ "David S. Miller" <davem@davemloft.net>,
+ Vladimir Oltean <olteanv@gmail.com>,
+ Florian Fainelli <f.fainelli@gmail.com>,
+ Andrew Lunn <andrew@lunn.ch>,
+ Vladimir Oltean <vladimir.oltean@nxp.com>
+Cc: =?iso-8859-1?q?Bj=F8rn?= Mork <bjorn@mork.no>,
+ Frank Wunderlich <frank-w@public-files.de>,
+ Alexander Couzens <lynxis@fe80.eu>
+Subject: [PATCH net-next v13 11/16] net: dsa: mt7530: use external PCS driver
+Message-ID:
+ <2ac2ee40d3b0e705461b50613fda6a7edfdbc4b3.1678357225.git.daniel@makrotopia.org>
+References: <cover.1678357225.git.daniel@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+In-Reply-To: <cover.1678357225.git.daniel@makrotopia.org>
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+Implement regmap access wrappers, for now only to be used by the
+pcs-mtk driver.
+Make use of external PCS driver and drop the reduntant implementation
+in mt7530.c.
+As a nice side effect the SGMII registers can now also more easily be
+inspected for debugging via /sys/kernel/debug/regmap.
+
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Tested-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Frank Wunderlich <frank-w@public-files.de>
+---
+ drivers/net/dsa/Kconfig | 1 +
+ drivers/net/dsa/mt7530.c | 277 ++++++++++-----------------------------
+ drivers/net/dsa/mt7530.h | 47 +------
+ 3 files changed, 71 insertions(+), 254 deletions(-)
+
+--- a/drivers/net/dsa/Kconfig
++++ b/drivers/net/dsa/Kconfig
+@@ -37,6 +37,7 @@ config NET_DSA_MT7530
+ tristate "MediaTek MT753x and MT7621 Ethernet switch support"
+ select NET_DSA_TAG_MTK
+ select MEDIATEK_GE_PHY
++ select PCS_MTK_LYNXI
+ help
+ This enables support for the MediaTek MT7530, MT7531, and MT7621
+ Ethernet switch chips.
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -14,6 +14,7 @@
+ #include <linux/of_mdio.h>
+ #include <linux/of_net.h>
+ #include <linux/of_platform.h>
++#include <linux/pcs/pcs-mtk-lynxi.h>
+ #include <linux/phylink.h>
+ #include <linux/regmap.h>
+ #include <linux/regulator/consumer.h>
+@@ -2573,128 +2574,11 @@ static int mt7531_rgmii_setup(struct mt7
+ return 0;
+ }
+
+-static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+- phy_interface_t interface, int speed, int duplex)
+-{
+- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
+- int port = pcs_to_mt753x_pcs(pcs)->port;
+- unsigned int val;
+-
+- /* For adjusting speed and duplex of SGMII force mode. */
+- if (interface != PHY_INTERFACE_MODE_SGMII ||
+- phylink_autoneg_inband(mode))
+- return;
+-
+- /* SGMII force mode setting */
+- val = mt7530_read(priv, MT7531_SGMII_MODE(port));
+- val &= ~MT7531_SGMII_IF_MODE_MASK;
+-
+- switch (speed) {
+- case SPEED_10:
+- val |= MT7531_SGMII_FORCE_SPEED_10;
+- break;
+- case SPEED_100:
+- val |= MT7531_SGMII_FORCE_SPEED_100;
+- break;
+- case SPEED_1000:
+- val |= MT7531_SGMII_FORCE_SPEED_1000;
+- break;
+- }
+-
+- /* MT7531 SGMII 1G force mode can only work in full duplex mode,
+- * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
+- *
+- * The speed check is unnecessary as the MAC capabilities apply
+- * this restriction. --rmk
+- */
+- if ((speed == SPEED_10 || speed == SPEED_100) &&
+- duplex != DUPLEX_FULL)
+- val |= MT7531_SGMII_FORCE_HALF_DUPLEX;
+-
+- mt7530_write(priv, MT7531_SGMII_MODE(port), val);
+-}
+-
+ static bool mt753x_is_mac_port(u32 port)
+ {
+ return (port == 5 || port == 6);
+ }
+
+-static int mt7531_sgmii_setup_mode_force(struct mt7530_priv *priv, u32 port,
+- phy_interface_t interface)
+-{
+- u32 val;
+-
+- if (!mt753x_is_mac_port(port))
+- return -EINVAL;
+-
+- mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
+- MT7531_SGMII_PHYA_PWD);
+-
+- val = mt7530_read(priv, MT7531_PHYA_CTRL_SIGNAL3(port));
+- val &= ~MT7531_RG_TPHY_SPEED_MASK;
+- /* Setup 2.5 times faster clock for 2.5Gbps data speeds with 10B/8B
+- * encoding.
+- */
+- val |= (interface == PHY_INTERFACE_MODE_2500BASEX) ?
+- MT7531_RG_TPHY_SPEED_3_125G : MT7531_RG_TPHY_SPEED_1_25G;
+- mt7530_write(priv, MT7531_PHYA_CTRL_SIGNAL3(port), val);
+-
+- mt7530_clear(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE);
+-
+- /* MT7531 SGMII 1G and 2.5G force mode can only work in full duplex
+- * mode, no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not.
+- */
+- mt7530_rmw(priv, MT7531_SGMII_MODE(port),
+- MT7531_SGMII_IF_MODE_MASK | MT7531_SGMII_REMOTE_FAULT_DIS,
+- MT7531_SGMII_FORCE_SPEED_1000);
+-
+- mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0);
+-
+- return 0;
+-}
+-
+-static int mt7531_sgmii_setup_mode_an(struct mt7530_priv *priv, int port,
+- phy_interface_t interface)
+-{
+- if (!mt753x_is_mac_port(port))
+- return -EINVAL;
+-
+- mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
+- MT7531_SGMII_PHYA_PWD);
+-
+- mt7530_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port),
+- MT7531_RG_TPHY_SPEED_MASK, MT7531_RG_TPHY_SPEED_1_25G);
+-
+- mt7530_set(priv, MT7531_SGMII_MODE(port),
+- MT7531_SGMII_REMOTE_FAULT_DIS |
+- MT7531_SGMII_SPEED_DUPLEX_AN);
+-
+- mt7530_rmw(priv, MT7531_PCS_SPEED_ABILITY(port),
+- MT7531_SGMII_TX_CONFIG_MASK, 1);
+-
+- mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE);
+-
+- mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_RESTART);
+-
+- mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0);
+-
+- return 0;
+-}
+-
+-static void mt7531_pcs_an_restart(struct phylink_pcs *pcs)
+-{
+- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
+- int port = pcs_to_mt753x_pcs(pcs)->port;
+- u32 val;
+-
+- /* Only restart AN when AN is enabled */
+- val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
+- if (val & MT7531_SGMII_AN_ENABLE) {
+- val |= MT7531_SGMII_AN_RESTART;
+- mt7530_write(priv, MT7531_PCS_CONTROL_1(port), val);
+- }
+-}
+-
+ static int
+ mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+ phy_interface_t interface)
+@@ -2717,11 +2601,11 @@ mt7531_mac_config(struct dsa_switch *ds,
+ phydev = dp->slave->phydev;
+ return mt7531_rgmii_setup(priv, port, interface, phydev);
+ case PHY_INTERFACE_MODE_SGMII:
+- return mt7531_sgmii_setup_mode_an(priv, port, interface);
+ case PHY_INTERFACE_MODE_NA:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+- return mt7531_sgmii_setup_mode_force(priv, port, interface);
++ /* handled in SGMII PCS driver */
++ return 0;
+ default:
+ return -EINVAL;
+ }
+@@ -2746,11 +2630,11 @@ mt753x_phylink_mac_select_pcs(struct dsa
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_TRGMII:
++ return &priv->pcs[port].pcs;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+- return &priv->pcs[port].pcs;
+-
++ return priv->ports[port].sgmii_pcs;
+ default:
+ return NULL;
+ }
+@@ -2991,86 +2875,6 @@ static void mt7530_pcs_get_state(struct
+ state->pause |= MLO_PAUSE_TX;
+ }
+
+-static int
+-mt7531_sgmii_pcs_get_state_an(struct mt7530_priv *priv, int port,
+- struct phylink_link_state *state)
+-{
+- u32 status, val;
+- u16 config_reg;
+-
+- status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
+- state->link = !!(status & MT7531_SGMII_LINK_STATUS);
+- state->an_complete = !!(status & MT7531_SGMII_AN_COMPLETE);
+- if (state->interface == PHY_INTERFACE_MODE_SGMII &&
+- (status & MT7531_SGMII_AN_ENABLE)) {
+- val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port));
+- config_reg = val >> 16;
+-
+- switch (config_reg & LPA_SGMII_SPD_MASK) {
+- case LPA_SGMII_1000:
+- state->speed = SPEED_1000;
+- break;
+- case LPA_SGMII_100:
+- state->speed = SPEED_100;
+- break;
+- case LPA_SGMII_10:
+- state->speed = SPEED_10;
+- break;
+- default:
+- dev_err(priv->dev, "invalid sgmii PHY speed\n");
+- state->link = false;
+- return -EINVAL;
+- }
+-
+- if (config_reg & LPA_SGMII_FULL_DUPLEX)
+- state->duplex = DUPLEX_FULL;
+- else
+- state->duplex = DUPLEX_HALF;
+- }
+-
+- return 0;
+-}
+-
+-static void
+-mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port,
+- struct phylink_link_state *state)
+-{
+- unsigned int val;
+-
+- val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
+- state->link = !!(val & MT7531_SGMII_LINK_STATUS);
+- if (!state->link)
+- return;
+-
+- state->an_complete = state->link;
+-
+- if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
+- state->speed = SPEED_2500;
+- else
+- state->speed = SPEED_1000;
+-
+- state->duplex = DUPLEX_FULL;
+- state->pause = MLO_PAUSE_NONE;
+-}
+-
+-static void mt7531_pcs_get_state(struct phylink_pcs *pcs,
+- struct phylink_link_state *state)
+-{
+- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
+- int port = pcs_to_mt753x_pcs(pcs)->port;
+-
+- if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+- mt7531_sgmii_pcs_get_state_an(priv, port, state);
+- return;
+- } else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) ||
+- (state->interface == PHY_INTERFACE_MODE_2500BASEX)) {
+- mt7531_sgmii_pcs_get_state_inband(priv, port, state);
+- return;
+- }
+-
+- state->link = false;
+-}
+-
+ static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+@@ -3090,18 +2894,57 @@ static const struct phylink_pcs_ops mt75
+ .pcs_an_restart = mt7530_pcs_an_restart,
+ };
+
+-static const struct phylink_pcs_ops mt7531_pcs_ops = {
+- .pcs_validate = mt753x_pcs_validate,
+- .pcs_get_state = mt7531_pcs_get_state,
+- .pcs_config = mt753x_pcs_config,
+- .pcs_an_restart = mt7531_pcs_an_restart,
+- .pcs_link_up = mt7531_pcs_link_up,
++static int mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
++{
++ struct mt7530_priv *priv = context;
++
++ *val = mt7530_read(priv, reg);
++ return 0;
++};
++
++static int mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
++{
++ struct mt7530_priv *priv = context;
++
++ mt7530_write(priv, reg, val);
++ return 0;
++};
++
++static int mt7530_regmap_update_bits(void *context, unsigned int reg,
++ unsigned int mask, unsigned int val)
++{
++ struct mt7530_priv *priv = context;
++
++ mt7530_rmw(priv, reg, mask, val);
++ return 0;
++};
++
++static const struct regmap_bus mt7531_regmap_bus = {
++ .reg_write = mt7530_regmap_write,
++ .reg_read = mt7530_regmap_read,
++ .reg_update_bits = mt7530_regmap_update_bits,
++};
++
++#define MT7531_PCS_REGMAP_CONFIG(_name, _reg_base) \
++ { \
++ .name = _name, \
++ .reg_bits = 16, \
++ .val_bits = 32, \
++ .reg_stride = 4, \
++ .reg_base = _reg_base, \
++ .max_register = 0x17c, \
++ }
++
++static const struct regmap_config mt7531_pcs_config[] = {
++ MT7531_PCS_REGMAP_CONFIG("port5", MT7531_SGMII_REG_BASE(5)),
++ MT7531_PCS_REGMAP_CONFIG("port6", MT7531_SGMII_REG_BASE(6)),
+ };
+
+ static int
+ mt753x_setup(struct dsa_switch *ds)
+ {
+ struct mt7530_priv *priv = ds->priv;
++ struct regmap *regmap;
+ int i, ret;
+
+ /* Initialise the PCS devices */
+@@ -3109,8 +2952,6 @@ mt753x_setup(struct dsa_switch *ds)
+ priv->pcs[i].pcs.ops = priv->info->pcs_ops;
+ priv->pcs[i].priv = priv;
+ priv->pcs[i].port = i;
+- if (mt753x_is_mac_port(i))
+- priv->pcs[i].pcs.poll = 1;
+ }
+
+ ret = priv->info->sw_setup(ds);
+@@ -3125,6 +2966,16 @@ mt753x_setup(struct dsa_switch *ds)
+ if (ret && priv->irq)
+ mt7530_free_irq_common(priv);
+
++ if (priv->id == ID_MT7531)
++ for (i = 0; i < 2; i++) {
++ regmap = devm_regmap_init(ds->dev,
++ &mt7531_regmap_bus, priv,
++ &mt7531_pcs_config[i]);
++ priv->ports[5 + i].sgmii_pcs =
++ mtk_pcs_lynxi_create(ds->dev, regmap,
++ MT7531_PHYA_CTRL_SIGNAL3, 0);
++ }
++
+ return ret;
+ }
+
+@@ -3216,7 +3067,7 @@ static const struct mt753x_info mt753x_t
+ },
+ [ID_MT7531] = {
+ .id = ID_MT7531,
+- .pcs_ops = &mt7531_pcs_ops,
++ .pcs_ops = &mt7530_pcs_ops,
+ .sw_setup = mt7531_setup,
+ .phy_read = mt7531_ind_phy_read,
+ .phy_write = mt7531_ind_phy_write,
+@@ -3324,7 +3175,7 @@ static void
+ mt7530_remove(struct mdio_device *mdiodev)
+ {
+ struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
+- int ret = 0;
++ int ret = 0, i;
+
+ if (!priv)
+ return;
+@@ -3343,6 +3194,10 @@ mt7530_remove(struct mdio_device *mdiode
+ mt7530_free_irq(priv);
+
+ dsa_unregister_switch(priv->ds);
++
++ for (i = 0; i < 2; ++i)
++ mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
++
+ mutex_destroy(&priv->reg_mutex);
+
+ dev_set_drvdata(&mdiodev->dev, NULL);
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -364,47 +364,8 @@ enum mt7530_vlan_port_acc_frm {
+ CCR_TX_OCT_CNT_BAD)
+
+ /* MT7531 SGMII register group */
+-#define MT7531_SGMII_REG_BASE 0x5000
+-#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \
+- ((p) - 5) * 0x1000 + (r))
+-
+-/* Register forSGMII PCS_CONTROL_1 */
+-#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(p, 0x00)
+-#define MT7531_SGMII_LINK_STATUS BIT(18)
+-#define MT7531_SGMII_AN_ENABLE BIT(12)
+-#define MT7531_SGMII_AN_RESTART BIT(9)
+-#define MT7531_SGMII_AN_COMPLETE BIT(21)
+-
+-/* Register for SGMII PCS_SPPED_ABILITY */
+-#define MT7531_PCS_SPEED_ABILITY(p) MT7531_SGMII_REG(p, 0x08)
+-#define MT7531_SGMII_TX_CONFIG_MASK GENMASK(15, 0)
+-#define MT7531_SGMII_TX_CONFIG BIT(0)
+-
+-/* Register for SGMII_MODE */
+-#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(p, 0x20)
+-#define MT7531_SGMII_REMOTE_FAULT_DIS BIT(8)
+-#define MT7531_SGMII_IF_MODE_MASK GENMASK(5, 1)
+-#define MT7531_SGMII_FORCE_DUPLEX BIT(4)
+-#define MT7531_SGMII_FORCE_SPEED_MASK GENMASK(3, 2)
+-#define MT7531_SGMII_FORCE_SPEED_1000 BIT(3)
+-#define MT7531_SGMII_FORCE_SPEED_100 BIT(2)
+-#define MT7531_SGMII_FORCE_SPEED_10 0
+-#define MT7531_SGMII_SPEED_DUPLEX_AN BIT(1)
+-
+-enum mt7531_sgmii_force_duplex {
+- MT7531_SGMII_FORCE_FULL_DUPLEX = 0,
+- MT7531_SGMII_FORCE_HALF_DUPLEX = 0x10,
+-};
+-
+-/* Fields of QPHY_PWR_STATE_CTRL */
+-#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(p, 0xe8)
+-#define MT7531_SGMII_PHYA_PWD BIT(4)
+-
+-/* Values of SGMII SPEED */
+-#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(p, 0x128)
+-#define MT7531_RG_TPHY_SPEED_MASK (BIT(2) | BIT(3))
+-#define MT7531_RG_TPHY_SPEED_1_25G 0x0
+-#define MT7531_RG_TPHY_SPEED_3_125G BIT(2)
++#define MT7531_SGMII_REG_BASE(p) (0x5000 + ((p) - 5) * 0x1000)
++#define MT7531_PHYA_CTRL_SIGNAL3 0x128
+
+ /* Register for system reset */
+ #define MT7530_SYS_CTRL 0x7000
+@@ -703,13 +664,13 @@ struct mt7530_fdb {
+ * @pm: The matrix used to show all connections with the port.
+ * @pvid: The VLAN specified is to be considered a PVID at ingress. Any
+ * untagged frames will be assigned to the related VLAN.
+- * @vlan_filtering: The flags indicating whether the port that can recognize
+- * VLAN-tagged frames.
++ * @sgmii_pcs: Pointer to PCS instance for SerDes ports
+ */
+ struct mt7530_port {
+ bool enable;
+ u32 pm;
+ u16 pvid;
++ struct phylink_pcs *sgmii_pcs;
+ };
+
+ /* Port 5 interface select definitions */
diff --git a/target/linux/generic/backport-6.1/789-v6.3-net-sfp-add-quirk-enabling-2500Base-x-for-HG-MXPD-48.patch b/target/linux/generic/backport-6.1/789-v6.3-net-sfp-add-quirk-enabling-2500Base-x-for-HG-MXPD-48.patch
new file mode 100644
index 0000000000..413c73f547
--- /dev/null
+++ b/target/linux/generic/backport-6.1/789-v6.3-net-sfp-add-quirk-enabling-2500Base-x-for-HG-MXPD-48.patch
@@ -0,0 +1,37 @@
+From ad651d68cee75e9ac20002254c4e5d09ee67a84b Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 2 Apr 2023 12:44:37 +0100
+Subject: [PATCH] net: sfp: add quirk enabling 2500Base-x for HG MXPD-483II
+
+The HG MXPD-483II 1310nm SFP module is meant to operate with 2500Base-X,
+however, in their EEPROM they incorrectly specify:
+ Transceiver type : Ethernet: 1000BASE-LX
+ ...
+ BR, Nominal : 2600MBd
+
+Use sfp_quirk_2500basex for this module to allow 2500Base-X mode anyway.
+
+https://forum.banana-pi.org/t/bpi-r3-sfp-module-compatibility/14573/60
+
+Reported-by: chowtom <chowtom@gmail.com>
+Tested-by: chowtom <chowtom@gmail.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/sfp.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -372,6 +372,10 @@ static const struct sfp_quirk sfp_quirks
+ .part = "HL-GSFP",
+ .fixup = sfp_fixup_halny_gsfp,
+ }, {
++ .vendor = "HG GENUINE",
++ .part = "MXPD-483II",
++ .modes = sfp_quirk_2500basex,
++ }, {
+ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
+ // NRZ in their EEPROM
+ .vendor = "HUAWEI",
diff --git a/target/linux/generic/backport-6.1/790-v6.0-net-mii-add-mii_bmcr_encode_fixed.patch b/target/linux/generic/backport-6.1/790-v6.0-net-mii-add-mii_bmcr_encode_fixed.patch
new file mode 100644
index 0000000000..87d7ff6154
--- /dev/null
+++ b/target/linux/generic/backport-6.1/790-v6.0-net-mii-add-mii_bmcr_encode_fixed.patch
@@ -0,0 +1,55 @@
+From bdb6cfe7512f7a214815a3092f0be50963dcacbc Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Sat, 18 Jun 2022 11:28:32 +0100
+Subject: [PATCH] net: mii: add mii_bmcr_encode_fixed()
+
+Add a function to encode a fixed speed/duplex to a BMCR value.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/mii.h | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+--- a/include/linux/mii.h
++++ b/include/linux/mii.h
+@@ -595,4 +595,39 @@ static inline u8 mii_resolve_flowctrl_fd
+ return cap;
+ }
+
++/**
++ * mii_bmcr_encode_fixed - encode fixed speed/duplex settings to a BMCR value
++ * @speed: a SPEED_* value
++ * @duplex: a DUPLEX_* value
++ *
++ * Encode the speed and duplex to a BMCR value. 2500, 1000, 100 and 10 Mbps are
++ * supported. 2500Mbps is encoded to 1000Mbps. Other speeds are encoded as 10
++ * Mbps. Unknown duplex values are encoded to half-duplex.
++ */
++static inline u16 mii_bmcr_encode_fixed(int speed, int duplex)
++{
++ u16 bmcr;
++
++ switch (speed) {
++ case SPEED_2500:
++ case SPEED_1000:
++ bmcr = BMCR_SPEED1000;
++ break;
++
++ case SPEED_100:
++ bmcr = BMCR_SPEED100;
++ break;
++
++ case SPEED_10:
++ default:
++ bmcr = BMCR_SPEED10;
++ break;
++ }
++
++ if (duplex == DUPLEX_FULL)
++ bmcr |= BMCR_FULLDPLX;
++
++ return bmcr;
++}
++
+ #endif /* __LINUX_MII_H__ */
diff --git a/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch b/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch
new file mode 100644
index 0000000000..57137c34fc
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch
@@ -0,0 +1,1724 @@
+From 70479a40954cf353e87a486997a3477108c75aa9 Mon Sep 17 00:00:00 2001
+From: Frank <Frank.Sae@motor-comm.com>
+Date: Fri, 28 Oct 2022 17:26:21 +0800
+Subject: [PATCH] net: phy: Add driver for Motorcomm yt8521 gigabit ethernet
+ phy
+
+Add a driver for the motorcomm yt8521 gigabit ethernet phy. We have verified
+ the driver on StarFive VisionFive development board, which is developed by
+ Shanghai StarFive Technology Co., Ltd.. On the board, yt8521 gigabit ethernet
+ phy works in utp mode, RGMII interface, supports 1000M/100M/10M speeds, and
+ wol(magic package).
+
+Signed-off-by: Frank <Frank.Sae@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ MAINTAINERS | 1 +
+ drivers/net/phy/Kconfig | 2 +-
+ drivers/net/phy/motorcomm.c | 1635 ++++++++++++++++++++++++++++++++++-
+ 3 files changed, 1635 insertions(+), 3 deletions(-)
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -12697,6 +12697,7 @@ F: include/uapi/linux/meye.h
+
+ MOTORCOMM PHY DRIVER
+ M: Peter Geis <pgwipeout@gmail.com>
++M: Frank <Frank.Sae@motor-comm.com>
+ L: netdev@vger.kernel.org
+ S: Maintained
+ F: drivers/net/phy/motorcomm.c
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -245,7 +245,7 @@ config MOTORCOMM_PHY
+ tristate "Motorcomm PHYs"
+ help
+ Enables support for Motorcomm network PHYs.
+- Currently supports the YT8511 gigabit PHY.
++ Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs.
+
+ config NATIONAL_PHY
+ tristate "National Semiconductor PHYs"
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1,15 +1,106 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+- * Driver for Motorcomm PHYs
++ * Motorcomm 8511/8521 PHY driver.
+ *
+ * Author: Peter Geis <pgwipeout@gmail.com>
++ * Author: Frank <Frank.Sae@motor-comm.com>
+ */
+
++#include <linux/etherdevice.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/phy.h>
+
+ #define PHY_ID_YT8511 0x0000010a
++#define PHY_ID_YT8521 0x0000011A
++
++/* YT8521 Register Overview
++ * UTP Register space | FIBER Register space
++ * ------------------------------------------------------------
++ * | UTP MII | FIBER MII |
++ * | UTP MMD | |
++ * | UTP Extended | FIBER Extended |
++ * ------------------------------------------------------------
++ * | Common Extended |
++ * ------------------------------------------------------------
++ */
++
++/* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */
++
++/* Specific Function Control Register */
++#define YTPHY_SPECIFIC_FUNCTION_CONTROL_REG 0x10
++
++/* 2b00 Manual MDI configuration
++ * 2b01 Manual MDIX configuration
++ * 2b10 Reserved
++ * 2b11 Enable automatic crossover for all modes *default*
++ */
++#define YTPHY_SFCR_MDI_CROSSOVER_MODE_MASK (BIT(6) | BIT(5))
++#define YTPHY_SFCR_CROSSOVER_EN BIT(3)
++#define YTPHY_SFCR_SQE_TEST_EN BIT(2)
++#define YTPHY_SFCR_POLARITY_REVERSAL_EN BIT(1)
++#define YTPHY_SFCR_JABBER_DIS BIT(0)
++
++/* Specific Status Register */
++#define YTPHY_SPECIFIC_STATUS_REG 0x11
++#define YTPHY_SSR_SPEED_MODE_OFFSET 14
++
++#define YTPHY_SSR_SPEED_MODE_MASK (BIT(15) | BIT(14))
++#define YTPHY_SSR_SPEED_10M 0x0
++#define YTPHY_SSR_SPEED_100M 0x1
++#define YTPHY_SSR_SPEED_1000M 0x2
++#define YTPHY_SSR_DUPLEX_OFFSET 13
++#define YTPHY_SSR_DUPLEX BIT(13)
++#define YTPHY_SSR_PAGE_RECEIVED BIT(12)
++#define YTPHY_SSR_SPEED_DUPLEX_RESOLVED BIT(11)
++#define YTPHY_SSR_LINK BIT(10)
++#define YTPHY_SSR_MDIX_CROSSOVER BIT(6)
++#define YTPHY_SSR_DOWNGRADE BIT(5)
++#define YTPHY_SSR_TRANSMIT_PAUSE BIT(3)
++#define YTPHY_SSR_RECEIVE_PAUSE BIT(2)
++#define YTPHY_SSR_POLARITY BIT(1)
++#define YTPHY_SSR_JABBER BIT(0)
++
++/* Interrupt enable Register */
++#define YTPHY_INTERRUPT_ENABLE_REG 0x12
++#define YTPHY_IER_WOL BIT(6)
++
++/* Interrupt Status Register */
++#define YTPHY_INTERRUPT_STATUS_REG 0x13
++#define YTPHY_ISR_AUTONEG_ERR BIT(15)
++#define YTPHY_ISR_SPEED_CHANGED BIT(14)
++#define YTPHY_ISR_DUPLEX_CHANGED BIT(13)
++#define YTPHY_ISR_PAGE_RECEIVED BIT(12)
++#define YTPHY_ISR_LINK_FAILED BIT(11)
++#define YTPHY_ISR_LINK_SUCCESSED BIT(10)
++#define YTPHY_ISR_WOL BIT(6)
++#define YTPHY_ISR_WIRESPEED_DOWNGRADE BIT(5)
++#define YTPHY_ISR_SERDES_LINK_FAILED BIT(3)
++#define YTPHY_ISR_SERDES_LINK_SUCCESSED BIT(2)
++#define YTPHY_ISR_POLARITY_CHANGED BIT(1)
++#define YTPHY_ISR_JABBER_HAPPENED BIT(0)
++
++/* Speed Auto Downgrade Control Register */
++#define YTPHY_SPEED_AUTO_DOWNGRADE_CONTROL_REG 0x14
++#define YTPHY_SADCR_SPEED_DOWNGRADE_EN BIT(5)
++
++/* If these bits are set to 3, the PHY attempts five times ( 3(set value) +
++ * additional 2) before downgrading, default 0x3
++ */
++#define YTPHY_SADCR_SPEED_RETRY_LIMIT (0x3 << 2)
++
++/* Rx Error Counter Register */
++#define YTPHY_RX_ERROR_COUNTER_REG 0x15
++
++/* Extended Register's Address Offset Register */
++#define YTPHY_PAGE_SELECT 0x1E
++
++/* Extended Register's Data Register */
++#define YTPHY_PAGE_DATA 0x1F
++
++/* FIBER Auto-Negotiation link partner ability */
++#define YTPHY_FLPA_PAUSE (0x3 << 7)
++#define YTPHY_FLPA_ASYM_PAUSE (0x2 << 7)
+
+ #define YT8511_PAGE_SELECT 0x1e
+ #define YT8511_PAGE 0x1f
+@@ -38,6 +129,352 @@
+ #define YT8511_DELAY_FE_TX_EN (0xf << 12)
+ #define YT8511_DELAY_FE_TX_DIS (0x2 << 12)
+
++/* Extended register is different from MMD Register and MII Register.
++ * We can use ytphy_read_ext/ytphy_write_ext/ytphy_modify_ext function to
++ * operate extended register.
++ * Extended Register start
++ */
++
++/* Phy gmii clock gating Register */
++#define YT8521_CLOCK_GATING_REG 0xC
++#define YT8521_CGR_RX_CLK_EN BIT(12)
++
++#define YT8521_EXTREG_SLEEP_CONTROL1_REG 0x27
++#define YT8521_ESC1R_SLEEP_SW BIT(15)
++#define YT8521_ESC1R_PLLON_SLP BIT(14)
++
++/* Phy fiber Link timer cfg2 Register */
++#define YT8521_LINK_TIMER_CFG2_REG 0xA5
++#define YT8521_LTCR_EN_AUTOSEN BIT(15)
++
++/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A are common ext registers
++ * of yt8521 phy. There is no need to switch reg space when operating these
++ * registers.
++ */
++
++#define YT8521_REG_SPACE_SELECT_REG 0xA000
++#define YT8521_RSSR_SPACE_MASK BIT(1)
++#define YT8521_RSSR_FIBER_SPACE (0x1 << 1)
++#define YT8521_RSSR_UTP_SPACE (0x0 << 1)
++#define YT8521_RSSR_TO_BE_ARBITRATED (0xFF)
++
++#define YT8521_CHIP_CONFIG_REG 0xA001
++#define YT8521_CCR_SW_RST BIT(15)
++
++#define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0))
++#define YT8521_CCR_MODE_UTP_TO_RGMII 0
++#define YT8521_CCR_MODE_FIBER_TO_RGMII 1
++#define YT8521_CCR_MODE_UTP_FIBER_TO_RGMII 2
++#define YT8521_CCR_MODE_UTP_TO_SGMII 3
++#define YT8521_CCR_MODE_SGPHY_TO_RGMAC 4
++#define YT8521_CCR_MODE_SGMAC_TO_RGPHY 5
++#define YT8521_CCR_MODE_UTP_TO_FIBER_AUTO 6
++#define YT8521_CCR_MODE_UTP_TO_FIBER_FORCE 7
++
++/* 3 phy polling modes,poll mode combines utp and fiber mode*/
++#define YT8521_MODE_FIBER 0x1
++#define YT8521_MODE_UTP 0x2
++#define YT8521_MODE_POLL 0x3
++
++#define YT8521_RGMII_CONFIG1_REG 0xA003
++
++/* TX Gig-E Delay is bits 3:0, default 0x1
++ * TX Fast-E Delay is bits 7:4, default 0xf
++ * RX Delay is bits 13:10, default 0x0
++ * Delay = 150ps * N
++ * On = 2250ps, off = 0ps
++ */
++#define YT8521_RC1R_RX_DELAY_MASK (0xF << 10)
++#define YT8521_RC1R_RX_DELAY_EN (0xF << 10)
++#define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10)
++#define YT8521_RC1R_FE_TX_DELAY_MASK (0xF << 4)
++#define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4)
++#define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4)
++#define YT8521_RC1R_GE_TX_DELAY_MASK (0xF << 0)
++#define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0)
++#define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0)
++
++#define YTPHY_MISC_CONFIG_REG 0xA006
++#define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
++#define YTPHY_MCR_FIBER_1000BX (0x1 << 0)
++#define YTPHY_MCR_FIBER_100FX (0x0 << 0)
++
++/* WOL MAC ADDR: MACADDR2(highest), MACADDR1(middle), MACADDR0(lowest) */
++#define YTPHY_WOL_MACADDR2_REG 0xA007
++#define YTPHY_WOL_MACADDR1_REG 0xA008
++#define YTPHY_WOL_MACADDR0_REG 0xA009
++
++#define YTPHY_WOL_CONFIG_REG 0xA00A
++#define YTPHY_WCR_INTR_SEL BIT(6)
++#define YTPHY_WCR_ENABLE BIT(3)
++
++/* 2b00 84ms
++ * 2b01 168ms *default*
++ * 2b10 336ms
++ * 2b11 672ms
++ */
++#define YTPHY_WCR_PULSE_WIDTH_MASK (BIT(2) | BIT(1))
++#define YTPHY_WCR_PULSE_WIDTH_672MS (BIT(2) | BIT(1))
++
++/* 1b0 Interrupt and WOL events is level triggered and active LOW *default*
++ * 1b1 Interrupt and WOL events is pulse triggered and active LOW
++ */
++#define YTPHY_WCR_TYPE_PULSE BIT(0)
++
++/* Extended Register end */
++
++struct yt8521_priv {
++ /* combo_advertising is used for case of YT8521 in combo mode,
++ * this means that yt8521 may work in utp or fiber mode which depends
++ * on which media is connected (YT8521_RSSR_TO_BE_ARBITRATED).
++ */
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(combo_advertising);
++
++ /* YT8521_MODE_FIBER / YT8521_MODE_UTP / YT8521_MODE_POLL*/
++ u8 polling_mode;
++ u8 strap_mode; /* 8 working modes */
++ /* current reg page of yt8521 phy:
++ * YT8521_RSSR_UTP_SPACE
++ * YT8521_RSSR_FIBER_SPACE
++ * YT8521_RSSR_TO_BE_ARBITRATED
++ */
++ u8 reg_page;
++};
++
++/**
++ * ytphy_read_ext() - read a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to read
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns the value of regnum reg or negative error code
++ */
++static int ytphy_read_ext(struct phy_device *phydev, u16 regnum)
++{
++ int ret;
++
++ ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
++ if (ret < 0)
++ return ret;
++
++ return __phy_read(phydev, YTPHY_PAGE_DATA);
++}
++
++/**
++ * ytphy_read_ext_with_lock() - read a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to read
++ *
++ * returns the value of regnum reg or negative error code
++ */
++static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum)
++{
++ int ret;
++
++ phy_lock_mdio_bus(phydev);
++ ret = ytphy_read_ext(phydev, regnum);
++ phy_unlock_mdio_bus(phydev);
++
++ return ret;
++}
++
++/**
++ * ytphy_write_ext() - write a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to write
++ * @val: value to write to @regnum
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative error code
++ */
++static int ytphy_write_ext(struct phy_device *phydev, u16 regnum, u16 val)
++{
++ int ret;
++
++ ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
++ if (ret < 0)
++ return ret;
++
++ return __phy_write(phydev, YTPHY_PAGE_DATA, val);
++}
++
++/**
++ * ytphy_write_ext_with_lock() - write a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to write
++ * @val: value to write to @regnum
++ *
++ * returns 0 or negative error code
++ */
++static int ytphy_write_ext_with_lock(struct phy_device *phydev, u16 regnum,
++ u16 val)
++{
++ int ret;
++
++ phy_lock_mdio_bus(phydev);
++ ret = ytphy_write_ext(phydev, regnum, val);
++ phy_unlock_mdio_bus(phydev);
++
++ return ret;
++}
++
++/**
++ * ytphy_modify_ext() - bits modify a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to write
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * NOTE: Convenience function which allows a PHY's extended register to be
++ * modified as new register value = (old register value & ~mask) | set.
++ * The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative error code
++ */
++static int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask,
++ u16 set)
++{
++ int ret;
++
++ ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
++ if (ret < 0)
++ return ret;
++
++ return __phy_modify(phydev, YTPHY_PAGE_DATA, mask, set);
++}
++
++/**
++ * ytphy_modify_ext_with_lock() - bits modify a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to write
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * NOTE: Convenience function which allows a PHY's extended register to be
++ * modified as new register value = (old register value & ~mask) | set.
++ *
++ * returns 0 or negative error code
++ */
++static int ytphy_modify_ext_with_lock(struct phy_device *phydev, u16 regnum,
++ u16 mask, u16 set)
++{
++ int ret;
++
++ phy_lock_mdio_bus(phydev);
++ ret = ytphy_modify_ext(phydev, regnum, mask, set);
++ phy_unlock_mdio_bus(phydev);
++
++ return ret;
++}
++
++/**
++ * ytphy_get_wol() - report whether wake-on-lan is enabled
++ * @phydev: a pointer to a &struct phy_device
++ * @wol: a pointer to a &struct ethtool_wolinfo
++ *
++ * NOTE: YTPHY_WOL_CONFIG_REG is common ext reg.
++ */
++static void ytphy_get_wol(struct phy_device *phydev,
++ struct ethtool_wolinfo *wol)
++{
++ int wol_config;
++
++ wol->supported = WAKE_MAGIC;
++ wol->wolopts = 0;
++
++ wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
++ if (wol_config < 0)
++ return;
++
++ if (wol_config & YTPHY_WCR_ENABLE)
++ wol->wolopts |= WAKE_MAGIC;
++}
++
++/**
++ * ytphy_set_wol() - turn wake-on-lan on or off
++ * @phydev: a pointer to a &struct phy_device
++ * @wol: a pointer to a &struct ethtool_wolinfo
++ *
++ * NOTE: YTPHY_WOL_CONFIG_REG, YTPHY_WOL_MACADDR2_REG, YTPHY_WOL_MACADDR1_REG
++ * and YTPHY_WOL_MACADDR0_REG are common ext reg. The
++ * YTPHY_INTERRUPT_ENABLE_REG of UTP is special, fiber also use this register.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
++{
++ struct net_device *p_attached_dev;
++ const u16 mac_addr_reg[] = {
++ YTPHY_WOL_MACADDR2_REG,
++ YTPHY_WOL_MACADDR1_REG,
++ YTPHY_WOL_MACADDR0_REG,
++ };
++ const u8 *mac_addr;
++ int old_page;
++ int ret = 0;
++ u16 mask;
++ u16 val;
++ u8 i;
++
++ if (wol->wolopts & WAKE_MAGIC) {
++ p_attached_dev = phydev->attached_dev;
++ if (!p_attached_dev)
++ return -ENODEV;
++
++ mac_addr = (const u8 *)p_attached_dev->dev_addr;
++ if (!is_valid_ether_addr(mac_addr))
++ return -EINVAL;
++
++ /* lock mdio bus then switch to utp reg space */
++ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
++ if (old_page < 0)
++ goto err_restore_page;
++
++ /* Store the device address for the magic packet */
++ for (i = 0; i < 3; i++) {
++ ret = ytphy_write_ext(phydev, mac_addr_reg[i],
++ ((mac_addr[i * 2] << 8)) |
++ (mac_addr[i * 2 + 1]));
++ if (ret < 0)
++ goto err_restore_page;
++ }
++
++ /* Enable WOL feature */
++ mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
++ val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
++ val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
++ ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val);
++ if (ret < 0)
++ goto err_restore_page;
++
++ /* Enable WOL interrupt */
++ ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
++ YTPHY_IER_WOL);
++ if (ret < 0)
++ goto err_restore_page;
++
++ } else {
++ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
++ if (old_page < 0)
++ goto err_restore_page;
++
++ /* Disable WOL feature */
++ mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
++ ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0);
++
++ /* Disable WOL interrupt */
++ ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
++ YTPHY_IER_WOL, 0);
++ if (ret < 0)
++ goto err_restore_page;
++ }
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
+ static int yt8511_read_page(struct phy_device *phydev)
+ {
+ return __phy_read(phydev, YT8511_PAGE_SELECT);
+@@ -111,6 +548,1181 @@ err_restore_page:
+ return phy_restore_page(phydev, oldpage, ret);
+ }
+
++/**
++ * yt8521_read_page() - read reg page
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns current reg space of yt8521 (YT8521_RSSR_FIBER_SPACE/
++ * YT8521_RSSR_UTP_SPACE) or negative errno code
++ */
++static int yt8521_read_page(struct phy_device *phydev)
++{
++ int old_page;
++
++ old_page = ytphy_read_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
++ if (old_page < 0)
++ return old_page;
++
++ if ((old_page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE)
++ return YT8521_RSSR_FIBER_SPACE;
++
++ return YT8521_RSSR_UTP_SPACE;
++};
++
++/**
++ * yt8521_write_page() - write reg page
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to write.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_write_page(struct phy_device *phydev, int page)
++{
++ int mask = YT8521_RSSR_SPACE_MASK;
++ int set;
++
++ if ((page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE)
++ set = YT8521_RSSR_FIBER_SPACE;
++ else
++ set = YT8521_RSSR_UTP_SPACE;
++
++ return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set);
++};
++
++/**
++ * yt8521_probe() - read chip config then set suitable polling_mode
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_probe(struct phy_device *phydev)
++{
++ struct device *dev = &phydev->mdio.dev;
++ struct yt8521_priv *priv;
++ int chip_config;
++ int ret;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ phydev->priv = priv;
++
++ chip_config = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
++ if (chip_config < 0)
++ return chip_config;
++
++ priv->strap_mode = chip_config & YT8521_CCR_MODE_SEL_MASK;
++ switch (priv->strap_mode) {
++ case YT8521_CCR_MODE_FIBER_TO_RGMII:
++ case YT8521_CCR_MODE_SGPHY_TO_RGMAC:
++ case YT8521_CCR_MODE_SGMAC_TO_RGPHY:
++ priv->polling_mode = YT8521_MODE_FIBER;
++ priv->reg_page = YT8521_RSSR_FIBER_SPACE;
++ phydev->port = PORT_FIBRE;
++ break;
++ case YT8521_CCR_MODE_UTP_FIBER_TO_RGMII:
++ case YT8521_CCR_MODE_UTP_TO_FIBER_AUTO:
++ case YT8521_CCR_MODE_UTP_TO_FIBER_FORCE:
++ priv->polling_mode = YT8521_MODE_POLL;
++ priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED;
++ phydev->port = PORT_NONE;
++ break;
++ case YT8521_CCR_MODE_UTP_TO_SGMII:
++ case YT8521_CCR_MODE_UTP_TO_RGMII:
++ priv->polling_mode = YT8521_MODE_UTP;
++ priv->reg_page = YT8521_RSSR_UTP_SPACE;
++ phydev->port = PORT_TP;
++ break;
++ }
++ /* set default reg space */
++ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++ ret = ytphy_write_ext_with_lock(phydev,
++ YT8521_REG_SPACE_SELECT_REG,
++ priv->reg_page);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++/**
++ * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_utp_read_lpa(struct phy_device *phydev)
++{
++ int lpa, lpagb;
++
++ if (phydev->autoneg == AUTONEG_ENABLE) {
++ if (!phydev->autoneg_complete) {
++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
++ 0);
++ mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
++ return 0;
++ }
++
++ if (phydev->is_gigabit_capable) {
++ lpagb = __phy_read(phydev, MII_STAT1000);
++ if (lpagb < 0)
++ return lpagb;
++
++ if (lpagb & LPA_1000MSFAIL) {
++ int adv = __phy_read(phydev, MII_CTRL1000);
++
++ if (adv < 0)
++ return adv;
++
++ if (adv & CTL1000_ENABLE_MASTER)
++ phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
++ else
++ phydev_err(phydev, "Master/Slave resolution failed\n");
++ return -ENOLINK;
++ }
++
++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
++ lpagb);
++ }
++
++ lpa = __phy_read(phydev, MII_LPA);
++ if (lpa < 0)
++ return lpa;
++
++ mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
++ } else {
++ linkmode_zero(phydev->lp_advertising);
++ }
++
++ return 0;
++}
++
++/**
++ * yt8521_adjust_status() - update speed and duplex to phydev. when in fiber
++ * mode, adjust speed and duplex.
++ * @phydev: a pointer to a &struct phy_device
++ * @status: yt8521 status read from YTPHY_SPECIFIC_STATUS_REG
++ * @is_utp: false(yt8521 work in fiber mode) or true(yt8521 work in utp mode)
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0
++ */
++static int yt8521_adjust_status(struct phy_device *phydev, int status,
++ bool is_utp)
++{
++ int speed_mode, duplex;
++ int speed;
++ int err;
++ int lpa;
++
++ if (is_utp)
++ duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_OFFSET;
++ else
++ duplex = DUPLEX_FULL; /* for fiber, it always DUPLEX_FULL */
++
++ speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >>
++ YTPHY_SSR_SPEED_MODE_OFFSET;
++
++ switch (speed_mode) {
++ case YTPHY_SSR_SPEED_10M:
++ if (is_utp)
++ speed = SPEED_10;
++ else
++ /* for fiber, it will never run here, default to
++ * SPEED_UNKNOWN
++ */
++ speed = SPEED_UNKNOWN;
++ break;
++ case YTPHY_SSR_SPEED_100M:
++ speed = SPEED_100;
++ break;
++ case YTPHY_SSR_SPEED_1000M:
++ speed = SPEED_1000;
++ break;
++ default:
++ speed = SPEED_UNKNOWN;
++ break;
++ }
++
++ phydev->speed = speed;
++ phydev->duplex = duplex;
++
++ if (is_utp) {
++ err = ytphy_utp_read_lpa(phydev);
++ if (err < 0)
++ return err;
++
++ phy_resolve_aneg_pause(phydev);
++ } else {
++ lpa = __phy_read(phydev, MII_LPA);
++ if (lpa < 0)
++ return lpa;
++
++ /* only support 1000baseX Full */
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
++ phydev->lp_advertising, lpa & LPA_1000XFULL);
++
++ if (!(lpa & YTPHY_FLPA_PAUSE)) {
++ phydev->pause = 0;
++ phydev->asym_pause = 0;
++ } else if ((lpa & YTPHY_FLPA_ASYM_PAUSE)) {
++ phydev->pause = 1;
++ phydev->asym_pause = 1;
++ } else {
++ phydev->pause = 1;
++ phydev->asym_pause = 0;
++ }
++ }
++
++ return 0;
++}
++
++/**
++ * yt8521_read_status_paged() - determines the speed and duplex of one page
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
++ * operate.
++ *
++ * returns 1 (utp or fiber link),0 (no link) or negative errno code
++ */
++static int yt8521_read_status_paged(struct phy_device *phydev, int page)
++{
++ int fiber_latch_val;
++ int fiber_curr_val;
++ int old_page;
++ int ret = 0;
++ int status;
++ int link;
++
++ linkmode_zero(phydev->lp_advertising);
++ phydev->duplex = DUPLEX_UNKNOWN;
++ phydev->speed = SPEED_UNKNOWN;
++ phydev->asym_pause = 0;
++ phydev->pause = 0;
++
++ /* YT8521 has two reg space (utp/fiber) for linkup with utp/fiber
++ * respectively. but for utp/fiber combo mode, reg space should be
++ * arbitrated based on media priority. by default, utp takes
++ * priority. reg space should be properly set before read
++ * YTPHY_SPECIFIC_STATUS_REG.
++ */
++
++ page &= YT8521_RSSR_SPACE_MASK;
++ old_page = phy_select_page(phydev, page);
++ if (old_page < 0)
++ goto err_restore_page;
++
++ /* Read YTPHY_SPECIFIC_STATUS_REG, which indicates the speed and duplex
++ * of the PHY is actually using.
++ */
++ ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
++ if (ret < 0)
++ goto err_restore_page;
++
++ status = ret;
++ link = !!(status & YTPHY_SSR_LINK);
++
++ /* When PHY is in fiber mode, speed transferred from 1000Mbps to
++ * 100Mbps,there is not link down from YTPHY_SPECIFIC_STATUS_REG, so
++ * we need check MII_BMSR to identify such case.
++ */
++ if (page == YT8521_RSSR_FIBER_SPACE) {
++ ret = __phy_read(phydev, MII_BMSR);
++ if (ret < 0)
++ goto err_restore_page;
++
++ fiber_latch_val = ret;
++ ret = __phy_read(phydev, MII_BMSR);
++ if (ret < 0)
++ goto err_restore_page;
++
++ fiber_curr_val = ret;
++ if (link && fiber_latch_val != fiber_curr_val) {
++ link = 0;
++ phydev_info(phydev,
++ "%s, fiber link down detect, latch = %04x, curr = %04x\n",
++ __func__, fiber_latch_val, fiber_curr_val);
++ }
++ } else {
++ /* Read autonegotiation status */
++ ret = __phy_read(phydev, MII_BMSR);
++ if (ret < 0)
++ goto err_restore_page;
++
++ phydev->autoneg_complete = ret & BMSR_ANEGCOMPLETE ? 1 : 0;
++ }
++
++ if (link) {
++ if (page == YT8521_RSSR_UTP_SPACE)
++ yt8521_adjust_status(phydev, status, true);
++ else
++ yt8521_adjust_status(phydev, status, false);
++ }
++ return phy_restore_page(phydev, old_page, link);
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_read_status() - determines the negotiated speed and duplex
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_read_status(struct phy_device *phydev)
++{
++ struct yt8521_priv *priv = phydev->priv;
++ int link_fiber = 0;
++ int link_utp;
++ int link;
++ int ret;
++
++ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++ link = yt8521_read_status_paged(phydev, priv->reg_page);
++ if (link < 0)
++ return link;
++ } else {
++ /* when page is YT8521_RSSR_TO_BE_ARBITRATED, arbitration is
++ * needed. by default, utp is higher priority.
++ */
++
++ link_utp = yt8521_read_status_paged(phydev,
++ YT8521_RSSR_UTP_SPACE);
++ if (link_utp < 0)
++ return link_utp;
++
++ if (!link_utp) {
++ link_fiber = yt8521_read_status_paged(phydev,
++ YT8521_RSSR_FIBER_SPACE);
++ if (link_fiber < 0)
++ return link_fiber;
++ }
++
++ link = link_utp || link_fiber;
++ }
++
++ if (link) {
++ if (phydev->link == 0) {
++ /* arbitrate reg space based on linkup media type. */
++ if (priv->polling_mode == YT8521_MODE_POLL &&
++ priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) {
++ if (link_fiber)
++ priv->reg_page =
++ YT8521_RSSR_FIBER_SPACE;
++ else
++ priv->reg_page = YT8521_RSSR_UTP_SPACE;
++
++ ret = ytphy_write_ext_with_lock(phydev,
++ YT8521_REG_SPACE_SELECT_REG,
++ priv->reg_page);
++ if (ret < 0)
++ return ret;
++
++ phydev->port = link_fiber ? PORT_FIBRE : PORT_TP;
++
++ phydev_info(phydev, "%s, link up, media: %s\n",
++ __func__,
++ (phydev->port == PORT_TP) ?
++ "UTP" : "Fiber");
++ }
++ }
++ phydev->link = 1;
++ } else {
++ if (phydev->link == 1) {
++ phydev_info(phydev, "%s, link down, media: %s\n",
++ __func__, (phydev->port == PORT_TP) ?
++ "UTP" : "Fiber");
++
++ /* When in YT8521_MODE_POLL mode, need prepare for next
++ * arbitration.
++ */
++ if (priv->polling_mode == YT8521_MODE_POLL) {
++ priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED;
++ phydev->port = PORT_NONE;
++ }
++ }
++
++ phydev->link = 0;
++ }
++
++ return 0;
++}
++
++/**
++ * yt8521_modify_bmcr_paged - bits modify a PHY's BMCR register of one page
++ * @phydev: the phy_device struct
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to operate
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * NOTE: Convenience function which allows a PHY's BMCR register to be
++ * modified as new register value = (old register value & ~mask) | set.
++ * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space
++ * has MII_BMCR. poll mode combines utp and faber,so need do both.
++ * If it is reset, it will wait for completion.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_modify_bmcr_paged(struct phy_device *phydev, int page,
++ u16 mask, u16 set)
++{
++ int max_cnt = 500; /* the max wait time of reset ~ 500 ms */
++ int old_page;
++ int ret = 0;
++
++ old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK);
++ if (old_page < 0)
++ goto err_restore_page;
++
++ ret = __phy_modify(phydev, MII_BMCR, mask, set);
++ if (ret < 0)
++ goto err_restore_page;
++
++ /* If it is reset, need to wait for the reset to complete */
++ if (set == BMCR_RESET) {
++ while (max_cnt--) {
++ usleep_range(1000, 1100);
++ ret = __phy_read(phydev, MII_BMCR);
++ if (ret < 0)
++ goto err_restore_page;
++
++ if (!(ret & BMCR_RESET))
++ return phy_restore_page(phydev, old_page, 0);
++ }
++ }
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register
++ * @phydev: the phy_device struct
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * NOTE: Convenience function which allows a PHY's BMCR register to be
++ * modified as new register value = (old register value & ~mask) | set.
++ * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space
++ * has MII_BMCR. poll mode combines utp and faber,so need do both.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_modify_utp_fiber_bmcr(struct phy_device *phydev, u16 mask,
++ u16 set)
++{
++ struct yt8521_priv *priv = phydev->priv;
++ int ret;
++
++ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++ ret = yt8521_modify_bmcr_paged(phydev, priv->reg_page, mask,
++ set);
++ if (ret < 0)
++ return ret;
++ } else {
++ ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE,
++ mask, set);
++ if (ret < 0)
++ return ret;
++
++ ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE,
++ mask, set);
++ if (ret < 0)
++ return ret;
++ }
++ return 0;
++}
++
++/**
++ * yt8521_soft_reset() - called to issue a PHY software reset
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_soft_reset(struct phy_device *phydev)
++{
++ return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_RESET);
++}
++
++/**
++ * yt8521_suspend() - suspend the hardware
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_suspend(struct phy_device *phydev)
++{
++ int wol_config;
++
++ /* YTPHY_WOL_CONFIG_REG is common ext reg */
++ wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
++ if (wol_config < 0)
++ return wol_config;
++
++ /* if wol enable, do nothing */
++ if (wol_config & YTPHY_WCR_ENABLE)
++ return 0;
++
++ return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN);
++}
++
++/**
++ * yt8521_resume() - resume the hardware
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_resume(struct phy_device *phydev)
++{
++ int ret;
++ int wol_config;
++
++ /* disable auto sleep */
++ ret = ytphy_modify_ext_with_lock(phydev,
++ YT8521_EXTREG_SLEEP_CONTROL1_REG,
++ YT8521_ESC1R_SLEEP_SW, 0);
++ if (ret < 0)
++ return ret;
++
++ wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
++ if (wol_config < 0)
++ return wol_config;
++
++ /* if wol enable, do nothing */
++ if (wol_config & YTPHY_WCR_ENABLE)
++ return 0;
++
++ return yt8521_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
++}
++
++/**
++ * yt8521_config_init() - called to initialize the PHY
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_config_init(struct phy_device *phydev)
++{
++ int old_page;
++ int ret = 0;
++ u16 val;
++
++ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
++ if (old_page < 0)
++ goto err_restore_page;
++
++ switch (phydev->interface) {
++ case PHY_INTERFACE_MODE_RGMII:
++ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
++ val |= YT8521_RC1R_RX_DELAY_DIS;
++ break;
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
++ val |= YT8521_RC1R_RX_DELAY_EN;
++ break;
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
++ val |= YT8521_RC1R_RX_DELAY_DIS;
++ break;
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
++ val |= YT8521_RC1R_RX_DELAY_EN;
++ break;
++ case PHY_INTERFACE_MODE_SGMII:
++ break;
++ default: /* do not support other modes */
++ ret = -EOPNOTSUPP;
++ goto err_restore_page;
++ }
++
++ /* set rgmii delay mode */
++ if (phydev->interface != PHY_INTERFACE_MODE_SGMII) {
++ ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG,
++ (YT8521_RC1R_RX_DELAY_MASK |
++ YT8521_RC1R_FE_TX_DELAY_MASK |
++ YT8521_RC1R_GE_TX_DELAY_MASK),
++ val);
++ if (ret < 0)
++ goto err_restore_page;
++ }
++
++ /* disable auto sleep */
++ ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
++ YT8521_ESC1R_SLEEP_SW, 0);
++ if (ret < 0)
++ goto err_restore_page;
++
++ /* enable RXC clock when no wire plug */
++ ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
++ YT8521_CGR_RX_CLK_EN, 0);
++ if (ret < 0)
++ goto err_restore_page;
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_prepare_fiber_features() - A small helper function that setup
++ * fiber's features.
++ * @phydev: a pointer to a &struct phy_device
++ * @dst: a pointer to store fiber's features
++ */
++static void yt8521_prepare_fiber_features(struct phy_device *phydev,
++ unsigned long *dst)
++{
++ linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, dst);
++ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, dst);
++ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, dst);
++ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, dst);
++}
++
++/**
++ * yt8521_fiber_setup_forced - configures/forces speed from @phydev
++ * @phydev: target phy_device struct
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_fiber_setup_forced(struct phy_device *phydev)
++{
++ u16 val;
++ int ret;
++
++ if (phydev->speed == SPEED_1000)
++ val = YTPHY_MCR_FIBER_1000BX;
++ else if (phydev->speed == SPEED_100)
++ val = YTPHY_MCR_FIBER_100FX;
++ else
++ return -EINVAL;
++
++ ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
++ if (ret < 0)
++ return ret;
++
++ /* disable Fiber auto sensing */
++ ret = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG,
++ YT8521_LTCR_EN_AUTOSEN, 0);
++ if (ret < 0)
++ return ret;
++
++ ret = ytphy_modify_ext(phydev, YTPHY_MISC_CONFIG_REG,
++ YTPHY_MCR_FIBER_SPEED_MASK, val);
++ if (ret < 0)
++ return ret;
++
++ return ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
++ YT8521_CCR_SW_RST, 0);
++}
++
++/**
++ * ytphy_check_and_restart_aneg - Enable and restart auto-negotiation
++ * @phydev: target phy_device struct
++ * @restart: whether aneg restart is requested
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_check_and_restart_aneg(struct phy_device *phydev, bool restart)
++{
++ int ret;
++
++ if (!restart) {
++ /* Advertisement hasn't changed, but maybe aneg was never on to
++ * begin with? Or maybe phy was isolated?
++ */
++ ret = __phy_read(phydev, MII_BMCR);
++ if (ret < 0)
++ return ret;
++
++ if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE))
++ restart = true;
++ }
++ /* Enable and Restart Autonegotiation
++ * Don't isolate the PHY if we're negotiating
++ */
++ if (restart)
++ return __phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,
++ BMCR_ANENABLE | BMCR_ANRESTART);
++
++ return 0;
++}
++
++/**
++ * yt8521_fiber_config_aneg - restart auto-negotiation or write
++ * YTPHY_MISC_CONFIG_REG.
++ * @phydev: target phy_device struct
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_fiber_config_aneg(struct phy_device *phydev)
++{
++ int err, changed = 0;
++ int bmcr;
++ u16 adv;
++
++ if (phydev->autoneg != AUTONEG_ENABLE)
++ return yt8521_fiber_setup_forced(phydev);
++
++ /* enable Fiber auto sensing */
++ err = ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG,
++ 0, YT8521_LTCR_EN_AUTOSEN);
++ if (err < 0)
++ return err;
++
++ err = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
++ YT8521_CCR_SW_RST, 0);
++ if (err < 0)
++ return err;
++
++ bmcr = __phy_read(phydev, MII_BMCR);
++ if (bmcr < 0)
++ return bmcr;
++
++ /* When it is coming from fiber forced mode, add bmcr power down
++ * and power up to let aneg work fine.
++ */
++ if (!(bmcr & BMCR_ANENABLE)) {
++ __phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
++ usleep_range(1000, 1100);
++ __phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
++ }
++
++ adv = linkmode_adv_to_mii_adv_x(phydev->advertising,
++ ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
++
++ /* Setup fiber advertisement */
++ err = __phy_modify_changed(phydev, MII_ADVERTISE,
++ ADVERTISE_1000XHALF | ADVERTISE_1000XFULL |
++ ADVERTISE_1000XPAUSE |
++ ADVERTISE_1000XPSE_ASYM,
++ adv);
++ if (err < 0)
++ return err;
++
++ if (err > 0)
++ changed = 1;
++
++ return ytphy_check_and_restart_aneg(phydev, changed);
++}
++
++/**
++ * ytphy_setup_master_slave
++ * @phydev: target phy_device struct
++ *
++ * NOTE: The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_setup_master_slave(struct phy_device *phydev)
++{
++ u16 ctl = 0;
++
++ if (!phydev->is_gigabit_capable)
++ return 0;
++
++ switch (phydev->master_slave_set) {
++ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
++ ctl |= CTL1000_PREFER_MASTER;
++ break;
++ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
++ break;
++ case MASTER_SLAVE_CFG_MASTER_FORCE:
++ ctl |= CTL1000_AS_MASTER;
++ fallthrough;
++ case MASTER_SLAVE_CFG_SLAVE_FORCE:
++ ctl |= CTL1000_ENABLE_MASTER;
++ break;
++ case MASTER_SLAVE_CFG_UNKNOWN:
++ case MASTER_SLAVE_CFG_UNSUPPORTED:
++ return 0;
++ default:
++ phydev_warn(phydev, "Unsupported Master/Slave mode\n");
++ return -EOPNOTSUPP;
++ }
++
++ return __phy_modify_changed(phydev, MII_CTRL1000,
++ (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
++ CTL1000_PREFER_MASTER), ctl);
++}
++
++/**
++ * ytphy_utp_config_advert - sanitize and advertise auto-negotiation parameters
++ * @phydev: target phy_device struct
++ *
++ * NOTE: Writes MII_ADVERTISE with the appropriate values,
++ * after sanitizing the values to make sure we only advertise
++ * what is supported. Returns < 0 on error, 0 if the PHY's advertisement
++ * hasn't changed, and > 0 if it has changed.
++ * The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_utp_config_advert(struct phy_device *phydev)
++{
++ int err, bmsr, changed = 0;
++ u32 adv;
++
++ /* Only allow advertising what this PHY supports */
++ linkmode_and(phydev->advertising, phydev->advertising,
++ phydev->supported);
++
++ adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
++
++ /* Setup standard advertisement */
++ err = __phy_modify_changed(phydev, MII_ADVERTISE,
++ ADVERTISE_ALL | ADVERTISE_100BASE4 |
++ ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
++ adv);
++ if (err < 0)
++ return err;
++ if (err > 0)
++ changed = 1;
++
++ bmsr = __phy_read(phydev, MII_BMSR);
++ if (bmsr < 0)
++ return bmsr;
++
++ /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
++ * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
++ * logical 1.
++ */
++ if (!(bmsr & BMSR_ESTATEN))
++ return changed;
++
++ adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
++
++ err = __phy_modify_changed(phydev, MII_CTRL1000,
++ ADVERTISE_1000FULL | ADVERTISE_1000HALF,
++ adv);
++ if (err < 0)
++ return err;
++ if (err > 0)
++ changed = 1;
++
++ return changed;
++}
++
++/**
++ * ytphy_utp_config_aneg - restart auto-negotiation or write BMCR
++ * @phydev: target phy_device struct
++ * @changed: whether autoneg is requested
++ *
++ * NOTE: If auto-negotiation is enabled, we configure the
++ * advertising, and then restart auto-negotiation. If it is not
++ * enabled, then we write the BMCR.
++ * The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_utp_config_aneg(struct phy_device *phydev, bool changed)
++{
++ int err;
++ u16 ctl;
++
++ err = ytphy_setup_master_slave(phydev);
++ if (err < 0)
++ return err;
++ else if (err)
++ changed = true;
++
++ if (phydev->autoneg != AUTONEG_ENABLE) {
++ /* configures/forces speed/duplex from @phydev */
++
++ ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
++
++ return __phy_modify(phydev, MII_BMCR, ~(BMCR_LOOPBACK |
++ BMCR_ISOLATE | BMCR_PDOWN), ctl);
++ }
++
++ err = ytphy_utp_config_advert(phydev);
++ if (err < 0) /* error */
++ return err;
++ else if (err)
++ changed = true;
++
++ return ytphy_check_and_restart_aneg(phydev, changed);
++}
++
++/**
++ * yt8521_config_aneg_paged() - switch reg space then call genphy_config_aneg
++ * of one page
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
++ * operate.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_config_aneg_paged(struct phy_device *phydev, int page)
++{
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(fiber_supported);
++ struct yt8521_priv *priv = phydev->priv;
++ int old_page;
++ int ret = 0;
++
++ page &= YT8521_RSSR_SPACE_MASK;
++
++ old_page = phy_select_page(phydev, page);
++ if (old_page < 0)
++ goto err_restore_page;
++
++ /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED,
++ * phydev->advertising should be updated.
++ */
++ if (priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) {
++ linkmode_zero(fiber_supported);
++ yt8521_prepare_fiber_features(phydev, fiber_supported);
++
++ /* prepare fiber_supported, then setup advertising. */
++ if (page == YT8521_RSSR_FIBER_SPACE) {
++ linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
++ fiber_supported);
++ linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
++ fiber_supported);
++ linkmode_and(phydev->advertising,
++ priv->combo_advertising, fiber_supported);
++ } else {
++ /* ETHTOOL_LINK_MODE_Autoneg_BIT is also used in utp */
++ linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
++ fiber_supported);
++ linkmode_andnot(phydev->advertising,
++ priv->combo_advertising,
++ fiber_supported);
++ }
++ }
++
++ if (page == YT8521_RSSR_FIBER_SPACE)
++ ret = yt8521_fiber_config_aneg(phydev);
++ else
++ ret = ytphy_utp_config_aneg(phydev, false);
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_config_aneg() - change reg space then call yt8521_config_aneg_paged
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_config_aneg(struct phy_device *phydev)
++{
++ struct yt8521_priv *priv = phydev->priv;
++ int ret;
++
++ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++ ret = yt8521_config_aneg_paged(phydev, priv->reg_page);
++ if (ret < 0)
++ return ret;
++ } else {
++ /* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED,
++ * phydev->advertising need to be saved at first run.
++ * Because it contains the advertising which supported by both
++ * mac and yt8521(utp and fiber).
++ */
++ if (linkmode_empty(priv->combo_advertising)) {
++ linkmode_copy(priv->combo_advertising,
++ phydev->advertising);
++ }
++
++ ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_UTP_SPACE);
++ if (ret < 0)
++ return ret;
++
++ ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_FIBER_SPACE);
++ if (ret < 0)
++ return ret;
++
++ /* we don't known which will be link, so restore
++ * phydev->advertising as default value.
++ */
++ linkmode_copy(phydev->advertising, priv->combo_advertising);
++ }
++ return 0;
++}
++
++/**
++ * yt8521_aneg_done_paged() - determines the auto negotiation result of one
++ * page.
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
++ * operate.
++ *
++ * returns 0(no link)or 1(fiber or utp link) or negative errno code
++ */
++static int yt8521_aneg_done_paged(struct phy_device *phydev, int page)
++{
++ int old_page;
++ int ret = 0;
++ int link;
++
++ old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK);
++ if (old_page < 0)
++ goto err_restore_page;
++
++ ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
++ if (ret < 0)
++ goto err_restore_page;
++
++ link = !!(ret & YTPHY_SSR_LINK);
++ ret = link;
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_aneg_done() - determines the auto negotiation result
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0(no link)or 1(fiber or utp link) or negative errno code
++ */
++static int yt8521_aneg_done(struct phy_device *phydev)
++{
++ struct yt8521_priv *priv = phydev->priv;
++ int link_fiber = 0;
++ int link_utp;
++ int link;
++
++ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++ link = yt8521_aneg_done_paged(phydev, priv->reg_page);
++ } else {
++ link_utp = yt8521_aneg_done_paged(phydev,
++ YT8521_RSSR_UTP_SPACE);
++ if (link_utp < 0)
++ return link_utp;
++
++ if (!link_utp) {
++ link_fiber = yt8521_aneg_done_paged(phydev,
++ YT8521_RSSR_FIBER_SPACE);
++ if (link_fiber < 0)
++ return link_fiber;
++ }
++ link = link_fiber || link_utp;
++ phydev_info(phydev, "%s, link_fiber: %d, link_utp: %d\n",
++ __func__, link_fiber, link_utp);
++ }
++
++ return link;
++}
++
++/**
++ * ytphy_utp_read_abilities - read PHY abilities from Clause 22 registers
++ * @phydev: target phy_device struct
++ *
++ * NOTE: Reads the PHY's abilities and populates
++ * phydev->supported accordingly.
++ * The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_utp_read_abilities(struct phy_device *phydev)
++{
++ int val;
++
++ linkmode_set_bit_array(phy_basic_ports_array,
++ ARRAY_SIZE(phy_basic_ports_array),
++ phydev->supported);
++
++ val = __phy_read(phydev, MII_BMSR);
++ if (val < 0)
++ return val;
++
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported,
++ val & BMSR_ANEGCAPABLE);
++
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported,
++ val & BMSR_100FULL);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported,
++ val & BMSR_100HALF);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported,
++ val & BMSR_10FULL);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported,
++ val & BMSR_10HALF);
++
++ if (val & BMSR_ESTATEN) {
++ val = __phy_read(phydev, MII_ESTATUS);
++ if (val < 0)
++ return val;
++
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
++ phydev->supported, val & ESTATUS_1000_TFULL);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
++ phydev->supported, val & ESTATUS_1000_THALF);
++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
++ phydev->supported, val & ESTATUS_1000_XFULL);
++ }
++
++ return 0;
++}
++
++/**
++ * yt8521_get_features_paged() - read supported link modes for one page
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
++ * operate.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_get_features_paged(struct phy_device *phydev, int page)
++{
++ int old_page;
++ int ret = 0;
++
++ page &= YT8521_RSSR_SPACE_MASK;
++ old_page = phy_select_page(phydev, page);
++ if (old_page < 0)
++ goto err_restore_page;
++
++ if (page == YT8521_RSSR_FIBER_SPACE) {
++ linkmode_zero(phydev->supported);
++ yt8521_prepare_fiber_features(phydev, phydev->supported);
++ } else {
++ ret = ytphy_utp_read_abilities(phydev);
++ if (ret < 0)
++ goto err_restore_page;
++ }
++
++err_restore_page:
++ return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_get_features - switch reg space then call yt8521_get_features_paged
++ * @phydev: target phy_device struct
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_get_features(struct phy_device *phydev)
++{
++ struct yt8521_priv *priv = phydev->priv;
++ int ret;
++
++ if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++ ret = yt8521_get_features_paged(phydev, priv->reg_page);
++ } else {
++ ret = yt8521_get_features_paged(phydev,
++ YT8521_RSSR_UTP_SPACE);
++ if (ret < 0)
++ return ret;
++
++ /* add fiber's features to phydev->supported */
++ yt8521_prepare_fiber_features(phydev, phydev->supported);
++ }
++ return ret;
++}
++
+ static struct phy_driver motorcomm_phy_drvs[] = {
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
+@@ -121,16 +1733,35 @@ static struct phy_driver motorcomm_phy_d
+ .read_page = yt8511_read_page,
+ .write_page = yt8511_write_page,
+ },
++ {
++ PHY_ID_MATCH_EXACT(PHY_ID_YT8521),
++ .name = "YT8521 Gigabit Ethernet",
++ .get_features = yt8521_get_features,
++ .probe = yt8521_probe,
++ .read_page = yt8521_read_page,
++ .write_page = yt8521_write_page,
++ .get_wol = ytphy_get_wol,
++ .set_wol = ytphy_set_wol,
++ .config_aneg = yt8521_config_aneg,
++ .aneg_done = yt8521_aneg_done,
++ .config_init = yt8521_config_init,
++ .read_status = yt8521_read_status,
++ .soft_reset = yt8521_soft_reset,
++ .suspend = yt8521_suspend,
++ .resume = yt8521_resume,
++ },
+ };
+
+ module_phy_driver(motorcomm_phy_drvs);
+
+-MODULE_DESCRIPTION("Motorcomm PHY driver");
++MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver");
+ MODULE_AUTHOR("Peter Geis");
++MODULE_AUTHOR("Frank");
+ MODULE_LICENSE("GPL");
+
+ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
++ { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
+ { /* sentinal */ }
+ };
+
diff --git a/target/linux/generic/backport-6.1/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch b/target/linux/generic/backport-6.1/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch
new file mode 100644
index 0000000000..cce71c8d84
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch
@@ -0,0 +1,49 @@
+From 4e0243e7128c9b25ea2739136076a95d6adaba5e Mon Sep 17 00:00:00 2001
+From: Frank <Frank.Sae@motor-comm.com>
+Date: Fri, 4 Nov 2022 16:44:41 +0800
+Subject: [PATCH] net: phy: fix yt8521 duplicated argument to & or |
+
+cocci warnings: (new ones prefixed by >>)
+>> drivers/net/phy/motorcomm.c:1122:8-35: duplicated argument to & or |
+ drivers/net/phy/motorcomm.c:1126:8-35: duplicated argument to & or |
+ drivers/net/phy/motorcomm.c:1130:8-34: duplicated argument to & or |
+ drivers/net/phy/motorcomm.c:1134:8-34: duplicated argument to & or |
+
+ The second YT8521_RC1R_GE_TX_DELAY_xx should be YT8521_RC1R_FE_TX_DELAY_xx.
+
+Fixes: 70479a40954c ("net: phy: Add driver for Motorcomm yt8521 gigabit ethernet phy")
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Julia Lawall <julia.lawall@lip6.fr>
+Signed-off-by: Frank <Frank.Sae@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/motorcomm.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1119,19 +1119,19 @@ static int yt8521_config_init(struct phy
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
++ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+ val |= YT8521_RC1R_RX_DELAY_DIS;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
++ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+ val |= YT8521_RC1R_RX_DELAY_EN;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
++ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+ val |= YT8521_RC1R_RX_DELAY_DIS;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
++ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+ val |= YT8521_RC1R_RX_DELAY_EN;
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
diff --git a/target/linux/generic/backport-6.1/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch b/target/linux/generic/backport-6.1/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch
new file mode 100644
index 0000000000..da60c63905
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch
@@ -0,0 +1,140 @@
+From 813abcd98fb1b2cccf850cdfa092a4bfc50b2363 Mon Sep 17 00:00:00 2001
+From: Frank <Frank.Sae@motor-comm.com>
+Date: Tue, 22 Nov 2022 16:42:32 +0800
+Subject: [PATCH] net: phy: add Motorcomm YT8531S phy id.
+
+We added patch for motorcomm.c to support YT8531S. This patch has
+been tested on AM335x platform which has one YT8531S interface
+card and passed all test cases.
+The tested cases indluding: YT8531S UTP function with support of
+10M/100M/1000M; YT8531S Fiber function with support of 100M/1000M;
+and YT8531S Combo function that supports auto detection of media type.
+
+Since most functions of YT8531S are similar to YT8521 and we reuse some
+codes for YT8521 in the patch file.
+
+Signed-off-by: Frank <Frank.Sae@motor-comm.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/Kconfig | 2 +-
+ drivers/net/phy/motorcomm.c | 52 +++++++++++++++++++++++++++++++++----
+ 2 files changed, 48 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -245,7 +245,7 @@ config MOTORCOMM_PHY
+ tristate "Motorcomm PHYs"
+ help
+ Enables support for Motorcomm network PHYs.
+- Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs.
++ Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
+
+ config NATIONAL_PHY
+ tristate "National Semiconductor PHYs"
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+- * Motorcomm 8511/8521 PHY driver.
++ * Motorcomm 8511/8521/8531S PHY driver.
+ *
+ * Author: Peter Geis <pgwipeout@gmail.com>
+ * Author: Frank <Frank.Sae@motor-comm.com>
+@@ -12,9 +12,10 @@
+ #include <linux/phy.h>
+
+ #define PHY_ID_YT8511 0x0000010a
+-#define PHY_ID_YT8521 0x0000011A
++#define PHY_ID_YT8521 0x0000011A
++#define PHY_ID_YT8531S 0x4F51E91A
+
+-/* YT8521 Register Overview
++/* YT8521/YT8531S Register Overview
+ * UTP Register space | FIBER Register space
+ * ------------------------------------------------------------
+ * | UTP MII | FIBER MII |
+@@ -147,7 +148,7 @@
+ #define YT8521_LINK_TIMER_CFG2_REG 0xA5
+ #define YT8521_LTCR_EN_AUTOSEN BIT(15)
+
+-/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A are common ext registers
++/* 0xA000, 0xA001, 0xA003, 0xA006 ~ 0xA00A and 0xA012 are common ext registers
+ * of yt8521 phy. There is no need to switch reg space when operating these
+ * registers.
+ */
+@@ -221,6 +222,9 @@
+ */
+ #define YTPHY_WCR_TYPE_PULSE BIT(0)
+
++#define YT8531S_SYNCE_CFG_REG 0xA012
++#define YT8531S_SCR_SYNCE_ENABLE BIT(6)
++
+ /* Extended Register end */
+
+ struct yt8521_priv {
+@@ -648,6 +652,26 @@ static int yt8521_probe(struct phy_devic
+ }
+
+ /**
++ * yt8531s_probe() - read chip config then set suitable polling_mode
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8531s_probe(struct phy_device *phydev)
++{
++ int ret;
++
++ /* Disable SyncE clock output by default */
++ ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG,
++ YT8531S_SCR_SYNCE_ENABLE, 0);
++ if (ret < 0)
++ return ret;
++
++ /* same as yt8521_probe */
++ return yt8521_probe(phydev);
++}
++
++/**
+ * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
+ * @phydev: a pointer to a &struct phy_device
+ *
+@@ -1750,11 +1774,28 @@ static struct phy_driver motorcomm_phy_d
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
+ },
++ {
++ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
++ .name = "YT8531S Gigabit Ethernet",
++ .get_features = yt8521_get_features,
++ .probe = yt8531s_probe,
++ .read_page = yt8521_read_page,
++ .write_page = yt8521_write_page,
++ .get_wol = ytphy_get_wol,
++ .set_wol = ytphy_set_wol,
++ .config_aneg = yt8521_config_aneg,
++ .aneg_done = yt8521_aneg_done,
++ .config_init = yt8521_config_init,
++ .read_status = yt8521_read_status,
++ .soft_reset = yt8521_soft_reset,
++ .suspend = yt8521_suspend,
++ .resume = yt8521_resume,
++ },
+ };
+
+ module_phy_driver(motorcomm_phy_drvs);
+
+-MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver");
++MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
+ MODULE_AUTHOR("Peter Geis");
+ MODULE_AUTHOR("Frank");
+ MODULE_LICENSE("GPL");
+@@ -1762,6 +1803,7 @@ MODULE_LICENSE("GPL");
+ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
++ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
+ { /* sentinal */ }
+ };
+
diff --git a/target/linux/generic/backport-6.1/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch b/target/linux/generic/backport-6.1/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch
new file mode 100644
index 0000000000..94fc32aadb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch
@@ -0,0 +1,26 @@
+From 4104a713204d62aca482eebb0c6226d82a0721eb Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@motor-comm.com>
+Date: Sat, 28 Jan 2023 14:35:57 +0800
+Subject: [PATCH] net: phy: fix the spelling problem of Sentinel
+
+CHECK: 'sentinal' may be misspelled - perhaps 'sentinel'?
+
+Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20230128063558.5850-1-Frank.Sae@motor-comm.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/motorcomm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1804,7 +1804,7 @@ static const struct mdio_device_id __may
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
+- { /* sentinal */ }
++ { /* sentinel */ }
+ };
+
+ MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);
diff --git a/target/linux/generic/backport-6.1/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch b/target/linux/generic/backport-6.1/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch
new file mode 100644
index 0000000000..076fa82d26
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch
@@ -0,0 +1,29 @@
+From 3c1dc22162d673d595855d24f95200ed2643f88f Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@motor-comm.com>
+Date: Sat, 28 Jan 2023 14:35:58 +0800
+Subject: [PATCH] net: phy: motorcomm: change the phy id of yt8521 and yt8531s
+ to lowercase
+
+The phy id is usually defined in lower case.
+
+Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20230128063558.5850-2-Frank.Sae@motor-comm.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/motorcomm.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -12,8 +12,8 @@
+ #include <linux/phy.h>
+
+ #define PHY_ID_YT8511 0x0000010a
+-#define PHY_ID_YT8521 0x0000011A
+-#define PHY_ID_YT8531S 0x4F51E91A
++#define PHY_ID_YT8521 0x0000011a
++#define PHY_ID_YT8531S 0x4f51e91a
+
+ /* YT8521/YT8531S Register Overview
+ * UTP Register space | FIBER Register space
diff --git a/target/linux/generic/backport-6.1/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch b/target/linux/generic/backport-6.1/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch
new file mode 100644
index 0000000000..ba9a6ab4cc
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch
@@ -0,0 +1,107 @@
+From 4869a146cd60fc8115230f0a45e15e534c531922 Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@motor-comm.com>
+Date: Thu, 2 Feb 2023 11:00:34 +0800
+Subject: [PATCH] net: phy: Add BIT macro for Motorcomm yt8521/yt8531 gigabit
+ ethernet phy
+
+Add BIT macro for Motorcomm yt8521/yt8531 gigabit ethernet phy.
+ This is a preparatory patch. Add BIT macro for 0xA012 reg, and
+ supplement for 0xA001 and 0xA003 reg. These will be used to support dts.
+
+Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/motorcomm.c | 55 ++++++++++++++++++++++++++++++++++---
+ 1 file changed, 51 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -161,6 +161,11 @@
+
+ #define YT8521_CHIP_CONFIG_REG 0xA001
+ #define YT8521_CCR_SW_RST BIT(15)
++/* 1b0 disable 1.9ns rxc clock delay *default*
++ * 1b1 enable 1.9ns rxc clock delay
++ */
++#define YT8521_CCR_RXC_DLY_EN BIT(8)
++#define YT8521_CCR_RXC_DLY_1_900_NS 1900
+
+ #define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0))
+ #define YT8521_CCR_MODE_UTP_TO_RGMII 0
+@@ -178,22 +183,41 @@
+ #define YT8521_MODE_POLL 0x3
+
+ #define YT8521_RGMII_CONFIG1_REG 0xA003
+-
++/* 1b0 use original tx_clk_rgmii *default*
++ * 1b1 use inverted tx_clk_rgmii.
++ */
++#define YT8521_RC1R_TX_CLK_SEL_INVERTED BIT(14)
+ /* TX Gig-E Delay is bits 3:0, default 0x1
+ * TX Fast-E Delay is bits 7:4, default 0xf
+ * RX Delay is bits 13:10, default 0x0
+ * Delay = 150ps * N
+ * On = 2250ps, off = 0ps
+ */
+-#define YT8521_RC1R_RX_DELAY_MASK (0xF << 10)
++#define YT8521_RC1R_RX_DELAY_MASK GENMASK(13, 10)
+ #define YT8521_RC1R_RX_DELAY_EN (0xF << 10)
+ #define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10)
+-#define YT8521_RC1R_FE_TX_DELAY_MASK (0xF << 4)
++#define YT8521_RC1R_FE_TX_DELAY_MASK GENMASK(7, 4)
+ #define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4)
+ #define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4)
+-#define YT8521_RC1R_GE_TX_DELAY_MASK (0xF << 0)
++#define YT8521_RC1R_GE_TX_DELAY_MASK GENMASK(3, 0)
+ #define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0)
+ #define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0)
++#define YT8521_RC1R_RGMII_0_000_NS 0
++#define YT8521_RC1R_RGMII_0_150_NS 1
++#define YT8521_RC1R_RGMII_0_300_NS 2
++#define YT8521_RC1R_RGMII_0_450_NS 3
++#define YT8521_RC1R_RGMII_0_600_NS 4
++#define YT8521_RC1R_RGMII_0_750_NS 5
++#define YT8521_RC1R_RGMII_0_900_NS 6
++#define YT8521_RC1R_RGMII_1_050_NS 7
++#define YT8521_RC1R_RGMII_1_200_NS 8
++#define YT8521_RC1R_RGMII_1_350_NS 9
++#define YT8521_RC1R_RGMII_1_500_NS 10
++#define YT8521_RC1R_RGMII_1_650_NS 11
++#define YT8521_RC1R_RGMII_1_800_NS 12
++#define YT8521_RC1R_RGMII_1_950_NS 13
++#define YT8521_RC1R_RGMII_2_100_NS 14
++#define YT8521_RC1R_RGMII_2_250_NS 15
+
+ #define YTPHY_MISC_CONFIG_REG 0xA006
+ #define YTPHY_MCR_FIBER_SPEED_MASK BIT(0)
+@@ -222,6 +246,29 @@
+ */
+ #define YTPHY_WCR_TYPE_PULSE BIT(0)
+
++#define YTPHY_SYNCE_CFG_REG 0xA012
++#define YT8521_SCR_SYNCE_ENABLE BIT(5)
++/* 1b0 output 25m clock
++ * 1b1 output 125m clock *default*
++ */
++#define YT8521_SCR_CLK_FRE_SEL_125M BIT(3)
++#define YT8521_SCR_CLK_SRC_MASK GENMASK(2, 1)
++#define YT8521_SCR_CLK_SRC_PLL_125M 0
++#define YT8521_SCR_CLK_SRC_UTP_RX 1
++#define YT8521_SCR_CLK_SRC_SDS_RX 2
++#define YT8521_SCR_CLK_SRC_REF_25M 3
++#define YT8531_SCR_SYNCE_ENABLE BIT(6)
++/* 1b0 output 25m clock *default*
++ * 1b1 output 125m clock
++ */
++#define YT8531_SCR_CLK_FRE_SEL_125M BIT(4)
++#define YT8531_SCR_CLK_SRC_MASK GENMASK(3, 1)
++#define YT8531_SCR_CLK_SRC_PLL_125M 0
++#define YT8531_SCR_CLK_SRC_UTP_RX 1
++#define YT8531_SCR_CLK_SRC_SDS_RX 2
++#define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL 3
++#define YT8531_SCR_CLK_SRC_REF_25M 4
++#define YT8531_SCR_CLK_SRC_SSC_25M 5
+ #define YT8531S_SYNCE_CFG_REG 0xA012
+ #define YT8531S_SCR_SYNCE_ENABLE BIT(6)
+
diff --git a/target/linux/generic/backport-6.1/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch b/target/linux/generic/backport-6.1/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch
new file mode 100644
index 0000000000..6d89fae84c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch
@@ -0,0 +1,343 @@
+From a6e68f0f8769f79c67cdcfb6302feecd36197dec Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@motor-comm.com>
+Date: Thu, 2 Feb 2023 11:00:35 +0800
+Subject: [PATCH] net: phy: Add dts support for Motorcomm yt8521 gigabit
+ ethernet phy
+
+Add dts support for Motorcomm yt8521 gigabit ethernet phy.
+ Add ytphy_rgmii_clk_delay_config function to support dst config for
+ the delay of rgmii clk. This funciont is common for yt8521, yt8531s
+ and yt8531.
+ This patch has been verified on AM335x platform.
+
+Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/motorcomm.c | 253 ++++++++++++++++++++++++++++--------
+ 1 file changed, 199 insertions(+), 54 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -10,6 +10,7 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/phy.h>
++#include <linux/of.h>
+
+ #define PHY_ID_YT8511 0x0000010a
+ #define PHY_ID_YT8521 0x0000011a
+@@ -187,21 +188,9 @@
+ * 1b1 use inverted tx_clk_rgmii.
+ */
+ #define YT8521_RC1R_TX_CLK_SEL_INVERTED BIT(14)
+-/* TX Gig-E Delay is bits 3:0, default 0x1
+- * TX Fast-E Delay is bits 7:4, default 0xf
+- * RX Delay is bits 13:10, default 0x0
+- * Delay = 150ps * N
+- * On = 2250ps, off = 0ps
+- */
+ #define YT8521_RC1R_RX_DELAY_MASK GENMASK(13, 10)
+-#define YT8521_RC1R_RX_DELAY_EN (0xF << 10)
+-#define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10)
+ #define YT8521_RC1R_FE_TX_DELAY_MASK GENMASK(7, 4)
+-#define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4)
+-#define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4)
+ #define YT8521_RC1R_GE_TX_DELAY_MASK GENMASK(3, 0)
+-#define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0)
+-#define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0)
+ #define YT8521_RC1R_RGMII_0_000_NS 0
+ #define YT8521_RC1R_RGMII_0_150_NS 1
+ #define YT8521_RC1R_RGMII_0_300_NS 2
+@@ -274,6 +263,10 @@
+
+ /* Extended Register end */
+
++#define YTPHY_DTS_OUTPUT_CLK_DIS 0
++#define YTPHY_DTS_OUTPUT_CLK_25M 25000000
++#define YTPHY_DTS_OUTPUT_CLK_125M 125000000
++
+ struct yt8521_priv {
+ /* combo_advertising is used for case of YT8521 in combo mode,
+ * this means that yt8521 may work in utp or fiber mode which depends
+@@ -641,6 +634,142 @@ static int yt8521_write_page(struct phy_
+ };
+
+ /**
++ * struct ytphy_cfg_reg_map - map a config value to a register value
++ * @cfg: value in device configuration
++ * @reg: value in the register
++ */
++struct ytphy_cfg_reg_map {
++ u32 cfg;
++ u32 reg;
++};
++
++static const struct ytphy_cfg_reg_map ytphy_rgmii_delays[] = {
++ /* for tx delay / rx delay with YT8521_CCR_RXC_DLY_EN is not set. */
++ { 0, YT8521_RC1R_RGMII_0_000_NS },
++ { 150, YT8521_RC1R_RGMII_0_150_NS },
++ { 300, YT8521_RC1R_RGMII_0_300_NS },
++ { 450, YT8521_RC1R_RGMII_0_450_NS },
++ { 600, YT8521_RC1R_RGMII_0_600_NS },
++ { 750, YT8521_RC1R_RGMII_0_750_NS },
++ { 900, YT8521_RC1R_RGMII_0_900_NS },
++ { 1050, YT8521_RC1R_RGMII_1_050_NS },
++ { 1200, YT8521_RC1R_RGMII_1_200_NS },
++ { 1350, YT8521_RC1R_RGMII_1_350_NS },
++ { 1500, YT8521_RC1R_RGMII_1_500_NS },
++ { 1650, YT8521_RC1R_RGMII_1_650_NS },
++ { 1800, YT8521_RC1R_RGMII_1_800_NS },
++ { 1950, YT8521_RC1R_RGMII_1_950_NS }, /* default tx/rx delay */
++ { 2100, YT8521_RC1R_RGMII_2_100_NS },
++ { 2250, YT8521_RC1R_RGMII_2_250_NS },
++
++ /* only for rx delay with YT8521_CCR_RXC_DLY_EN is set. */
++ { 0 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_000_NS },
++ { 150 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_150_NS },
++ { 300 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_300_NS },
++ { 450 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_450_NS },
++ { 600 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_600_NS },
++ { 750 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_750_NS },
++ { 900 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_0_900_NS },
++ { 1050 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_050_NS },
++ { 1200 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_200_NS },
++ { 1350 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_350_NS },
++ { 1500 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_500_NS },
++ { 1650 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_650_NS },
++ { 1800 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_800_NS },
++ { 1950 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_1_950_NS },
++ { 2100 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_2_100_NS },
++ { 2250 + YT8521_CCR_RXC_DLY_1_900_NS, YT8521_RC1R_RGMII_2_250_NS }
++};
++
++static u32 ytphy_get_delay_reg_value(struct phy_device *phydev,
++ const char *prop_name,
++ const struct ytphy_cfg_reg_map *tbl,
++ int tb_size,
++ u16 *rxc_dly_en,
++ u32 dflt)
++{
++ struct device_node *node = phydev->mdio.dev.of_node;
++ int tb_size_half = tb_size / 2;
++ u32 val;
++ int i;
++
++ if (of_property_read_u32(node, prop_name, &val))
++ goto err_dts_val;
++
++ /* when rxc_dly_en is NULL, it is get the delay for tx, only half of
++ * tb_size is valid.
++ */
++ if (!rxc_dly_en)
++ tb_size = tb_size_half;
++
++ for (i = 0; i < tb_size; i++) {
++ if (tbl[i].cfg == val) {
++ if (rxc_dly_en && i < tb_size_half)
++ *rxc_dly_en = 0;
++ return tbl[i].reg;
++ }
++ }
++
++ phydev_warn(phydev, "Unsupported value %d for %s using default (%u)\n",
++ val, prop_name, dflt);
++
++err_dts_val:
++ /* when rxc_dly_en is not NULL, it is get the delay for rx.
++ * The rx default in dts and ytphy_rgmii_clk_delay_config is 1950 ps,
++ * so YT8521_CCR_RXC_DLY_EN should not be set.
++ */
++ if (rxc_dly_en)
++ *rxc_dly_en = 0;
++
++ return dflt;
++}
++
++static int ytphy_rgmii_clk_delay_config(struct phy_device *phydev)
++{
++ int tb_size = ARRAY_SIZE(ytphy_rgmii_delays);
++ u16 rxc_dly_en = YT8521_CCR_RXC_DLY_EN;
++ u32 rx_reg, tx_reg;
++ u16 mask, val = 0;
++ int ret;
++
++ rx_reg = ytphy_get_delay_reg_value(phydev, "rx-internal-delay-ps",
++ ytphy_rgmii_delays, tb_size,
++ &rxc_dly_en,
++ YT8521_RC1R_RGMII_1_950_NS);
++ tx_reg = ytphy_get_delay_reg_value(phydev, "tx-internal-delay-ps",
++ ytphy_rgmii_delays, tb_size, NULL,
++ YT8521_RC1R_RGMII_1_950_NS);
++
++ switch (phydev->interface) {
++ case PHY_INTERFACE_MODE_RGMII:
++ rxc_dly_en = 0;
++ break;
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg);
++ break;
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ rxc_dly_en = 0;
++ val |= FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
++ break;
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg) |
++ FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
++ break;
++ default: /* do not support other modes */
++ return -EOPNOTSUPP;
++ }
++
++ ret = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
++ YT8521_CCR_RXC_DLY_EN, rxc_dly_en);
++ if (ret < 0)
++ return ret;
++
++ /* Generally, it is not necessary to adjust YT8521_RC1R_FE_TX_DELAY */
++ mask = YT8521_RC1R_RX_DELAY_MASK | YT8521_RC1R_GE_TX_DELAY_MASK;
++ return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
++}
++
++/**
+ * yt8521_probe() - read chip config then set suitable polling_mode
+ * @phydev: a pointer to a &struct phy_device
+ *
+@@ -648,9 +777,12 @@ static int yt8521_write_page(struct phy_
+ */
+ static int yt8521_probe(struct phy_device *phydev)
+ {
++ struct device_node *node = phydev->mdio.dev.of_node;
+ struct device *dev = &phydev->mdio.dev;
+ struct yt8521_priv *priv;
+ int chip_config;
++ u16 mask, val;
++ u32 freq;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+@@ -695,7 +827,45 @@ static int yt8521_probe(struct phy_devic
+ return ret;
+ }
+
+- return 0;
++ if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
++ freq = YTPHY_DTS_OUTPUT_CLK_DIS;
++
++ if (phydev->drv->phy_id == PHY_ID_YT8521) {
++ switch (freq) {
++ case YTPHY_DTS_OUTPUT_CLK_DIS:
++ mask = YT8521_SCR_SYNCE_ENABLE;
++ val = 0;
++ break;
++ case YTPHY_DTS_OUTPUT_CLK_25M:
++ mask = YT8521_SCR_SYNCE_ENABLE |
++ YT8521_SCR_CLK_SRC_MASK |
++ YT8521_SCR_CLK_FRE_SEL_125M;
++ val = YT8521_SCR_SYNCE_ENABLE |
++ FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
++ YT8521_SCR_CLK_SRC_REF_25M);
++ break;
++ case YTPHY_DTS_OUTPUT_CLK_125M:
++ mask = YT8521_SCR_SYNCE_ENABLE |
++ YT8521_SCR_CLK_SRC_MASK |
++ YT8521_SCR_CLK_FRE_SEL_125M;
++ val = YT8521_SCR_SYNCE_ENABLE |
++ YT8521_SCR_CLK_FRE_SEL_125M |
++ FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
++ YT8521_SCR_CLK_SRC_PLL_125M);
++ break;
++ default:
++ phydev_warn(phydev, "Freq err:%u\n", freq);
++ return -EINVAL;
++ }
++ } else if (phydev->drv->phy_id == PHY_ID_YT8531S) {
++ return 0;
++ } else {
++ phydev_warn(phydev, "PHY id err\n");
++ return -EINVAL;
++ }
++
++ return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
++ val);
+ }
+
+ /**
+@@ -1180,61 +1350,36 @@ static int yt8521_resume(struct phy_devi
+ */
+ static int yt8521_config_init(struct phy_device *phydev)
+ {
++ struct device_node *node = phydev->mdio.dev.of_node;
+ int old_page;
+ int ret = 0;
+- u16 val;
+
+ old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+- switch (phydev->interface) {
+- case PHY_INTERFACE_MODE_RGMII:
+- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+- val |= YT8521_RC1R_RX_DELAY_DIS;
+- break;
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+- val |= YT8521_RC1R_RX_DELAY_EN;
+- break;
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+- val |= YT8521_RC1R_RX_DELAY_DIS;
+- break;
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+- val |= YT8521_RC1R_RX_DELAY_EN;
+- break;
+- case PHY_INTERFACE_MODE_SGMII:
+- break;
+- default: /* do not support other modes */
+- ret = -EOPNOTSUPP;
+- goto err_restore_page;
+- }
+-
+ /* set rgmii delay mode */
+ if (phydev->interface != PHY_INTERFACE_MODE_SGMII) {
+- ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG,
+- (YT8521_RC1R_RX_DELAY_MASK |
+- YT8521_RC1R_FE_TX_DELAY_MASK |
+- YT8521_RC1R_GE_TX_DELAY_MASK),
+- val);
++ ret = ytphy_rgmii_clk_delay_config(phydev);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+- /* disable auto sleep */
+- ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
+- YT8521_ESC1R_SLEEP_SW, 0);
+- if (ret < 0)
+- goto err_restore_page;
+-
+- /* enable RXC clock when no wire plug */
+- ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
+- YT8521_CGR_RX_CLK_EN, 0);
+- if (ret < 0)
+- goto err_restore_page;
++ if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
++ /* disable auto sleep */
++ ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
++ YT8521_ESC1R_SLEEP_SW, 0);
++ if (ret < 0)
++ goto err_restore_page;
++ }
+
++ if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
++ /* enable RXC clock when no wire plug */
++ ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
++ YT8521_CGR_RX_CLK_EN, 0);
++ if (ret < 0)
++ goto err_restore_page;
++ }
+ err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+ }
diff --git a/target/linux/generic/backport-6.1/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch b/target/linux/generic/backport-6.1/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch
new file mode 100644
index 0000000000..86fc04695c
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch
@@ -0,0 +1,100 @@
+From 36152f87dda4af221b16258751451d9cd3d0fb0b Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@motor-comm.com>
+Date: Thu, 2 Feb 2023 11:00:36 +0800
+Subject: [PATCH] net: phy: Add dts support for Motorcomm yt8531s gigabit
+ ethernet phy
+
+Add dts support for Motorcomm yt8531s gigabit ethernet phy.
+ Change yt8521_probe to support clk config of yt8531s. Becase
+ yt8521_probe does the things which yt8531s is needed, so
+ removed yt8531s function.
+ This patch has been verified on AM335x platform with yt8531s board.
+
+Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/motorcomm.c | 51 ++++++++++++++++++++-----------------
+ 1 file changed, 27 insertions(+), 24 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -258,8 +258,6 @@
+ #define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL 3
+ #define YT8531_SCR_CLK_SRC_REF_25M 4
+ #define YT8531_SCR_CLK_SRC_SSC_25M 5
+-#define YT8531S_SYNCE_CFG_REG 0xA012
+-#define YT8531S_SCR_SYNCE_ENABLE BIT(6)
+
+ /* Extended Register end */
+
+@@ -858,7 +856,32 @@ static int yt8521_probe(struct phy_devic
+ return -EINVAL;
+ }
+ } else if (phydev->drv->phy_id == PHY_ID_YT8531S) {
+- return 0;
++ switch (freq) {
++ case YTPHY_DTS_OUTPUT_CLK_DIS:
++ mask = YT8531_SCR_SYNCE_ENABLE;
++ val = 0;
++ break;
++ case YTPHY_DTS_OUTPUT_CLK_25M:
++ mask = YT8531_SCR_SYNCE_ENABLE |
++ YT8531_SCR_CLK_SRC_MASK |
++ YT8531_SCR_CLK_FRE_SEL_125M;
++ val = YT8531_SCR_SYNCE_ENABLE |
++ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++ YT8531_SCR_CLK_SRC_REF_25M);
++ break;
++ case YTPHY_DTS_OUTPUT_CLK_125M:
++ mask = YT8531_SCR_SYNCE_ENABLE |
++ YT8531_SCR_CLK_SRC_MASK |
++ YT8531_SCR_CLK_FRE_SEL_125M;
++ val = YT8531_SCR_SYNCE_ENABLE |
++ YT8531_SCR_CLK_FRE_SEL_125M |
++ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++ YT8531_SCR_CLK_SRC_PLL_125M);
++ break;
++ default:
++ phydev_warn(phydev, "Freq err:%u\n", freq);
++ return -EINVAL;
++ }
+ } else {
+ phydev_warn(phydev, "PHY id err\n");
+ return -EINVAL;
+@@ -869,26 +892,6 @@ static int yt8521_probe(struct phy_devic
+ }
+
+ /**
+- * yt8531s_probe() - read chip config then set suitable polling_mode
+- * @phydev: a pointer to a &struct phy_device
+- *
+- * returns 0 or negative errno code
+- */
+-static int yt8531s_probe(struct phy_device *phydev)
+-{
+- int ret;
+-
+- /* Disable SyncE clock output by default */
+- ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG,
+- YT8531S_SCR_SYNCE_ENABLE, 0);
+- if (ret < 0)
+- return ret;
+-
+- /* same as yt8521_probe */
+- return yt8521_probe(phydev);
+-}
+-
+-/**
+ * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
+ * @phydev: a pointer to a &struct phy_device
+ *
+@@ -1970,7 +1973,7 @@ static struct phy_driver motorcomm_phy_d
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
+ .name = "YT8531S Gigabit Ethernet",
+ .get_features = yt8521_get_features,
+- .probe = yt8531s_probe,
++ .probe = yt8521_probe,
+ .read_page = yt8521_read_page,
+ .write_page = yt8521_write_page,
+ .get_wol = ytphy_get_wol,
diff --git a/target/linux/generic/backport-6.1/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch b/target/linux/generic/backport-6.1/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch
new file mode 100644
index 0000000000..9dce5a8add
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch
@@ -0,0 +1,302 @@
+From 4ac94f728a588e7096dd5010cd7141a309ea7805 Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@motor-comm.com>
+Date: Thu, 2 Feb 2023 11:00:37 +0800
+Subject: [PATCH] net: phy: Add driver for Motorcomm yt8531 gigabit ethernet
+ phy
+
+Add a driver for the motorcomm yt8531 gigabit ethernet phy. We have
+ verified the driver on AM335x platform with yt8531 board. On the
+ board, yt8531 gigabit ethernet phy works in utp mode, RGMII
+ interface, supports 1000M/100M/10M speeds, and wol(magic package).
+
+Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/Kconfig | 2 +-
+ drivers/net/phy/motorcomm.c | 208 +++++++++++++++++++++++++++++++++++-
+ 2 files changed, 207 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -245,7 +245,7 @@ config MOTORCOMM_PHY
+ tristate "Motorcomm PHYs"
+ help
+ Enables support for Motorcomm network PHYs.
+- Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
++ Currently supports YT85xx Gigabit Ethernet PHYs.
+
+ config NATIONAL_PHY
+ tristate "National Semiconductor PHYs"
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+- * Motorcomm 8511/8521/8531S PHY driver.
++ * Motorcomm 8511/8521/8531/8531S PHY driver.
+ *
+ * Author: Peter Geis <pgwipeout@gmail.com>
+ * Author: Frank <Frank.Sae@motor-comm.com>
+@@ -14,6 +14,7 @@
+
+ #define PHY_ID_YT8511 0x0000010a
+ #define PHY_ID_YT8521 0x0000011a
++#define PHY_ID_YT8531 0x4f51e91b
+ #define PHY_ID_YT8531S 0x4f51e91a
+
+ /* YT8521/YT8531S Register Overview
+@@ -517,6 +518,61 @@ err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+ }
+
++static int yt8531_set_wol(struct phy_device *phydev,
++ struct ethtool_wolinfo *wol)
++{
++ const u16 mac_addr_reg[] = {
++ YTPHY_WOL_MACADDR2_REG,
++ YTPHY_WOL_MACADDR1_REG,
++ YTPHY_WOL_MACADDR0_REG,
++ };
++ const u8 *mac_addr;
++ u16 mask, val;
++ int ret;
++ u8 i;
++
++ if (wol->wolopts & WAKE_MAGIC) {
++ mac_addr = phydev->attached_dev->dev_addr;
++
++ /* Store the device address for the magic packet */
++ for (i = 0; i < 3; i++) {
++ ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i],
++ ((mac_addr[i * 2] << 8)) |
++ (mac_addr[i * 2 + 1]));
++ if (ret < 0)
++ return ret;
++ }
++
++ /* Enable WOL feature */
++ mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
++ val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
++ val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
++ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
++ mask, val);
++ if (ret < 0)
++ return ret;
++
++ /* Enable WOL interrupt */
++ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
++ YTPHY_IER_WOL);
++ if (ret < 0)
++ return ret;
++ } else {
++ /* Disable WOL feature */
++ mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
++ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
++ mask, 0);
++
++ /* Disable WOL interrupt */
++ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
++ YTPHY_IER_WOL, 0);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
+ static int yt8511_read_page(struct phy_device *phydev)
+ {
+ return __phy_read(phydev, YT8511_PAGE_SELECT);
+@@ -767,6 +823,17 @@ static int ytphy_rgmii_clk_delay_config(
+ return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
+ }
+
++static int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev)
++{
++ int ret;
++
++ phy_lock_mdio_bus(phydev);
++ ret = ytphy_rgmii_clk_delay_config(phydev);
++ phy_unlock_mdio_bus(phydev);
++
++ return ret;
++}
++
+ /**
+ * yt8521_probe() - read chip config then set suitable polling_mode
+ * @phydev: a pointer to a &struct phy_device
+@@ -891,6 +958,43 @@ static int yt8521_probe(struct phy_devic
+ val);
+ }
+
++static int yt8531_probe(struct phy_device *phydev)
++{
++ struct device_node *node = phydev->mdio.dev.of_node;
++ u16 mask, val;
++ u32 freq;
++
++ if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
++ freq = YTPHY_DTS_OUTPUT_CLK_DIS;
++
++ switch (freq) {
++ case YTPHY_DTS_OUTPUT_CLK_DIS:
++ mask = YT8531_SCR_SYNCE_ENABLE;
++ val = 0;
++ break;
++ case YTPHY_DTS_OUTPUT_CLK_25M:
++ mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
++ YT8531_SCR_CLK_FRE_SEL_125M;
++ val = YT8531_SCR_SYNCE_ENABLE |
++ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++ YT8531_SCR_CLK_SRC_REF_25M);
++ break;
++ case YTPHY_DTS_OUTPUT_CLK_125M:
++ mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
++ YT8531_SCR_CLK_FRE_SEL_125M;
++ val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M |
++ FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++ YT8531_SCR_CLK_SRC_PLL_125M);
++ break;
++ default:
++ phydev_warn(phydev, "Freq err:%u\n", freq);
++ return -EINVAL;
++ }
++
++ return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
++ val);
++}
++
+ /**
+ * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
+ * @phydev: a pointer to a &struct phy_device
+@@ -1387,6 +1491,94 @@ err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+ }
+
++static int yt8531_config_init(struct phy_device *phydev)
++{
++ struct device_node *node = phydev->mdio.dev.of_node;
++ int ret;
++
++ ret = ytphy_rgmii_clk_delay_config_with_lock(phydev);
++ if (ret < 0)
++ return ret;
++
++ if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
++ /* disable auto sleep */
++ ret = ytphy_modify_ext_with_lock(phydev,
++ YT8521_EXTREG_SLEEP_CONTROL1_REG,
++ YT8521_ESC1R_SLEEP_SW, 0);
++ if (ret < 0)
++ return ret;
++ }
++
++ if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
++ /* enable RXC clock when no wire plug */
++ ret = ytphy_modify_ext_with_lock(phydev,
++ YT8521_CLOCK_GATING_REG,
++ YT8521_CGR_RX_CLK_EN, 0);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++/**
++ * yt8531_link_change_notify() - Adjust the tx clock direction according to
++ * the current speed and dts config.
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * NOTE: This function is only used to adapt to VF2 with JH7110 SoC. Please
++ * keep "motorcomm,tx-clk-adj-enabled" not exist in dts when the soc is not
++ * JH7110.
++ */
++static void yt8531_link_change_notify(struct phy_device *phydev)
++{
++ struct device_node *node = phydev->mdio.dev.of_node;
++ bool tx_clk_adj_enabled = false;
++ bool tx_clk_1000_inverted;
++ bool tx_clk_100_inverted;
++ bool tx_clk_10_inverted;
++ u16 val = 0;
++ int ret;
++
++ if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled"))
++ tx_clk_adj_enabled = true;
++
++ if (!tx_clk_adj_enabled)
++ return;
++
++ if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted"))
++ tx_clk_10_inverted = true;
++ if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted"))
++ tx_clk_100_inverted = true;
++ if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted"))
++ tx_clk_1000_inverted = true;
++
++ if (phydev->speed < 0)
++ return;
++
++ switch (phydev->speed) {
++ case SPEED_1000:
++ if (tx_clk_1000_inverted)
++ val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
++ break;
++ case SPEED_100:
++ if (tx_clk_100_inverted)
++ val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
++ break;
++ case SPEED_10:
++ if (tx_clk_10_inverted)
++ val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
++ break;
++ default:
++ return;
++ }
++
++ ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG,
++ YT8521_RC1R_TX_CLK_SEL_INVERTED, val);
++ if (ret < 0)
++ phydev_warn(phydev, "Modify TX_CLK_SEL err:%d\n", ret);
++}
++
+ /**
+ * yt8521_prepare_fiber_features() - A small helper function that setup
+ * fiber's features.
+@@ -1970,6 +2162,17 @@ static struct phy_driver motorcomm_phy_d
+ .resume = yt8521_resume,
+ },
+ {
++ PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
++ .name = "YT8531 Gigabit Ethernet",
++ .probe = yt8531_probe,
++ .config_init = yt8531_config_init,
++ .suspend = genphy_suspend,
++ .resume = genphy_resume,
++ .get_wol = ytphy_get_wol,
++ .set_wol = yt8531_set_wol,
++ .link_change_notify = yt8531_link_change_notify,
++ },
++ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
+ .name = "YT8531S Gigabit Ethernet",
+ .get_features = yt8521_get_features,
+@@ -1990,7 +2193,7 @@ static struct phy_driver motorcomm_phy_d
+
+ module_phy_driver(motorcomm_phy_drvs);
+
+-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
++MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver");
+ MODULE_AUTHOR("Peter Geis");
+ MODULE_AUTHOR("Frank");
+ MODULE_LICENSE("GPL");
+@@ -1998,6 +2201,7 @@ MODULE_LICENSE("GPL");
+ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
++ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
+ { /* sentinel */ }
+ };
diff --git a/target/linux/generic/backport-6.1/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch b/target/linux/generic/backport-6.1/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch
new file mode 100644
index 0000000000..29ae86dbbc
--- /dev/null
+++ b/target/linux/generic/backport-6.1/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch
@@ -0,0 +1,34 @@
+From 9753613f7399601f9bae6ee81e9d4274246c98ab Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <error27@gmail.com>
+Date: Wed, 15 Feb 2023 07:21:47 +0300
+Subject: [PATCH] net: phy: motorcomm: uninitialized variables in
+ yt8531_link_change_notify()
+
+These booleans are never set to false, but are just used without being
+initialized.
+
+Fixes: 4ac94f728a58 ("net: phy: Add driver for Motorcomm yt8531 gigabit ethernet phy")
+Signed-off-by: Dan Carpenter <error27@gmail.com>
+Reviewed-by: Frank Sae <Frank.Sae@motor-comm.com>
+Link: https://lore.kernel.org/r/Y+xd2yJet2ImHLoQ@kili
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/motorcomm.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1533,10 +1533,10 @@ static int yt8531_config_init(struct phy
+ static void yt8531_link_change_notify(struct phy_device *phydev)
+ {
+ struct device_node *node = phydev->mdio.dev.of_node;
++ bool tx_clk_1000_inverted = false;
++ bool tx_clk_100_inverted = false;
++ bool tx_clk_10_inverted = false;
+ bool tx_clk_adj_enabled = false;
+- bool tx_clk_1000_inverted;
+- bool tx_clk_100_inverted;
+- bool tx_clk_10_inverted;
+ u16 val = 0;
+ int ret;
+
diff --git a/target/linux/generic/backport-6.1/797-v5.17-net-usb-ax88179_178a-add-TSO-feature.patch b/target/linux/generic/backport-6.1/797-v5.17-net-usb-ax88179_178a-add-TSO-feature.patch
new file mode 100644
index 0000000000..a2168aaba5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/797-v5.17-net-usb-ax88179_178a-add-TSO-feature.patch
@@ -0,0 +1,68 @@
+From 16b1c4e01c89ba07367461e0bc4cb84993c2d027 Mon Sep 17 00:00:00 2001
+From: Jacky Chou <jackychou@asix.com.tw>
+Date: Mon, 15 Nov 2021 11:49:41 +0800
+Subject: [PATCH] net: usb: ax88179_178a: add TSO feature
+
+On low-effciency embedded platforms, transmission performance is poor
+due to on Bulk-out with single packet.
+Adding TSO feature improves the transmission performance and reduces
+the number of interrupt caused by Bulk-out complete.
+
+Reference to module, net: usb: aqc111.
+
+Signed-off-by: Jacky Chou <jackychou@asix.com.tw>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/ax88179_178a.c | 17 +++++++++++------
+ 1 file changed, 11 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/usb/ax88179_178a.c
++++ b/drivers/net/usb/ax88179_178a.c
+@@ -1377,11 +1377,12 @@ static int ax88179_bind(struct usbnet *d
+ dev->mii.phy_id = 0x03;
+ dev->mii.supports_gmii = 1;
+
+- dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+- NETIF_F_RXCSUM;
++ dev->net->features |= NETIF_F_SG | NETIF_F_IP_CSUM |
++ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO;
+
+- dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+- NETIF_F_RXCSUM;
++ dev->net->hw_features |= dev->net->features;
++
++ netif_set_gso_max_size(dev->net, 16384);
+
+ /* Enable checksum offload */
+ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+@@ -1587,17 +1588,19 @@ ax88179_tx_fixup(struct usbnet *dev, str
+ {
+ u32 tx_hdr1, tx_hdr2;
+ int frame_size = dev->maxpacket;
+- int mss = skb_shinfo(skb)->gso_size;
+ int headroom;
+ void *ptr;
+
+ tx_hdr1 = skb->len;
+- tx_hdr2 = mss;
++ tx_hdr2 = skb_shinfo(skb)->gso_size; /* Set TSO mss */
+ if (((skb->len + 8) % frame_size) == 0)
+ tx_hdr2 |= 0x80008000; /* Enable padding */
+
+ headroom = skb_headroom(skb) - 8;
+
++ if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb))
++ return NULL;
++
+ if ((skb_header_cloned(skb) || headroom < 0) &&
+ pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) {
+ dev_kfree_skb_any(skb);
+@@ -1608,6 +1611,8 @@ ax88179_tx_fixup(struct usbnet *dev, str
+ put_unaligned_le32(tx_hdr1, ptr);
+ put_unaligned_le32(tx_hdr2, ptr + 4);
+
++ usbnet_set_skb_tx_stats(skb, (skb_shinfo(skb)->gso_segs ?: 1), 0);
++
+ return skb;
+ }
+
diff --git a/target/linux/generic/backport-6.1/800-v6.0-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch b/target/linux/generic/backport-6.1/800-v6.0-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch
new file mode 100644
index 0000000000..b1072ce640
--- /dev/null
+++ b/target/linux/generic/backport-6.1/800-v6.0-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch
@@ -0,0 +1,125 @@
+From 13344f8ce8a0d98aa7f5d69ce3b47393c73a343b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 27 Dec 2021 15:59:04 +0100
+Subject: [PATCH] dt-bindings: leds: add Broadcom's BCM63138 controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Broadcom used 2 LEDs hardware blocks for their BCM63xx SoCs:
+1. Older one (BCM6318, BCM6328, BCM6362, BCM63268, BCM6838)
+2. Newer one (BCM6848, BCM6858, BCM63138, BCM63148, BCM63381, BCM68360)
+
+The newer one was also later also used on BCM4908 SoC.
+
+Old block is already documented in the leds-bcm6328.yaml. This binding
+documents the new one which uses different registers & programming. It's
+first used in BCM63138 thus the binding name.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+---
+ .../bindings/leds/leds-bcm63138.yaml | 95 +++++++++++++++++++
+ 1 file changed, 95 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/leds/leds-bcm63138.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/leds/leds-bcm63138.yaml
+@@ -0,0 +1,95 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/leds/leds-bcm63138.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom's BCM63138 LEDs controller
++
++maintainers:
++ - Rafał Miłecki <rafal@milecki.pl>
++
++description: |
++ This LEDs controller was first used on BCM63138 and later reused on BCM4908,
++ BCM6848, BCM6858, BCM63138, BCM63148, BCM63381 and BCM68360 SoCs.
++
++ It supports up to 32 LEDs that can be connected parallelly or serially. It
++ also includes limited support for hardware blinking.
++
++ Binding serially connected LEDs isn't documented yet.
++
++properties:
++ compatible:
++ oneOf:
++ - items:
++ - enum:
++ - brcm,bcm4908-leds
++ - brcm,bcm6848-leds
++ - brcm,bcm6858-leds
++ - brcm,bcm63148-leds
++ - brcm,bcm63381-leds
++ - brcm,bcm68360-leds
++ - const: brcm,bcm63138-leds
++ - const: brcm,bcm63138-leds
++
++ reg:
++ maxItems: 1
++
++ "#address-cells":
++ const: 1
++
++ "#size-cells":
++ const: 0
++
++patternProperties:
++ "^led@[a-f0-9]+$":
++ type: object
++
++ $ref: common.yaml#
++
++ properties:
++ reg:
++ maxItems: 1
++ description: LED pin number
++
++ active-low:
++ type: boolean
++ description: Makes LED active low.
++
++ required:
++ - reg
++
++ unevaluatedProperties: false
++
++required:
++ - reg
++ - "#address-cells"
++ - "#size-cells"
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/leds/common.h>
++
++ leds@ff800800 {
++ compatible = "brcm,bcm4908-leds", "brcm,bcm63138-leds";
++ reg = <0xff800800 0xdc>;
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ led@0 {
++ reg = <0x0>;
++ function = LED_FUNCTION_POWER;
++ color = <LED_COLOR_ID_GREEN>;
++ default-state = "on";
++ };
++
++ led@3 {
++ reg = <0x3>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_GREEN>;
++ active-low;
++ };
++ };
diff --git a/target/linux/generic/backport-6.1/800-v6.0-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch b/target/linux/generic/backport-6.1/800-v6.0-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch
new file mode 100644
index 0000000000..376584574f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/800-v6.0-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch
@@ -0,0 +1,356 @@
+From a0ba692072d89075d0a75c7ad9df31f2c1ee9a1c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 27 Dec 2021 15:59:05 +0100
+Subject: [PATCH] leds: bcm63138: add support for BCM63138 controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It's a new controller first introduced in BCM63138 SoC. Later it was
+also used in BCM4908, some BCM68xx and some BCM63xxx SoCs.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+---
+ drivers/leds/blink/Kconfig | 12 ++
+ drivers/leds/blink/Makefile | 1 +
+ drivers/leds/blink/leds-bcm63138.c | 308 +++++++++++++++++++++++++++++
+ 3 files changed, 321 insertions(+)
+ create mode 100644 drivers/leds/blink/leds-bcm63138.c
+
+--- a/drivers/leds/blink/Kconfig
++++ b/drivers/leds/blink/Kconfig
+@@ -1,3 +1,15 @@
++config LEDS_BCM63138
++ tristate "LED Support for Broadcom BCM63138 SoC"
++ depends on LEDS_CLASS
++ depends on ARCH_BCM4908 || ARCH_BCM_5301X || BCM63XX || COMPILE_TEST
++ depends on HAS_IOMEM
++ depends on OF
++ default ARCH_BCM4908
++ help
++ This option enables support for LED controller that is part of
++ BCM63138 SoC. The same hardware block is known to be also used
++ in BCM4908, BCM6848, BCM6858, BCM63148, BCM63381 and BCM68360.
++
+ config LEDS_LGM
+ tristate "LED support for LGM SoC series"
+ depends on X86 || COMPILE_TEST
+--- a/drivers/leds/blink/Makefile
++++ b/drivers/leds/blink/Makefile
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_LEDS_BCM63138) += leds-bcm63138.o
+ obj-$(CONFIG_LEDS_LGM) += leds-lgm-sso.o
+--- /dev/null
++++ b/drivers/leds/blink/leds-bcm63138.c
+@@ -0,0 +1,308 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
++ */
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/leds.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++#define BCM63138_MAX_LEDS 32
++#define BCM63138_MAX_BRIGHTNESS 9
++
++#define BCM63138_LED_BITS 4 /* how many bits control a single LED */
++#define BCM63138_LED_MASK ((1 << BCM63138_LED_BITS) - 1) /* 0xf */
++#define BCM63138_LEDS_PER_REG (32 / BCM63138_LED_BITS) /* 8 */
++
++#define BCM63138_GLB_CTRL 0x00
++#define BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL 0x00000002
++#define BCM63138_GLB_CTRL_SERIAL_LED_EN_POL 0x00000008
++#define BCM63138_MASK 0x04
++#define BCM63138_HW_LED_EN 0x08
++#define BCM63138_SERIAL_LED_SHIFT_SEL 0x0c
++#define BCM63138_FLASH_RATE_CTRL1 0x10
++#define BCM63138_FLASH_RATE_CTRL2 0x14
++#define BCM63138_FLASH_RATE_CTRL3 0x18
++#define BCM63138_FLASH_RATE_CTRL4 0x1c
++#define BCM63138_BRIGHT_CTRL1 0x20
++#define BCM63138_BRIGHT_CTRL2 0x24
++#define BCM63138_BRIGHT_CTRL3 0x28
++#define BCM63138_BRIGHT_CTRL4 0x2c
++#define BCM63138_POWER_LED_CFG 0x30
++#define BCM63138_HW_POLARITY 0xb4
++#define BCM63138_SW_DATA 0xb8
++#define BCM63138_SW_POLARITY 0xbc
++#define BCM63138_PARALLEL_LED_POLARITY 0xc0
++#define BCM63138_SERIAL_LED_POLARITY 0xc4
++#define BCM63138_HW_LED_STATUS 0xc8
++#define BCM63138_FLASH_CTRL_STATUS 0xcc
++#define BCM63138_FLASH_BRT_CTRL 0xd0
++#define BCM63138_FLASH_P_LED_OUT_STATUS 0xd4
++#define BCM63138_FLASH_S_LED_OUT_STATUS 0xd8
++
++struct bcm63138_leds {
++ struct device *dev;
++ void __iomem *base;
++ spinlock_t lock;
++};
++
++struct bcm63138_led {
++ struct bcm63138_leds *leds;
++ struct led_classdev cdev;
++ u32 pin;
++ bool active_low;
++};
++
++/*
++ * I/O access
++ */
++
++static void bcm63138_leds_write(struct bcm63138_leds *leds, unsigned int reg,
++ u32 data)
++{
++ writel(data, leds->base + reg);
++}
++
++static unsigned long bcm63138_leds_read(struct bcm63138_leds *leds,
++ unsigned int reg)
++{
++ return readl(leds->base + reg);
++}
++
++static void bcm63138_leds_update_bits(struct bcm63138_leds *leds,
++ unsigned int reg, u32 mask, u32 val)
++{
++ WARN_ON(val & ~mask);
++
++ bcm63138_leds_write(leds, reg, (bcm63138_leds_read(leds, reg) & ~mask) | (val & mask));
++}
++
++/*
++ * Helpers
++ */
++
++static void bcm63138_leds_set_flash_rate(struct bcm63138_leds *leds,
++ struct bcm63138_led *led,
++ u8 value)
++{
++ int reg_offset = (led->pin >> fls((BCM63138_LEDS_PER_REG - 1))) * 4;
++ int shift = (led->pin & (BCM63138_LEDS_PER_REG - 1)) * BCM63138_LED_BITS;
++
++ bcm63138_leds_update_bits(leds, BCM63138_FLASH_RATE_CTRL1 + reg_offset,
++ BCM63138_LED_MASK << shift, value << shift);
++}
++
++static void bcm63138_leds_set_bright(struct bcm63138_leds *leds,
++ struct bcm63138_led *led,
++ u8 value)
++{
++ int reg_offset = (led->pin >> fls((BCM63138_LEDS_PER_REG - 1))) * 4;
++ int shift = (led->pin & (BCM63138_LEDS_PER_REG - 1)) * BCM63138_LED_BITS;
++
++ bcm63138_leds_update_bits(leds, BCM63138_BRIGHT_CTRL1 + reg_offset,
++ BCM63138_LED_MASK << shift, value << shift);
++}
++
++static void bcm63138_leds_enable_led(struct bcm63138_leds *leds,
++ struct bcm63138_led *led,
++ enum led_brightness value)
++{
++ u32 bit = BIT(led->pin);
++
++ bcm63138_leds_update_bits(leds, BCM63138_SW_DATA, bit,
++ value == LED_OFF ? 0 : bit);
++}
++
++/*
++ * API callbacks
++ */
++
++static void bcm63138_leds_brightness_set(struct led_classdev *led_cdev,
++ enum led_brightness value)
++{
++ struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev);
++ struct bcm63138_leds *leds = led->leds;
++ unsigned long flags;
++
++ spin_lock_irqsave(&leds->lock, flags);
++
++ bcm63138_leds_enable_led(leds, led, value);
++ if (!value)
++ bcm63138_leds_set_flash_rate(leds, led, 0);
++ else
++ bcm63138_leds_set_bright(leds, led, value);
++
++ spin_unlock_irqrestore(&leds->lock, flags);
++}
++
++static int bcm63138_leds_blink_set(struct led_classdev *led_cdev,
++ unsigned long *delay_on,
++ unsigned long *delay_off)
++{
++ struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev);
++ struct bcm63138_leds *leds = led->leds;
++ unsigned long flags;
++ u8 value;
++
++ if (!*delay_on && !*delay_off) {
++ *delay_on = 640;
++ *delay_off = 640;
++ }
++
++ if (*delay_on != *delay_off) {
++ dev_dbg(led_cdev->dev, "Blinking at unequal delays is not supported\n");
++ return -EINVAL;
++ }
++
++ switch (*delay_on) {
++ case 1152 ... 1408: /* 1280 ms ± 10% */
++ value = 0x7;
++ break;
++ case 576 ... 704: /* 640 ms ± 10% */
++ value = 0x6;
++ break;
++ case 288 ... 352: /* 320 ms ± 10% */
++ value = 0x5;
++ break;
++ case 126 ... 154: /* 140 ms ± 10% */
++ value = 0x4;
++ break;
++ case 59 ... 72: /* 65 ms ± 10% */
++ value = 0x3;
++ break;
++ default:
++ dev_dbg(led_cdev->dev, "Blinking delay value %lu is unsupported\n",
++ *delay_on);
++ return -EINVAL;
++ }
++
++ spin_lock_irqsave(&leds->lock, flags);
++
++ bcm63138_leds_enable_led(leds, led, BCM63138_MAX_BRIGHTNESS);
++ bcm63138_leds_set_flash_rate(leds, led, value);
++
++ spin_unlock_irqrestore(&leds->lock, flags);
++
++ return 0;
++}
++
++/*
++ * LED driver
++ */
++
++static void bcm63138_leds_create_led(struct bcm63138_leds *leds,
++ struct device_node *np)
++{
++ struct led_init_data init_data = {
++ .fwnode = of_fwnode_handle(np),
++ };
++ struct device *dev = leds->dev;
++ struct bcm63138_led *led;
++ struct pinctrl *pinctrl;
++ u32 bit;
++ int err;
++
++ led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
++ if (!led) {
++ dev_err(dev, "Failed to alloc LED\n");
++ return;
++ }
++
++ led->leds = leds;
++
++ if (of_property_read_u32(np, "reg", &led->pin)) {
++ dev_err(dev, "Missing \"reg\" property in %pOF\n", np);
++ goto err_free;
++ }
++
++ if (led->pin >= BCM63138_MAX_LEDS) {
++ dev_err(dev, "Invalid \"reg\" value %d\n", led->pin);
++ goto err_free;
++ }
++
++ led->active_low = of_property_read_bool(np, "active-low");
++
++ led->cdev.max_brightness = BCM63138_MAX_BRIGHTNESS;
++ led->cdev.brightness_set = bcm63138_leds_brightness_set;
++ led->cdev.blink_set = bcm63138_leds_blink_set;
++
++ err = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
++ if (err) {
++ dev_err(dev, "Failed to register LED %pOF: %d\n", np, err);
++ goto err_free;
++ }
++
++ pinctrl = devm_pinctrl_get_select_default(led->cdev.dev);
++ if (IS_ERR(pinctrl) && PTR_ERR(pinctrl) != -ENODEV) {
++ dev_warn(led->cdev.dev, "Failed to select %pOF pinctrl: %ld\n",
++ np, PTR_ERR(pinctrl));
++ }
++
++ bit = BIT(led->pin);
++ bcm63138_leds_update_bits(leds, BCM63138_PARALLEL_LED_POLARITY, bit,
++ led->active_low ? 0 : bit);
++ bcm63138_leds_update_bits(leds, BCM63138_HW_LED_EN, bit, 0);
++ bcm63138_leds_set_flash_rate(leds, led, 0);
++ bcm63138_leds_enable_led(leds, led, led->cdev.brightness);
++
++ return;
++
++err_free:
++ devm_kfree(dev, led);
++}
++
++static int bcm63138_leds_probe(struct platform_device *pdev)
++{
++ struct device_node *np = dev_of_node(&pdev->dev);
++ struct device *dev = &pdev->dev;
++ struct bcm63138_leds *leds;
++ struct device_node *child;
++
++ leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
++ if (!leds)
++ return -ENOMEM;
++
++ leds->dev = dev;
++
++ leds->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(leds->base))
++ return PTR_ERR(leds->base);
++
++ spin_lock_init(&leds->lock);
++
++ bcm63138_leds_write(leds, BCM63138_GLB_CTRL,
++ BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL |
++ BCM63138_GLB_CTRL_SERIAL_LED_EN_POL);
++ bcm63138_leds_write(leds, BCM63138_HW_LED_EN, 0);
++ bcm63138_leds_write(leds, BCM63138_SERIAL_LED_POLARITY, 0);
++ bcm63138_leds_write(leds, BCM63138_PARALLEL_LED_POLARITY, 0);
++
++ for_each_available_child_of_node(np, child) {
++ bcm63138_leds_create_led(leds, child);
++ }
++
++ return 0;
++}
++
++static const struct of_device_id bcm63138_leds_of_match_table[] = {
++ { .compatible = "brcm,bcm63138-leds", },
++ { },
++};
++
++static struct platform_driver bcm63138_leds_driver = {
++ .probe = bcm63138_leds_probe,
++ .driver = {
++ .name = "leds-bcm63xxx",
++ .of_match_table = bcm63138_leds_of_match_table,
++ },
++};
++
++module_platform_driver(bcm63138_leds_driver);
++
++MODULE_AUTHOR("Rafał Miłecki");
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(of, bcm63138_leds_of_match_table);
diff --git a/target/linux/generic/backport-6.1/801-v6.0-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch b/target/linux/generic/backport-6.1/801-v6.0-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch
new file mode 100644
index 0000000000..483826abed
--- /dev/null
+++ b/target/linux/generic/backport-6.1/801-v6.0-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch
@@ -0,0 +1,30 @@
+From 13b64a0c19059b38150c79d65d350ae44034c5df Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sun, 17 Jul 2022 14:42:46 +0200
+Subject: [PATCH] dt-bindings: leds: leds-bcm63138: unify full stops in
+ descriptions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Description of "reg" doesn't have full stop at the end. It makes sense
+as it's a one-sentence only. Use the same style for "active-low".
+
+Reported-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+---
+ Documentation/devicetree/bindings/leds/leds-bcm63138.yaml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/leds/leds-bcm63138.yaml
++++ b/Documentation/devicetree/bindings/leds/leds-bcm63138.yaml
+@@ -54,7 +54,7 @@ patternProperties:
+
+ active-low:
+ type: boolean
+- description: Makes LED active low.
++ description: Makes LED active low
+
+ required:
+ - reg
diff --git a/target/linux/generic/backport-6.1/801-v6.0-0002-leds-add-help-info-about-BCM63138-module-name.patch b/target/linux/generic/backport-6.1/801-v6.0-0002-leds-add-help-info-about-BCM63138-module-name.patch
new file mode 100644
index 0000000000..5430b1f084
--- /dev/null
+++ b/target/linux/generic/backport-6.1/801-v6.0-0002-leds-add-help-info-about-BCM63138-module-name.patch
@@ -0,0 +1,28 @@
+From bcc607cdbb1f931111196699426f0cb83bfb296a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sun, 17 Jul 2022 14:42:47 +0200
+Subject: [PATCH] leds: add help info about BCM63138 module name
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It's what we do for all other LEDs drivers.
+
+Reported-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+---
+ drivers/leds/blink/Kconfig | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/leds/blink/Kconfig
++++ b/drivers/leds/blink/Kconfig
+@@ -10,6 +10,8 @@ config LEDS_BCM63138
+ BCM63138 SoC. The same hardware block is known to be also used
+ in BCM4908, BCM6848, BCM6858, BCM63148, BCM63381 and BCM68360.
+
++ If compiled as module it will be called leds-bcm63138.
++
+ config LEDS_LGM
+ tristate "LED support for LGM SoC series"
+ depends on X86 || COMPILE_TEST
diff --git a/target/linux/generic/backport-6.1/801-v6.0-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch b/target/linux/generic/backport-6.1/801-v6.0-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch
new file mode 100644
index 0000000000..e125a54613
--- /dev/null
+++ b/target/linux/generic/backport-6.1/801-v6.0-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch
@@ -0,0 +1,30 @@
+From 92cfc71ee2ddfb499ed53e21b28bdf8739bc70bc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sun, 17 Jul 2022 14:42:48 +0200
+Subject: [PATCH] leds: leds-bcm63138: get rid of LED_OFF
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The whole "enum led_brightness" is marked as obsolete. Replace it with a
+(non-)zero check.
+
+Reported-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+---
+ drivers/leds/blink/leds-bcm63138.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/leds/blink/leds-bcm63138.c
++++ b/drivers/leds/blink/leds-bcm63138.c
+@@ -113,8 +113,7 @@ static void bcm63138_leds_enable_led(str
+ {
+ u32 bit = BIT(led->pin);
+
+- bcm63138_leds_update_bits(leds, BCM63138_SW_DATA, bit,
+- value == LED_OFF ? 0 : bit);
++ bcm63138_leds_update_bits(leds, BCM63138_SW_DATA, bit, value ? bit : 0);
+ }
+
+ /*
diff --git a/target/linux/generic/backport-6.1/802-v5.16-0001-nvmem-core-rework-nvmem-cell-instance-creation.patch b/target/linux/generic/backport-6.1/802-v5.16-0001-nvmem-core-rework-nvmem-cell-instance-creation.patch
new file mode 100644
index 0000000000..ebab8c7c73
--- /dev/null
+++ b/target/linux/generic/backport-6.1/802-v5.16-0001-nvmem-core-rework-nvmem-cell-instance-creation.patch
@@ -0,0 +1,456 @@
+From 7ae6478b304bc004c3139b422665b0e23b57f05c Mon Sep 17 00:00:00 2001
+From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Date: Wed, 13 Oct 2021 14:19:55 +0100
+Subject: [PATCH] nvmem: core: rework nvmem cell instance creation
+
+In the existing design, we do not create a instance per nvmem cell consumer
+but we directly refer cell from nvmem cell list that are added to provider.
+
+However this design has some limitations when consumers want to assign name
+or connection id the nvmem cell instance, ex: via "nvmem-cell-names" or
+id in nvmem_cell_get(id).
+
+Having a name associated with nvmem cell consumer instance will help
+provider drivers in performing post processing of nvmem cell data if required
+before data is seen by the consumers. This is pretty normal with some vendors
+storing nvmem cells like mac-address in a vendor specific data layouts that
+are not directly usable by the consumer drivers.
+
+With this patch nvmem cell will be created dynamically during nvmem_cell_get
+and destroyed in nvmem_cell_put, allowing consumers to associate name with
+nvmem cell consumer instance.
+
+With this patch a new struct nvmem_cell_entry replaces struct nvmem_cell
+for storing nvmem cell information within the core.
+This patch does not change nvmem-consumer interface based on nvmem_cell.
+
+Tested-by: Joakim Zhang <qiangqing.zhang@nxp.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20211013131957.30271-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 165 +++++++++++++++++++++++++++----------------
+ 1 file changed, 105 insertions(+), 60 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -45,8 +45,7 @@ struct nvmem_device {
+ #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
+
+ #define FLAG_COMPAT BIT(0)
+-
+-struct nvmem_cell {
++struct nvmem_cell_entry {
+ const char *name;
+ int offset;
+ int bytes;
+@@ -57,6 +56,11 @@ struct nvmem_cell {
+ struct list_head node;
+ };
+
++struct nvmem_cell {
++ struct nvmem_cell_entry *entry;
++ const char *id;
++};
++
+ static DEFINE_MUTEX(nvmem_mutex);
+ static DEFINE_IDA(nvmem_ida);
+
+@@ -424,7 +428,7 @@ static struct bus_type nvmem_bus_type =
+ .name = "nvmem",
+ };
+
+-static void nvmem_cell_drop(struct nvmem_cell *cell)
++static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell)
+ {
+ blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell);
+ mutex_lock(&nvmem_mutex);
+@@ -437,13 +441,13 @@ static void nvmem_cell_drop(struct nvmem
+
+ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem)
+ {
+- struct nvmem_cell *cell, *p;
++ struct nvmem_cell_entry *cell, *p;
+
+ list_for_each_entry_safe(cell, p, &nvmem->cells, node)
+- nvmem_cell_drop(cell);
++ nvmem_cell_entry_drop(cell);
+ }
+
+-static void nvmem_cell_add(struct nvmem_cell *cell)
++static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell)
+ {
+ mutex_lock(&nvmem_mutex);
+ list_add_tail(&cell->node, &cell->nvmem->cells);
+@@ -451,9 +455,9 @@ static void nvmem_cell_add(struct nvmem_
+ blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell);
+ }
+
+-static int nvmem_cell_info_to_nvmem_cell_nodup(struct nvmem_device *nvmem,
+- const struct nvmem_cell_info *info,
+- struct nvmem_cell *cell)
++static int nvmem_cell_info_to_nvmem_cell_entry_nodup(struct nvmem_device *nvmem,
++ const struct nvmem_cell_info *info,
++ struct nvmem_cell_entry *cell)
+ {
+ cell->nvmem = nvmem;
+ cell->offset = info->offset;
+@@ -477,13 +481,13 @@ static int nvmem_cell_info_to_nvmem_cell
+ return 0;
+ }
+
+-static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem,
+- const struct nvmem_cell_info *info,
+- struct nvmem_cell *cell)
++static int nvmem_cell_info_to_nvmem_cell_entry(struct nvmem_device *nvmem,
++ const struct nvmem_cell_info *info,
++ struct nvmem_cell_entry *cell)
+ {
+ int err;
+
+- err = nvmem_cell_info_to_nvmem_cell_nodup(nvmem, info, cell);
++ err = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, cell);
+ if (err)
+ return err;
+
+@@ -507,7 +511,7 @@ static int nvmem_add_cells(struct nvmem_
+ const struct nvmem_cell_info *info,
+ int ncells)
+ {
+- struct nvmem_cell **cells;
++ struct nvmem_cell_entry **cells;
+ int i, rval;
+
+ cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL);
+@@ -521,13 +525,13 @@ static int nvmem_add_cells(struct nvmem_
+ goto err;
+ }
+
+- rval = nvmem_cell_info_to_nvmem_cell(nvmem, &info[i], cells[i]);
++ rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, &info[i], cells[i]);
+ if (rval) {
+ kfree(cells[i]);
+ goto err;
+ }
+
+- nvmem_cell_add(cells[i]);
++ nvmem_cell_entry_add(cells[i]);
+ }
+
+ /* remove tmp array */
+@@ -536,7 +540,7 @@ static int nvmem_add_cells(struct nvmem_
+ return 0;
+ err:
+ while (i--)
+- nvmem_cell_drop(cells[i]);
++ nvmem_cell_entry_drop(cells[i]);
+
+ kfree(cells);
+
+@@ -573,7 +577,7 @@ static int nvmem_add_cells_from_table(st
+ {
+ const struct nvmem_cell_info *info;
+ struct nvmem_cell_table *table;
+- struct nvmem_cell *cell;
++ struct nvmem_cell_entry *cell;
+ int rval = 0, i;
+
+ mutex_lock(&nvmem_cell_mutex);
+@@ -588,15 +592,13 @@ static int nvmem_add_cells_from_table(st
+ goto out;
+ }
+
+- rval = nvmem_cell_info_to_nvmem_cell(nvmem,
+- info,
+- cell);
++ rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, info, cell);
+ if (rval) {
+ kfree(cell);
+ goto out;
+ }
+
+- nvmem_cell_add(cell);
++ nvmem_cell_entry_add(cell);
+ }
+ }
+ }
+@@ -606,10 +608,10 @@ out:
+ return rval;
+ }
+
+-static struct nvmem_cell *
+-nvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id)
++static struct nvmem_cell_entry *
++nvmem_find_cell_entry_by_name(struct nvmem_device *nvmem, const char *cell_id)
+ {
+- struct nvmem_cell *iter, *cell = NULL;
++ struct nvmem_cell_entry *iter, *cell = NULL;
+
+ mutex_lock(&nvmem_mutex);
+ list_for_each_entry(iter, &nvmem->cells, node) {
+@@ -680,7 +682,7 @@ static int nvmem_add_cells_from_of(struc
+ {
+ struct device_node *parent, *child;
+ struct device *dev = &nvmem->dev;
+- struct nvmem_cell *cell;
++ struct nvmem_cell_entry *cell;
+ const __be32 *addr;
+ int len;
+
+@@ -729,7 +731,7 @@ static int nvmem_add_cells_from_of(struc
+ }
+
+ cell->np = of_node_get(child);
+- nvmem_cell_add(cell);
++ nvmem_cell_entry_add(cell);
+ }
+
+ return 0;
+@@ -1142,9 +1144,33 @@ struct nvmem_device *devm_nvmem_device_g
+ }
+ EXPORT_SYMBOL_GPL(devm_nvmem_device_get);
+
++static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, const char *id)
++{
++ struct nvmem_cell *cell;
++ const char *name = NULL;
++
++ cell = kzalloc(sizeof(*cell), GFP_KERNEL);
++ if (!cell)
++ return ERR_PTR(-ENOMEM);
++
++ if (id) {
++ name = kstrdup_const(id, GFP_KERNEL);
++ if (!name) {
++ kfree(cell);
++ return ERR_PTR(-ENOMEM);
++ }
++ }
++
++ cell->id = name;
++ cell->entry = entry;
++
++ return cell;
++}
++
+ static struct nvmem_cell *
+ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
+ {
++ struct nvmem_cell_entry *cell_entry;
+ struct nvmem_cell *cell = ERR_PTR(-ENOENT);
+ struct nvmem_cell_lookup *lookup;
+ struct nvmem_device *nvmem;
+@@ -1169,11 +1195,15 @@ nvmem_cell_get_from_lookup(struct device
+ break;
+ }
+
+- cell = nvmem_find_cell_by_name(nvmem,
+- lookup->cell_name);
+- if (!cell) {
++ cell_entry = nvmem_find_cell_entry_by_name(nvmem,
++ lookup->cell_name);
++ if (!cell_entry) {
+ __nvmem_device_put(nvmem);
+ cell = ERR_PTR(-ENOENT);
++ } else {
++ cell = nvmem_create_cell(cell_entry, con_id);
++ if (IS_ERR(cell))
++ __nvmem_device_put(nvmem);
+ }
+ break;
+ }
+@@ -1184,10 +1214,10 @@ nvmem_cell_get_from_lookup(struct device
+ }
+
+ #if IS_ENABLED(CONFIG_OF)
+-static struct nvmem_cell *
+-nvmem_find_cell_by_node(struct nvmem_device *nvmem, struct device_node *np)
++static struct nvmem_cell_entry *
++nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np)
+ {
+- struct nvmem_cell *iter, *cell = NULL;
++ struct nvmem_cell_entry *iter, *cell = NULL;
+
+ mutex_lock(&nvmem_mutex);
+ list_for_each_entry(iter, &nvmem->cells, node) {
+@@ -1217,6 +1247,7 @@ struct nvmem_cell *of_nvmem_cell_get(str
+ {
+ struct device_node *cell_np, *nvmem_np;
+ struct nvmem_device *nvmem;
++ struct nvmem_cell_entry *cell_entry;
+ struct nvmem_cell *cell;
+ int index = 0;
+
+@@ -1237,12 +1268,16 @@ struct nvmem_cell *of_nvmem_cell_get(str
+ if (IS_ERR(nvmem))
+ return ERR_CAST(nvmem);
+
+- cell = nvmem_find_cell_by_node(nvmem, cell_np);
+- if (!cell) {
++ cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
++ if (!cell_entry) {
+ __nvmem_device_put(nvmem);
+ return ERR_PTR(-ENOENT);
+ }
+
++ cell = nvmem_create_cell(cell_entry, id);
++ if (IS_ERR(cell))
++ __nvmem_device_put(nvmem);
++
+ return cell;
+ }
+ EXPORT_SYMBOL_GPL(of_nvmem_cell_get);
+@@ -1348,13 +1383,17 @@ EXPORT_SYMBOL(devm_nvmem_cell_put);
+ */
+ void nvmem_cell_put(struct nvmem_cell *cell)
+ {
+- struct nvmem_device *nvmem = cell->nvmem;
++ struct nvmem_device *nvmem = cell->entry->nvmem;
++
++ if (cell->id)
++ kfree_const(cell->id);
+
++ kfree(cell);
+ __nvmem_device_put(nvmem);
+ }
+ EXPORT_SYMBOL_GPL(nvmem_cell_put);
+
+-static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
++static void nvmem_shift_read_buffer_in_place(struct nvmem_cell_entry *cell, void *buf)
+ {
+ u8 *p, *b;
+ int i, extra, bit_offset = cell->bit_offset;
+@@ -1388,8 +1427,8 @@ static void nvmem_shift_read_buffer_in_p
+ }
+
+ static int __nvmem_cell_read(struct nvmem_device *nvmem,
+- struct nvmem_cell *cell,
+- void *buf, size_t *len)
++ struct nvmem_cell_entry *cell,
++ void *buf, size_t *len, const char *id)
+ {
+ int rc;
+
+@@ -1420,18 +1459,18 @@ static int __nvmem_cell_read(struct nvme
+ */
+ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
+ {
+- struct nvmem_device *nvmem = cell->nvmem;
++ struct nvmem_device *nvmem = cell->entry->nvmem;
+ u8 *buf;
+ int rc;
+
+ if (!nvmem)
+ return ERR_PTR(-EINVAL);
+
+- buf = kzalloc(cell->bytes, GFP_KERNEL);
++ buf = kzalloc(cell->entry->bytes, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+- rc = __nvmem_cell_read(nvmem, cell, buf, len);
++ rc = __nvmem_cell_read(nvmem, cell->entry, buf, len, cell->id);
+ if (rc) {
+ kfree(buf);
+ return ERR_PTR(rc);
+@@ -1441,7 +1480,7 @@ void *nvmem_cell_read(struct nvmem_cell
+ }
+ EXPORT_SYMBOL_GPL(nvmem_cell_read);
+
+-static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
++static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell_entry *cell,
+ u8 *_buf, int len)
+ {
+ struct nvmem_device *nvmem = cell->nvmem;
+@@ -1494,16 +1533,7 @@ err:
+ return ERR_PTR(rc);
+ }
+
+-/**
+- * nvmem_cell_write() - Write to a given nvmem cell
+- *
+- * @cell: nvmem cell to be written.
+- * @buf: Buffer to be written.
+- * @len: length of buffer to be written to nvmem cell.
+- *
+- * Return: length of bytes written or negative on failure.
+- */
+-int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
++static int __nvmem_cell_entry_write(struct nvmem_cell_entry *cell, void *buf, size_t len)
+ {
+ struct nvmem_device *nvmem = cell->nvmem;
+ int rc;
+@@ -1529,6 +1559,21 @@ int nvmem_cell_write(struct nvmem_cell *
+
+ return len;
+ }
++
++/**
++ * nvmem_cell_write() - Write to a given nvmem cell
++ *
++ * @cell: nvmem cell to be written.
++ * @buf: Buffer to be written.
++ * @len: length of buffer to be written to nvmem cell.
++ *
++ * Return: length of bytes written or negative on failure.
++ */
++int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
++{
++ return __nvmem_cell_entry_write(cell->entry, buf, len);
++}
++
+ EXPORT_SYMBOL_GPL(nvmem_cell_write);
+
+ static int nvmem_cell_read_common(struct device *dev, const char *cell_id,
+@@ -1631,7 +1676,7 @@ static const void *nvmem_cell_read_varia
+ if (IS_ERR(cell))
+ return cell;
+
+- nbits = cell->nbits;
++ nbits = cell->entry->nbits;
+ buf = nvmem_cell_read(cell, len);
+ nvmem_cell_put(cell);
+ if (IS_ERR(buf))
+@@ -1727,18 +1772,18 @@ EXPORT_SYMBOL_GPL(nvmem_cell_read_variab
+ ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
+ struct nvmem_cell_info *info, void *buf)
+ {
+- struct nvmem_cell cell;
++ struct nvmem_cell_entry cell;
+ int rc;
+ ssize_t len;
+
+ if (!nvmem)
+ return -EINVAL;
+
+- rc = nvmem_cell_info_to_nvmem_cell_nodup(nvmem, info, &cell);
++ rc = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, &cell);
+ if (rc)
+ return rc;
+
+- rc = __nvmem_cell_read(nvmem, &cell, buf, &len);
++ rc = __nvmem_cell_read(nvmem, &cell, buf, &len, NULL);
+ if (rc)
+ return rc;
+
+@@ -1758,17 +1803,17 @@ EXPORT_SYMBOL_GPL(nvmem_device_cell_read
+ int nvmem_device_cell_write(struct nvmem_device *nvmem,
+ struct nvmem_cell_info *info, void *buf)
+ {
+- struct nvmem_cell cell;
++ struct nvmem_cell_entry cell;
+ int rc;
+
+ if (!nvmem)
+ return -EINVAL;
+
+- rc = nvmem_cell_info_to_nvmem_cell_nodup(nvmem, info, &cell);
++ rc = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, &cell);
+ if (rc)
+ return rc;
+
+- return nvmem_cell_write(&cell, buf, cell.bytes);
++ return __nvmem_cell_entry_write(&cell, buf, cell.bytes);
+ }
+ EXPORT_SYMBOL_GPL(nvmem_device_cell_write);
+
diff --git a/target/linux/generic/backport-6.1/802-v5.16-0002-nvmem-core-add-nvmem-cell-post-processing-callback.patch b/target/linux/generic/backport-6.1/802-v5.16-0002-nvmem-core-add-nvmem-cell-post-processing-callback.patch
new file mode 100644
index 0000000000..df264add24
--- /dev/null
+++ b/target/linux/generic/backport-6.1/802-v5.16-0002-nvmem-core-add-nvmem-cell-post-processing-callback.patch
@@ -0,0 +1,82 @@
+From 5008062f1c3f5af3acf86164aa6fcc77b0c7bdce Mon Sep 17 00:00:00 2001
+From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Date: Wed, 13 Oct 2021 14:19:56 +0100
+Subject: [PATCH] nvmem: core: add nvmem cell post processing callback
+
+Some NVMEM providers have certain nvmem cells encoded, which requires
+post processing before actually using it.
+
+For example mac-address is stored in either in ascii or delimited or reverse-order.
+
+Having a post-process callback hook to provider drivers would enable them to
+do this vendor specific post processing before nvmem consumers see it.
+
+Tested-by: Joakim Zhang <qiangqing.zhang@nxp.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20211013131957.30271-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 9 +++++++++
+ include/linux/nvmem-provider.h | 5 +++++
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -38,6 +38,7 @@ struct nvmem_device {
+ unsigned int nkeepout;
+ nvmem_reg_read_t reg_read;
+ nvmem_reg_write_t reg_write;
++ nvmem_cell_post_process_t cell_post_process;
+ struct gpio_desc *wp_gpio;
+ void *priv;
+ };
+@@ -799,6 +800,7 @@ struct nvmem_device *nvmem_register(cons
+ nvmem->type = config->type;
+ nvmem->reg_read = config->reg_read;
+ nvmem->reg_write = config->reg_write;
++ nvmem->cell_post_process = config->cell_post_process;
+ nvmem->keepout = config->keepout;
+ nvmem->nkeepout = config->nkeepout;
+ if (config->of_node)
+@@ -1441,6 +1443,13 @@ static int __nvmem_cell_read(struct nvme
+ if (cell->bit_offset || cell->nbits)
+ nvmem_shift_read_buffer_in_place(cell, buf);
+
++ if (nvmem->cell_post_process) {
++ rc = nvmem->cell_post_process(nvmem->priv, id,
++ cell->offset, buf, cell->bytes);
++ if (rc)
++ return rc;
++ }
++
+ if (len)
+ *len = cell->bytes;
+
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -19,6 +19,9 @@ typedef int (*nvmem_reg_read_t)(void *pr
+ void *val, size_t bytes);
+ typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset,
+ void *val, size_t bytes);
++/* used for vendor specific post processing of cell data */
++typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, unsigned int offset,
++ void *buf, size_t bytes);
+
+ enum nvmem_type {
+ NVMEM_TYPE_UNKNOWN = 0,
+@@ -62,6 +65,7 @@ struct nvmem_keepout {
+ * @no_of_node: Device should not use the parent's of_node even if it's !NULL.
+ * @reg_read: Callback to read data.
+ * @reg_write: Callback to write data.
++ * @cell_post_process: Callback for vendor specific post processing of cell data
+ * @size: Device size.
+ * @word_size: Minimum read/write access granularity.
+ * @stride: Minimum read/write access stride.
+@@ -92,6 +96,7 @@ struct nvmem_config {
+ bool no_of_node;
+ nvmem_reg_read_t reg_read;
+ nvmem_reg_write_t reg_write;
++ nvmem_cell_post_process_t cell_post_process;
+ int size;
+ int word_size;
+ int stride;
diff --git a/target/linux/generic/backport-6.1/802-v5.16-0003-nvmem-imx-ocotp-add-support-for-post-processing.patch b/target/linux/generic/backport-6.1/802-v5.16-0003-nvmem-imx-ocotp-add-support-for-post-processing.patch
new file mode 100644
index 0000000000..ee19228270
--- /dev/null
+++ b/target/linux/generic/backport-6.1/802-v5.16-0003-nvmem-imx-ocotp-add-support-for-post-processing.patch
@@ -0,0 +1,92 @@
+From d0221a780cbc99fec6c27a98dba2828dc5735c00 Mon Sep 17 00:00:00 2001
+From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Date: Wed, 13 Oct 2021 14:19:57 +0100
+Subject: [PATCH] nvmem: imx-ocotp: add support for post processing
+
+Add .cell_post_process callback for imx-ocotp to deal with MAC address,
+since MAC address need to be reversed byte for some i.MX SoCs.
+
+Tested-by: Joakim Zhang <qiangqing.zhang@nxp.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20211013131957.30271-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/imx-ocotp.c | 25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+--- a/drivers/nvmem/imx-ocotp.c
++++ b/drivers/nvmem/imx-ocotp.c
+@@ -97,6 +97,7 @@ struct ocotp_params {
+ unsigned int bank_address_words;
+ void (*set_timing)(struct ocotp_priv *priv);
+ struct ocotp_ctrl_reg ctrl;
++ bool reverse_mac_address;
+ };
+
+ static int imx_ocotp_wait_for_busy(struct ocotp_priv *priv, u32 flags)
+@@ -221,6 +222,25 @@ read_end:
+ return ret;
+ }
+
++static int imx_ocotp_cell_pp(void *context, const char *id, unsigned int offset,
++ void *data, size_t bytes)
++{
++ struct ocotp_priv *priv = context;
++
++ /* Deal with some post processing of nvmem cell data */
++ if (id && !strcmp(id, "mac-address")) {
++ if (priv->params->reverse_mac_address) {
++ u8 *buf = data;
++ int i;
++
++ for (i = 0; i < bytes/2; i++)
++ swap(buf[i], buf[bytes - i - 1]);
++ }
++ }
++
++ return 0;
++}
++
+ static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv)
+ {
+ unsigned long clk_rate;
+@@ -468,6 +488,7 @@ static struct nvmem_config imx_ocotp_nvm
+ .stride = 1,
+ .reg_read = imx_ocotp_read,
+ .reg_write = imx_ocotp_write,
++ .cell_post_process = imx_ocotp_cell_pp,
+ };
+
+ static const struct ocotp_params imx6q_params = {
+@@ -530,6 +551,7 @@ static const struct ocotp_params imx8mq_
+ .bank_address_words = 0,
+ .set_timing = imx_ocotp_set_imx6_timing,
+ .ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
++ .reverse_mac_address = true,
+ };
+
+ static const struct ocotp_params imx8mm_params = {
+@@ -537,6 +559,7 @@ static const struct ocotp_params imx8mm_
+ .bank_address_words = 0,
+ .set_timing = imx_ocotp_set_imx6_timing,
+ .ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
++ .reverse_mac_address = true,
+ };
+
+ static const struct ocotp_params imx8mn_params = {
+@@ -544,6 +567,7 @@ static const struct ocotp_params imx8mn_
+ .bank_address_words = 0,
+ .set_timing = imx_ocotp_set_imx6_timing,
+ .ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
++ .reverse_mac_address = true,
+ };
+
+ static const struct ocotp_params imx8mp_params = {
+@@ -551,6 +575,7 @@ static const struct ocotp_params imx8mp_
+ .bank_address_words = 0,
+ .set_timing = imx_ocotp_set_imx6_timing,
+ .ctrl = IMX_OCOTP_BM_CTRL_8MP,
++ .reverse_mac_address = true,
+ };
+
+ static const struct of_device_id imx_ocotp_dt_ids[] = {
diff --git a/target/linux/generic/backport-6.1/803-v5.17-0002-nvmem-mtk-efuse-support-minimum-one-byte-access-stri.patch b/target/linux/generic/backport-6.1/803-v5.17-0002-nvmem-mtk-efuse-support-minimum-one-byte-access-stri.patch
new file mode 100644
index 0000000000..785bfe53f5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/803-v5.17-0002-nvmem-mtk-efuse-support-minimum-one-byte-access-stri.patch
@@ -0,0 +1,47 @@
+From 98e2c4efae214fb7086cac9117616eb6ea11475d Mon Sep 17 00:00:00 2001
+From: Chunfeng Yun <chunfeng.yun@mediatek.com>
+Date: Thu, 9 Dec 2021 17:42:34 +0000
+Subject: [PATCH] nvmem: mtk-efuse: support minimum one byte access stride and
+ granularity
+
+In order to support nvmem bits property, should support minimum 1 byte
+read stride and minimum 1 byte read granularity at the same time.
+
+Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20211209174235.14049-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -19,11 +19,12 @@ static int mtk_reg_read(void *context,
+ unsigned int reg, void *_val, size_t bytes)
+ {
+ struct mtk_efuse_priv *priv = context;
+- u32 *val = _val;
+- int i = 0, words = bytes / 4;
++ void __iomem *addr = priv->base + reg;
++ u8 *val = _val;
++ int i;
+
+- while (words--)
+- *val++ = readl(priv->base + reg + (i++ * 4));
++ for (i = 0; i < bytes; i++, val++)
++ *val = readb(addr + i);
+
+ return 0;
+ }
+@@ -45,8 +46,8 @@ static int mtk_efuse_probe(struct platfo
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+- econfig.stride = 4;
+- econfig.word_size = 4;
++ econfig.stride = 1;
++ econfig.word_size = 1;
+ econfig.reg_read = mtk_reg_read;
+ econfig.size = resource_size(res);
+ econfig.priv = priv;
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0001-nvmem-core-Remove-unused-devm_nvmem_unregister.patch b/target/linux/generic/backport-6.1/804-v5.18-0001-nvmem-core-Remove-unused-devm_nvmem_unregister.patch
new file mode 100644
index 0000000000..ca5357c8d9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0001-nvmem-core-Remove-unused-devm_nvmem_unregister.patch
@@ -0,0 +1,72 @@
+From 190fae468592bc2f0efc8b928920f8f712b5831e Mon Sep 17 00:00:00 2001
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Date: Sun, 20 Feb 2022 15:15:15 +0000
+Subject: [PATCH] nvmem: core: Remove unused devm_nvmem_unregister()
+
+There are no users and seems no will come of the devm_nvmem_unregister().
+Remove the function and remove the unused devm_nvmem_match() along with it.
+
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220220151527.17216-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 22 ----------------------
+ include/linux/nvmem-provider.h | 8 --------
+ 2 files changed, 30 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -943,28 +943,6 @@ struct nvmem_device *devm_nvmem_register
+ }
+ EXPORT_SYMBOL_GPL(devm_nvmem_register);
+
+-static int devm_nvmem_match(struct device *dev, void *res, void *data)
+-{
+- struct nvmem_device **r = res;
+-
+- return *r == data;
+-}
+-
+-/**
+- * devm_nvmem_unregister() - Unregister previously registered managed nvmem
+- * device.
+- *
+- * @dev: Device that uses the nvmem device.
+- * @nvmem: Pointer to previously registered nvmem device.
+- *
+- * Return: Will be negative on error or zero on success.
+- */
+-int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
+-{
+- return devres_release(dev, devm_nvmem_release, devm_nvmem_match, nvmem);
+-}
+-EXPORT_SYMBOL(devm_nvmem_unregister);
+-
+ static struct nvmem_device *__nvmem_device_get(void *data,
+ int (*match)(struct device *dev, const void *data))
+ {
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -133,8 +133,6 @@ void nvmem_unregister(struct nvmem_devic
+ struct nvmem_device *devm_nvmem_register(struct device *dev,
+ const struct nvmem_config *cfg);
+
+-int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem);
+-
+ void nvmem_add_cell_table(struct nvmem_cell_table *table);
+ void nvmem_del_cell_table(struct nvmem_cell_table *table);
+
+@@ -153,12 +151,6 @@ devm_nvmem_register(struct device *dev,
+ return nvmem_register(c);
+ }
+
+-static inline int
+-devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
+-{
+- return -EOPNOTSUPP;
+-}
+-
+ static inline void nvmem_add_cell_table(struct nvmem_cell_table *table) {}
+ static inline void nvmem_del_cell_table(struct nvmem_cell_table *table) {}
+
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0002-nvmem-core-Use-devm_add_action_or_reset.patch b/target/linux/generic/backport-6.1/804-v5.18-0002-nvmem-core-Use-devm_add_action_or_reset.patch
new file mode 100644
index 0000000000..b71a0a365b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0002-nvmem-core-Use-devm_add_action_or_reset.patch
@@ -0,0 +1,58 @@
+From 5825b2c6762611e67ccaf3ccf64485365a120f0b Mon Sep 17 00:00:00 2001
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Date: Sun, 20 Feb 2022 15:15:16 +0000
+Subject: [PATCH] nvmem: core: Use devm_add_action_or_reset()
+
+Slightly simplify the devm_nvmem_register() by using the
+devm_add_action_or_reset().
+
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220220151527.17216-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 22 +++++++++-------------
+ 1 file changed, 9 insertions(+), 13 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -905,9 +905,9 @@ void nvmem_unregister(struct nvmem_devic
+ }
+ EXPORT_SYMBOL_GPL(nvmem_unregister);
+
+-static void devm_nvmem_release(struct device *dev, void *res)
++static void devm_nvmem_unregister(void *nvmem)
+ {
+- nvmem_unregister(*(struct nvmem_device **)res);
++ nvmem_unregister(nvmem);
+ }
+
+ /**
+@@ -924,20 +924,16 @@ static void devm_nvmem_release(struct de
+ struct nvmem_device *devm_nvmem_register(struct device *dev,
+ const struct nvmem_config *config)
+ {
+- struct nvmem_device **ptr, *nvmem;
+-
+- ptr = devres_alloc(devm_nvmem_release, sizeof(*ptr), GFP_KERNEL);
+- if (!ptr)
+- return ERR_PTR(-ENOMEM);
++ struct nvmem_device *nvmem;
++ int ret;
+
+ nvmem = nvmem_register(config);
++ if (IS_ERR(nvmem))
++ return nvmem;
+
+- if (!IS_ERR(nvmem)) {
+- *ptr = nvmem;
+- devres_add(dev, ptr);
+- } else {
+- devres_free(ptr);
+- }
++ ret = devm_add_action_or_reset(dev, devm_nvmem_unregister, nvmem);
++ if (ret)
++ return ERR_PTR(ret);
+
+ return nvmem;
+ }
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0003-nvmem-core-Check-input-parameter-for-NULL-in-nvmem_u.patch b/target/linux/generic/backport-6.1/804-v5.18-0003-nvmem-core-Check-input-parameter-for-NULL-in-nvmem_u.patch
new file mode 100644
index 0000000000..4f471f2667
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0003-nvmem-core-Check-input-parameter-for-NULL-in-nvmem_u.patch
@@ -0,0 +1,30 @@
+From 8c751e0d9a5264376935a84429a2d468c8877d99 Mon Sep 17 00:00:00 2001
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Date: Sun, 20 Feb 2022 15:15:17 +0000
+Subject: [PATCH] nvmem: core: Check input parameter for NULL in
+ nvmem_unregister()
+
+nvmem_unregister() frees resources and standard pattern is to allow
+caller to not care if it's NULL or not. This will reduce burden on
+the callers to perform this check.
+
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220220151527.17216-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -901,7 +901,8 @@ static void nvmem_device_release(struct
+ */
+ void nvmem_unregister(struct nvmem_device *nvmem)
+ {
+- kref_put(&nvmem->refcnt, nvmem_device_release);
++ if (nvmem)
++ kref_put(&nvmem->refcnt, nvmem_device_release);
+ }
+ EXPORT_SYMBOL_GPL(nvmem_unregister);
+
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0004-nvmem-qfprom-fix-kerneldoc-warning.patch b/target/linux/generic/backport-6.1/804-v5.18-0004-nvmem-qfprom-fix-kerneldoc-warning.patch
new file mode 100644
index 0000000000..c98f8e9d54
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0004-nvmem-qfprom-fix-kerneldoc-warning.patch
@@ -0,0 +1,29 @@
+From 05196facc052385960028ac634447ecf6c764ec3 Mon Sep 17 00:00:00 2001
+From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Date: Sun, 20 Feb 2022 15:15:18 +0000
+Subject: [PATCH] nvmem: qfprom: fix kerneldoc warning
+
+This patch fixes below kernel doc warning,
+warning: expecting prototype for qfprom_efuse_reg_write().
+Prototype was for qfprom_reg_write() instead
+
+No code changes.
+
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220220151527.17216-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/qfprom.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/qfprom.c
++++ b/drivers/nvmem/qfprom.c
+@@ -244,7 +244,7 @@ err_clk_prepared:
+ }
+
+ /**
+- * qfprom_efuse_reg_write() - Write to fuses.
++ * qfprom_reg_write() - Write to fuses.
+ * @context: Our driver data.
+ * @reg: The offset to write at.
+ * @_val: Pointer to data to write.
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0005-nvmem-sunxi_sid-Add-support-for-D1-variant.patch b/target/linux/generic/backport-6.1/804-v5.18-0005-nvmem-sunxi_sid-Add-support-for-D1-variant.patch
new file mode 100644
index 0000000000..6aad6af080
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0005-nvmem-sunxi_sid-Add-support-for-D1-variant.patch
@@ -0,0 +1,38 @@
+From 07ae4fde9efada7878e1383d6ccc7da70315ca23 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 20 Feb 2022 15:15:20 +0000
+Subject: [PATCH] nvmem: sunxi_sid: Add support for D1 variant
+
+D1 has a smaller eFuse block than some other recent SoCs, and it no
+longer requires a workaround to read the eFuse data.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220220151527.17216-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sunxi_sid.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/nvmem/sunxi_sid.c
++++ b/drivers/nvmem/sunxi_sid.c
+@@ -184,6 +184,11 @@ static const struct sunxi_sid_cfg sun8i_
+ .need_register_readout = true,
+ };
+
++static const struct sunxi_sid_cfg sun20i_d1_cfg = {
++ .value_offset = 0x200,
++ .size = 0x100,
++};
++
+ static const struct sunxi_sid_cfg sun50i_a64_cfg = {
+ .value_offset = 0x200,
+ .size = 0x100,
+@@ -200,6 +205,7 @@ static const struct of_device_id sunxi_s
+ { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg },
+ { .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg },
+ { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg },
++ { .compatible = "allwinner,sun20i-d1-sid", .data = &sun20i_d1_cfg },
+ { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg },
+ { .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg },
+ { .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg },
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0006-nvmem-meson-mx-efuse-replace-unnecessary-devm_kstrdu.patch b/target/linux/generic/backport-6.1/804-v5.18-0006-nvmem-meson-mx-efuse-replace-unnecessary-devm_kstrdu.patch
new file mode 100644
index 0000000000..a73b42c5de
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0006-nvmem-meson-mx-efuse-replace-unnecessary-devm_kstrdu.patch
@@ -0,0 +1,28 @@
+From 4dc8d89faed9bb05f116fa1794fc955b14910386 Mon Sep 17 00:00:00 2001
+From: Xiaoke Wang <xkernel.wang@foxmail.com>
+Date: Sun, 20 Feb 2022 15:15:21 +0000
+Subject: [PATCH] nvmem: meson-mx-efuse: replace unnecessary devm_kstrdup()
+
+Replace unnecessary devm_kstrdup() so to avoid redundant memory allocation.
+
+Suggested-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+Signed-off-by: Xiaoke Wang <xkernel.wang@foxmail.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220220151527.17216-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/meson-mx-efuse.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/nvmem/meson-mx-efuse.c
++++ b/drivers/nvmem/meson-mx-efuse.c
+@@ -209,8 +209,7 @@ static int meson_mx_efuse_probe(struct p
+ if (IS_ERR(efuse->base))
+ return PTR_ERR(efuse->base);
+
+- efuse->config.name = devm_kstrdup(&pdev->dev, drvdata->name,
+- GFP_KERNEL);
++ efuse->config.name = drvdata->name;
+ efuse->config.owner = THIS_MODULE;
+ efuse->config.dev = &pdev->dev;
+ efuse->config.priv = efuse;
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0007-nvmem-add-driver-for-Layerscape-SFP-Security-Fuse-Pr.patch b/target/linux/generic/backport-6.1/804-v5.18-0007-nvmem-add-driver-for-Layerscape-SFP-Security-Fuse-Pr.patch
new file mode 100644
index 0000000000..6afb68b3f9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0007-nvmem-add-driver-for-Layerscape-SFP-Security-Fuse-Pr.patch
@@ -0,0 +1,139 @@
+From f78451012b9e159afdba31c3eb69f223a9f42adc Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Sun, 20 Feb 2022 15:15:23 +0000
+Subject: [PATCH] nvmem: add driver for Layerscape SFP (Security Fuse
+ Processor)
+
+Add support for the Security Fuse Processor found on Layerscape SoCs.
+This driver implements basic read access.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220220151527.17216-10-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 12 +++++
+ drivers/nvmem/Makefile | 2 +
+ drivers/nvmem/layerscape-sfp.c | 89 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 103 insertions(+)
+ create mode 100644 drivers/nvmem/layerscape-sfp.c
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -300,4 +300,16 @@ config NVMEM_BRCM_NVRAM
+ This driver provides support for Broadcom's NVRAM that can be accessed
+ using I/O mapping.
+
++config NVMEM_LAYERSCAPE_SFP
++ tristate "Layerscape SFP (Security Fuse Processor) support"
++ depends on ARCH_LAYERSCAPE || COMPILE_TEST
++ depends on HAS_IOMEM
++ help
++ This driver provides support to read the eFuses on Freescale
++ Layerscape SoC's. For example, the vendor provides a per part
++ unique ID there.
++
++ This driver can also be built as a module. If so, the module
++ will be called layerscape-sfp.
++
+ endif
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -61,3 +61,5 @@ obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.
+ nvmem-rmem-y := rmem.o
+ obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o
+ nvmem_brcm_nvram-y := brcm_nvram.o
++obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o
++nvmem-layerscape-sfp-y := layerscape-sfp.o
+--- /dev/null
++++ b/drivers/nvmem/layerscape-sfp.c
+@@ -0,0 +1,89 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Layerscape SFP driver
++ *
++ * Copyright (c) 2022 Michael Walle <michael@walle.cc>
++ *
++ */
++
++#include <linux/device.h>
++#include <linux/io.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/nvmem-provider.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++
++#define LAYERSCAPE_SFP_OTP_OFFSET 0x0200
++
++struct layerscape_sfp_priv {
++ void __iomem *base;
++};
++
++struct layerscape_sfp_data {
++ int size;
++};
++
++static int layerscape_sfp_read(void *context, unsigned int offset, void *val,
++ size_t bytes)
++{
++ struct layerscape_sfp_priv *priv = context;
++
++ memcpy_fromio(val, priv->base + LAYERSCAPE_SFP_OTP_OFFSET + offset,
++ bytes);
++
++ return 0;
++}
++
++static struct nvmem_config layerscape_sfp_nvmem_config = {
++ .name = "fsl-sfp",
++ .reg_read = layerscape_sfp_read,
++};
++
++static int layerscape_sfp_probe(struct platform_device *pdev)
++{
++ const struct layerscape_sfp_data *data;
++ struct layerscape_sfp_priv *priv;
++ struct nvmem_device *nvmem;
++
++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(priv->base))
++ return PTR_ERR(priv->base);
++
++ data = device_get_match_data(&pdev->dev);
++
++ layerscape_sfp_nvmem_config.size = data->size;
++ layerscape_sfp_nvmem_config.dev = &pdev->dev;
++ layerscape_sfp_nvmem_config.priv = priv;
++
++ nvmem = devm_nvmem_register(&pdev->dev, &layerscape_sfp_nvmem_config);
++
++ return PTR_ERR_OR_ZERO(nvmem);
++}
++
++static const struct layerscape_sfp_data ls1028a_data = {
++ .size = 0x88,
++};
++
++static const struct of_device_id layerscape_sfp_dt_ids[] = {
++ { .compatible = "fsl,ls1028a-sfp", .data = &ls1028a_data },
++ {},
++};
++MODULE_DEVICE_TABLE(of, layerscape_sfp_dt_ids);
++
++static struct platform_driver layerscape_sfp_driver = {
++ .probe = layerscape_sfp_probe,
++ .driver = {
++ .name = "layerscape_sfp",
++ .of_match_table = layerscape_sfp_dt_ids,
++ },
++};
++module_platform_driver(layerscape_sfp_driver);
++
++MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
++MODULE_DESCRIPTION("Layerscape Security Fuse Processor driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0008-nvmem-qfprom-Increase-fuse-blow-timeout-to-prevent-w.patch b/target/linux/generic/backport-6.1/804-v5.18-0008-nvmem-qfprom-Increase-fuse-blow-timeout-to-prevent-w.patch
new file mode 100644
index 0000000000..74bd4a7eb6
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0008-nvmem-qfprom-Increase-fuse-blow-timeout-to-prevent-w.patch
@@ -0,0 +1,32 @@
+From bc5c75e0a5a9400f81a987cc720100ac475fa4d8 Mon Sep 17 00:00:00 2001
+From: Knox Chiou <knoxchiou@chromium.org>
+Date: Wed, 23 Feb 2022 22:35:00 +0000
+Subject: [PATCH] nvmem: qfprom: Increase fuse blow timeout to prevent write
+ fail
+
+sc7180 blow fuses got slightly chances to hit qfprom_reg_write timeout.
+Current timeout is simply too low. Since blowing fuses is a
+very rare operation, so the risk associated with overestimating this
+number is low.
+Increase fuse blow timeout from 1ms to 10ms.
+
+Reviewed-by: Douglas Anderson <dianders@chromium.org>
+Signed-off-by: Knox Chiou <knoxchiou@chromium.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220223223502.29454-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/qfprom.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/qfprom.c
++++ b/drivers/nvmem/qfprom.c
+@@ -22,7 +22,7 @@
+
+ /* Amount of time required to hold charge to blow fuse in micro-seconds */
+ #define QFPROM_FUSE_BLOW_POLL_US 100
+-#define QFPROM_FUSE_BLOW_TIMEOUT_US 1000
++#define QFPROM_FUSE_BLOW_TIMEOUT_US 10000
+
+ #define QFPROM_BLOW_STATUS_OFFSET 0x048
+ #define QFPROM_BLOW_STATUS_BUSY 0x1
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch b/target/linux/generic/backport-6.1/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch
new file mode 100644
index 0000000000..2a9dc74947
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch
@@ -0,0 +1,291 @@
+From 8747ec2e9762ed9ae53b3a590938f454b6a1abdf Mon Sep 17 00:00:00 2001
+From: Vincent Shih <vincent.sunplus@gmail.com>
+Date: Wed, 23 Feb 2022 22:35:01 +0000
+Subject: [PATCH] nvmem: Add driver for OCOTP in Sunplus SP7021
+
+Add driver for OCOTP in Sunplus SP7021
+
+Signed-off-by: Vincent Shih <vincent.sunplus@gmail.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220223223502.29454-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ MAINTAINERS | 5 +
+ drivers/nvmem/Kconfig | 12 ++
+ drivers/nvmem/Makefile | 2 +
+ drivers/nvmem/sunplus-ocotp.c | 228 ++++++++++++++++++++++++++++++++++
+ 4 files changed, 247 insertions(+)
+ create mode 100644 drivers/nvmem/sunplus-ocotp.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -17962,6 +17962,11 @@ L: netdev@vger.kernel.org
+ S: Maintained
+ F: drivers/net/ethernet/dlink/sundance.c
+
++SUNPLUS OCOTP DRIVER
++M: Vincent Shih <vincent.sunplus@gmail.com>
++S: Maintained
++F: drivers/nvmem/sunplus-ocotp.c
++
+ SUPERH
+ M: Yoshinori Sato <ysato@users.sourceforge.jp>
+ M: Rich Felker <dalias@libc.org>
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -312,4 +312,16 @@ config NVMEM_LAYERSCAPE_SFP
+ This driver can also be built as a module. If so, the module
+ will be called layerscape-sfp.
+
++config NVMEM_SUNPLUS_OCOTP
++ tristate "Sunplus SoC OTP support"
++ depends on SOC_SP7021 || COMPILE_TEST
++ depends on HAS_IOMEM
++ help
++ This is a driver for the On-chip OTP controller (OCOTP) available
++ on Sunplus SoCs. It provides access to 128 bytes of one-time
++ programmable eFuse.
++
++ This driver can also be built as a module. If so, the module
++ will be called nvmem-sunplus-ocotp.
++
+ endif
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -63,3 +63,5 @@ obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_
+ nvmem_brcm_nvram-y := brcm_nvram.o
+ obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o
+ nvmem-layerscape-sfp-y := layerscape-sfp.o
++obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o
++nvmem_sunplus_ocotp-y := sunplus-ocotp.o
+--- /dev/null
++++ b/drivers/nvmem/sunplus-ocotp.c
+@@ -0,0 +1,228 @@
++// SPDX-License-Identifier: GPL-2.0
++
++/*
++ * The OCOTP driver for Sunplus SP7021
++ *
++ * Copyright (C) 2019 Sunplus Technology Inc., All rights reserved.
++ */
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/module.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++
++/*
++ * OTP memory
++ * Each bank contains 4 words (32 bits).
++ * Bank 0 starts at offset 0 from the base.
++ */
++
++#define OTP_WORDS_PER_BANK 4
++#define OTP_WORD_SIZE sizeof(u32)
++#define OTP_BIT_ADDR_OF_BANK (8 * OTP_WORD_SIZE * OTP_WORDS_PER_BANK)
++#define QAC628_OTP_NUM_BANKS 8
++#define QAC628_OTP_SIZE (QAC628_OTP_NUM_BANKS * OTP_WORDS_PER_BANK * OTP_WORD_SIZE)
++#define OTP_READ_TIMEOUT_US 200000
++
++/* HB_GPIO */
++#define ADDRESS_8_DATA 0x20
++
++/* OTP_RX */
++#define OTP_CONTROL_2 0x48
++#define OTP_RD_PERIOD GENMASK(15, 8)
++#define OTP_RD_PERIOD_MASK ~GENMASK(15, 8)
++#define CPU_CLOCK FIELD_PREP(OTP_RD_PERIOD, 30)
++#define SEL_BAK_KEY2 BIT(5)
++#define SEL_BAK_KEY2_MASK ~BIT(5)
++#define SW_TRIM_EN BIT(4)
++#define SW_TRIM_EN_MASK ~BIT(4)
++#define SEL_BAK_KEY BIT(3)
++#define SEL_BAK_KEY_MASK ~BIT(3)
++#define OTP_READ BIT(2)
++#define OTP_LOAD_SECURE_DATA BIT(1)
++#define OTP_LOAD_SECURE_DATA_MASK ~BIT(1)
++#define OTP_DO_CRC BIT(0)
++#define OTP_DO_CRC_MASK ~BIT(0)
++#define OTP_STATUS 0x4c
++#define OTP_READ_DONE BIT(4)
++#define OTP_READ_DONE_MASK ~BIT(4)
++#define OTP_LOAD_SECURE_DONE_MASK ~BIT(2)
++#define OTP_READ_ADDRESS 0x50
++
++enum base_type {
++ HB_GPIO,
++ OTPRX,
++ BASEMAX,
++};
++
++struct sp_ocotp_priv {
++ struct device *dev;
++ void __iomem *base[BASEMAX];
++ struct clk *clk;
++};
++
++struct sp_ocotp_data {
++ int size;
++};
++
++const struct sp_ocotp_data sp_otp_v0 = {
++ .size = QAC628_OTP_SIZE,
++};
++
++static int sp_otp_read_real(struct sp_ocotp_priv *otp, int addr, char *value)
++{
++ unsigned int addr_data;
++ unsigned int byte_shift;
++ unsigned int status;
++ int ret;
++
++ addr_data = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK);
++ addr_data = addr_data / OTP_WORD_SIZE;
++
++ byte_shift = addr % (OTP_WORD_SIZE * OTP_WORDS_PER_BANK);
++ byte_shift = byte_shift % OTP_WORD_SIZE;
++
++ addr = addr / (OTP_WORD_SIZE * OTP_WORDS_PER_BANK);
++ addr = addr * OTP_BIT_ADDR_OF_BANK;
++
++ writel(readl(otp->base[OTPRX] + OTP_STATUS) & OTP_READ_DONE_MASK &
++ OTP_LOAD_SECURE_DONE_MASK, otp->base[OTPRX] + OTP_STATUS);
++ writel(addr, otp->base[OTPRX] + OTP_READ_ADDRESS);
++ writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) | OTP_READ,
++ otp->base[OTPRX] + OTP_CONTROL_2);
++ writel(readl(otp->base[OTPRX] + OTP_CONTROL_2) & SEL_BAK_KEY2_MASK & SW_TRIM_EN_MASK
++ & SEL_BAK_KEY_MASK & OTP_LOAD_SECURE_DATA_MASK & OTP_DO_CRC_MASK,
++ otp->base[OTPRX] + OTP_CONTROL_2);
++ writel((readl(otp->base[OTPRX] + OTP_CONTROL_2) & OTP_RD_PERIOD_MASK) | CPU_CLOCK,
++ otp->base[OTPRX] + OTP_CONTROL_2);
++
++ ret = readl_poll_timeout(otp->base[OTPRX] + OTP_STATUS, status,
++ status & OTP_READ_DONE, 10, OTP_READ_TIMEOUT_US);
++
++ if (ret < 0)
++ return ret;
++
++ *value = (readl(otp->base[HB_GPIO] + ADDRESS_8_DATA + addr_data * OTP_WORD_SIZE)
++ >> (8 * byte_shift)) & 0xff;
++
++ return ret;
++}
++
++static int sp_ocotp_read(void *priv, unsigned int offset, void *value, size_t bytes)
++{
++ struct sp_ocotp_priv *otp = priv;
++ unsigned int addr;
++ char *buf = value;
++ char val[4];
++ int ret;
++
++ ret = clk_enable(otp->clk);
++ if (ret)
++ return ret;
++
++ *buf = 0;
++ for (addr = offset; addr < (offset + bytes); addr++) {
++ ret = sp_otp_read_real(otp, addr, val);
++ if (ret < 0) {
++ dev_err(otp->dev, "OTP read fail:%d at %d", ret, addr);
++ goto disable_clk;
++ }
++
++ *buf++ = *val;
++ }
++
++disable_clk:
++ clk_disable(otp->clk);
++
++ return ret;
++}
++
++static struct nvmem_config sp_ocotp_nvmem_config = {
++ .name = "sp-ocotp",
++ .read_only = true,
++ .word_size = 1,
++ .size = QAC628_OTP_SIZE,
++ .stride = 1,
++ .reg_read = sp_ocotp_read,
++ .owner = THIS_MODULE,
++};
++
++static int sp_ocotp_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct nvmem_device *nvmem;
++ struct sp_ocotp_priv *otp;
++ struct resource *res;
++ int ret;
++
++ otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
++ if (!otp)
++ return -ENOMEM;
++
++ otp->dev = dev;
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio");
++ otp->base[HB_GPIO] = devm_ioremap_resource(dev, res);
++ if (IS_ERR(otp->base[HB_GPIO]))
++ return PTR_ERR(otp->base[HB_GPIO]);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx");
++ otp->base[OTPRX] = devm_ioremap_resource(dev, res);
++ if (IS_ERR(otp->base[OTPRX]))
++ return PTR_ERR(otp->base[OTPRX]);
++
++ otp->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(otp->clk))
++ return dev_err_probe(&pdev->dev, PTR_ERR(otp->clk),
++ "devm_clk_get fail\n");
++
++ ret = clk_prepare(otp->clk);
++ if (ret < 0) {
++ dev_err(dev, "failed to prepare clk: %d\n", ret);
++ return ret;
++ }
++
++ sp_ocotp_nvmem_config.priv = otp;
++ sp_ocotp_nvmem_config.dev = dev;
++
++ nvmem = devm_nvmem_register(dev, &sp_ocotp_nvmem_config);
++ if (IS_ERR(nvmem))
++ return dev_err_probe(&pdev->dev, PTR_ERR(nvmem),
++ "register nvmem device fail\n");
++
++ platform_set_drvdata(pdev, nvmem);
++
++ dev_dbg(dev, "banks:%d x wpb:%d x wsize:%d = %d",
++ (int)QAC628_OTP_NUM_BANKS, (int)OTP_WORDS_PER_BANK,
++ (int)OTP_WORD_SIZE, (int)QAC628_OTP_SIZE);
++
++ dev_info(dev, "by Sunplus (C) 2020");
++
++ return 0;
++}
++
++static const struct of_device_id sp_ocotp_dt_ids[] = {
++ { .compatible = "sunplus,sp7021-ocotp", .data = &sp_otp_v0 },
++ { }
++};
++MODULE_DEVICE_TABLE(of, sp_ocotp_dt_ids);
++
++static struct platform_driver sp_otp_driver = {
++ .probe = sp_ocotp_probe,
++ .driver = {
++ .name = "sunplus,sp7021-ocotp",
++ .of_match_table = sp_ocotp_dt_ids,
++ }
++};
++module_platform_driver(sp_otp_driver);
++
++MODULE_AUTHOR("Vincent Shih <vincent.sunplus@gmail.com>");
++MODULE_DESCRIPTION("Sunplus On-Chip OTP driver");
++MODULE_LICENSE("GPL");
++
diff --git a/target/linux/generic/backport-6.1/804-v5.18-0010-nvmem-brcm_nvram-parse-NVRAM-content-into-NVMEM-cell.patch b/target/linux/generic/backport-6.1/804-v5.18-0010-nvmem-brcm_nvram-parse-NVRAM-content-into-NVMEM-cell.patch
new file mode 100644
index 0000000000..99781b3a7b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/804-v5.18-0010-nvmem-brcm_nvram-parse-NVRAM-content-into-NVMEM-cell.patch
@@ -0,0 +1,146 @@
+From 6e977eaa8280e957b87904b536661550f2a6b3e8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 25 Feb 2022 17:58:20 +0000
+Subject: [PATCH] nvmem: brcm_nvram: parse NVRAM content into NVMEM cells
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+NVRAM consist of header and NUL separated key-value pairs. Parse it and
+create NVMEM cell for every key-value entry.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220225175822.8293-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/brcm_nvram.c | 90 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 90 insertions(+)
+
+--- a/drivers/nvmem/brcm_nvram.c
++++ b/drivers/nvmem/brcm_nvram.c
+@@ -6,12 +6,26 @@
+ #include <linux/io.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
++#include <linux/nvmem-consumer.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/platform_device.h>
++#include <linux/slab.h>
++
++#define NVRAM_MAGIC "FLSH"
+
+ struct brcm_nvram {
+ struct device *dev;
+ void __iomem *base;
++ struct nvmem_cell_info *cells;
++ int ncells;
++};
++
++struct brcm_nvram_header {
++ char magic[4];
++ __le32 len;
++ __le32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */
++ __le32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */
++ __le32 config_ncdl; /* ncdl values for memc */
+ };
+
+ static int brcm_nvram_read(void *context, unsigned int offset, void *val,
+@@ -26,6 +40,75 @@ static int brcm_nvram_read(void *context
+ return 0;
+ }
+
++static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data,
++ size_t len)
++{
++ struct device *dev = priv->dev;
++ char *var, *value, *eq;
++ int idx;
++
++ priv->ncells = 0;
++ for (var = data + sizeof(struct brcm_nvram_header);
++ var < (char *)data + len && *var;
++ var += strlen(var) + 1) {
++ priv->ncells++;
++ }
++
++ priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL);
++ if (!priv->cells)
++ return -ENOMEM;
++
++ for (var = data + sizeof(struct brcm_nvram_header), idx = 0;
++ var < (char *)data + len && *var;
++ var = value + strlen(value) + 1, idx++) {
++ eq = strchr(var, '=');
++ if (!eq)
++ break;
++ *eq = '\0';
++ value = eq + 1;
++
++ priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL);
++ if (!priv->cells[idx].name)
++ return -ENOMEM;
++ priv->cells[idx].offset = value - (char *)data;
++ priv->cells[idx].bytes = strlen(value);
++ }
++
++ return 0;
++}
++
++static int brcm_nvram_parse(struct brcm_nvram *priv)
++{
++ struct device *dev = priv->dev;
++ struct brcm_nvram_header header;
++ uint8_t *data;
++ size_t len;
++ int err;
++
++ memcpy_fromio(&header, priv->base, sizeof(header));
++
++ if (memcmp(header.magic, NVRAM_MAGIC, 4)) {
++ dev_err(dev, "Invalid NVRAM magic\n");
++ return -EINVAL;
++ }
++
++ len = le32_to_cpu(header.len);
++
++ data = kcalloc(1, len, GFP_KERNEL);
++ memcpy_fromio(data, priv->base, len);
++ data[len - 1] = '\0';
++
++ err = brcm_nvram_add_cells(priv, data, len);
++ if (err) {
++ dev_err(dev, "Failed to add cells: %d\n", err);
++ return err;
++ }
++
++ kfree(data);
++
++ return 0;
++}
++
+ static int brcm_nvram_probe(struct platform_device *pdev)
+ {
+ struct nvmem_config config = {
+@@ -35,6 +118,7 @@ static int brcm_nvram_probe(struct platf
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct brcm_nvram *priv;
++ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -46,7 +130,13 @@ static int brcm_nvram_probe(struct platf
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
++ err = brcm_nvram_parse(priv);
++ if (err)
++ return err;
++
+ config.dev = dev;
++ config.cells = priv->cells;
++ config.ncells = priv->ncells;
+ config.priv = priv;
+ config.size = resource_size(res);
+
diff --git a/target/linux/generic/backport-6.1/805-v5.19-0001-nvmem-bcm-ocotp-mark-ACPI-device-ID-table-as-maybe-u.patch b/target/linux/generic/backport-6.1/805-v5.19-0001-nvmem-bcm-ocotp-mark-ACPI-device-ID-table-as-maybe-u.patch
new file mode 100644
index 0000000000..ef3107db94
--- /dev/null
+++ b/target/linux/generic/backport-6.1/805-v5.19-0001-nvmem-bcm-ocotp-mark-ACPI-device-ID-table-as-maybe-u.patch
@@ -0,0 +1,32 @@
+From 6bd0ffeaa389866089e9573b2298ae58d6359b75 Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzk@kernel.org>
+Date: Mon, 21 Mar 2022 12:03:24 +0100
+Subject: [PATCH] nvmem: bcm-ocotp: mark ACPI device ID table as maybe unused
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+"bcm_otpc_acpi_ids" is used with ACPI_PTR, so a build with !CONFIG_ACPI
+has a warning:
+
+ drivers/nvmem/bcm-ocotp.c:247:36: error:
+ ‘bcm_otpc_acpi_ids’ defined but not used [-Werror=unused-const-variable=]
+
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Link: https://lore.kernel.org/r/20220321110326.44652-1-krzk@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/bcm-ocotp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/bcm-ocotp.c
++++ b/drivers/nvmem/bcm-ocotp.c
+@@ -244,7 +244,7 @@ static const struct of_device_id bcm_otp
+ };
+ MODULE_DEVICE_TABLE(of, bcm_otpc_dt_ids);
+
+-static const struct acpi_device_id bcm_otpc_acpi_ids[] = {
++static const struct acpi_device_id bcm_otpc_acpi_ids[] __maybe_unused = {
+ { .id = "BRCM0700", .driver_data = (kernel_ulong_t)&otp_map },
+ { .id = "BRCM0701", .driver_data = (kernel_ulong_t)&otp_map_v2 },
+ { /* sentinel */ }
diff --git a/target/linux/generic/backport-6.1/805-v5.19-0002-nvmem-sunplus-ocotp-staticize-sp_otp_v0.patch b/target/linux/generic/backport-6.1/805-v5.19-0002-nvmem-sunplus-ocotp-staticize-sp_otp_v0.patch
new file mode 100644
index 0000000000..a84d2316f0
--- /dev/null
+++ b/target/linux/generic/backport-6.1/805-v5.19-0002-nvmem-sunplus-ocotp-staticize-sp_otp_v0.patch
@@ -0,0 +1,30 @@
+From 1066f8156351fcd997125257cea47cf805ba4f6d Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzk@kernel.org>
+Date: Mon, 21 Mar 2022 12:03:25 +0100
+Subject: [PATCH] nvmem: sunplus-ocotp: staticize sp_otp_v0
+
+The "sp_otp_v0" file scope variable is not used outside, so make it
+static to fix warning:
+
+ drivers/nvmem/sunplus-ocotp.c:74:29: sparse:
+ sparse: symbol 'sp_otp_v0' was not declared. Should it be static?
+
+Reported-by: kernel test robot <lkp@intel.com>
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Link: https://lore.kernel.org/r/20220321110326.44652-2-krzk@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sunplus-ocotp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/sunplus-ocotp.c
++++ b/drivers/nvmem/sunplus-ocotp.c
+@@ -71,7 +71,7 @@ struct sp_ocotp_data {
+ int size;
+ };
+
+-const struct sp_ocotp_data sp_otp_v0 = {
++static const struct sp_ocotp_data sp_otp_v0 = {
+ .size = QAC628_OTP_SIZE,
+ };
+
diff --git a/target/linux/generic/backport-6.1/805-v5.19-0003-nvmem-sunplus-ocotp-drop-useless-probe-confirmation.patch b/target/linux/generic/backport-6.1/805-v5.19-0003-nvmem-sunplus-ocotp-drop-useless-probe-confirmation.patch
new file mode 100644
index 0000000000..886ebc12a9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/805-v5.19-0003-nvmem-sunplus-ocotp-drop-useless-probe-confirmation.patch
@@ -0,0 +1,27 @@
+From 874dfbcf219ccc42a2cbd187d087c7db82c3024b Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzk@kernel.org>
+Date: Mon, 21 Mar 2022 12:03:26 +0100
+Subject: [PATCH] nvmem: sunplus-ocotp: drop useless probe confirmation
+
+Printing probe success is discouraged, because we can use tracing for
+this purpose. Remove useless print message after Sunplus OCOTP driver
+probe.
+
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Link: https://lore.kernel.org/r/20220321110326.44652-3-krzk@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sunplus-ocotp.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/nvmem/sunplus-ocotp.c
++++ b/drivers/nvmem/sunplus-ocotp.c
+@@ -202,8 +202,6 @@ static int sp_ocotp_probe(struct platfor
+ (int)QAC628_OTP_NUM_BANKS, (int)OTP_WORDS_PER_BANK,
+ (int)OTP_WORD_SIZE, (int)QAC628_OTP_SIZE);
+
+- dev_info(dev, "by Sunplus (C) 2020");
+-
+ return 0;
+ }
+
diff --git a/target/linux/generic/backport-6.1/805-v5.19-0004-nvmem-core-support-passing-DT-node-in-cell-info.patch b/target/linux/generic/backport-6.1/805-v5.19-0004-nvmem-core-support-passing-DT-node-in-cell-info.patch
new file mode 100644
index 0000000000..3b1e76147a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/805-v5.19-0004-nvmem-core-support-passing-DT-node-in-cell-info.patch
@@ -0,0 +1,41 @@
+From dbc2f62061c6bfba0aee93161ee3194dcee84bd0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 29 Apr 2022 17:26:46 +0100
+Subject: [PATCH] nvmem: core: support passing DT node in cell info
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some hardware may have NVMEM cells described in Device Tree using
+individual nodes. Let drivers pass such nodes to the NVMEM subsystem so
+they can be later used by NVMEM consumers.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220429162701.2222-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 1 +
+ include/linux/nvmem-consumer.h | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -467,6 +467,7 @@ static int nvmem_cell_info_to_nvmem_cell
+
+ cell->bit_offset = info->bit_offset;
+ cell->nbits = info->nbits;
++ cell->np = info->np;
+
+ if (cell->nbits)
+ cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset,
+--- a/include/linux/nvmem-consumer.h
++++ b/include/linux/nvmem-consumer.h
+@@ -25,6 +25,7 @@ struct nvmem_cell_info {
+ unsigned int bytes;
+ unsigned int bit_offset;
+ unsigned int nbits;
++ struct device_node *np;
+ };
+
+ /**
diff --git a/target/linux/generic/backport-6.1/805-v5.19-0005-nvmem-brcm_nvram-find-Device-Tree-nodes-for-NVMEM-ce.patch b/target/linux/generic/backport-6.1/805-v5.19-0005-nvmem-brcm_nvram-find-Device-Tree-nodes-for-NVMEM-ce.patch
new file mode 100644
index 0000000000..a9eacd9419
--- /dev/null
+++ b/target/linux/generic/backport-6.1/805-v5.19-0005-nvmem-brcm_nvram-find-Device-Tree-nodes-for-NVMEM-ce.patch
@@ -0,0 +1,38 @@
+From 207775f7e17b8fd0426a2ac4a5b81e4e1d71849e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 29 Apr 2022 17:26:47 +0100
+Subject: [PATCH] nvmem: brcm_nvram: find Device Tree nodes for NVMEM cells
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+DT binding for Broadcom's NVRAM supports specifying NVMEM cells as NVMEM
+device (provider) subnodes. Look for such subnodes when collecing NVMEM
+cells. This allows NVMEM consumers to use NVRAM variables.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220429162701.2222-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/brcm_nvram.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/nvmem/brcm_nvram.c
++++ b/drivers/nvmem/brcm_nvram.c
+@@ -8,6 +8,7 @@
+ #include <linux/module.h>
+ #include <linux/nvmem-consumer.h>
+ #include <linux/nvmem-provider.h>
++#include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+
+@@ -72,6 +73,7 @@ static int brcm_nvram_add_cells(struct b
+ return -ENOMEM;
+ priv->cells[idx].offset = value - (char *)data;
+ priv->cells[idx].bytes = strlen(value);
++ priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
+ }
+
+ return 0;
diff --git a/target/linux/generic/backport-6.1/805-v5.19-0006-nvmem-Add-Apple-eFuse-driver.patch b/target/linux/generic/backport-6.1/805-v5.19-0006-nvmem-Add-Apple-eFuse-driver.patch
new file mode 100644
index 0000000000..ebeb6f5ad3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/805-v5.19-0006-nvmem-Add-Apple-eFuse-driver.patch
@@ -0,0 +1,130 @@
+From b6b7ef932ae838209254f016ecf8862d716a5ced Mon Sep 17 00:00:00 2001
+From: Sven Peter <sven@svenpeter.dev>
+Date: Fri, 29 Apr 2022 17:26:50 +0100
+Subject: [PATCH] nvmem: Add Apple eFuse driver
+
+Apple SoCs contain eFuses used to store factory-programmed data such
+as calibration values for the PCIe or the Type-C PHY. They are organized
+as 32bit values exposed as MMIO.
+
+Signed-off-by: Sven Peter <sven@svenpeter.dev>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220429162701.2222-6-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 12 ++++++
+ drivers/nvmem/Makefile | 2 +
+ drivers/nvmem/apple-efuses.c | 80 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 94 insertions(+)
+ create mode 100644 drivers/nvmem/apple-efuses.c
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -324,4 +324,16 @@ config NVMEM_SUNPLUS_OCOTP
+ This driver can also be built as a module. If so, the module
+ will be called nvmem-sunplus-ocotp.
+
++config NVMEM_APPLE_EFUSES
++ tristate "Apple eFuse support"
++ depends on ARCH_APPLE || COMPILE_TEST
++ default ARCH_APPLE
++ help
++ Say y here to enable support for reading eFuses on Apple SoCs
++ such as the M1. These are e.g. used to store factory programmed
++ calibration data required for the PCIe or the USB-C PHY.
++
++ This driver can also be built as a module. If so, the module will
++ be called nvmem-apple-efuses.
++
+ endif
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -65,3 +65,5 @@ obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nv
+ nvmem-layerscape-sfp-y := layerscape-sfp.o
+ obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o
+ nvmem_sunplus_ocotp-y := sunplus-ocotp.o
++obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
++nvmem-apple-efuses-y := apple-efuses.o
+--- /dev/null
++++ b/drivers/nvmem/apple-efuses.c
+@@ -0,0 +1,80 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Apple SoC eFuse driver
++ *
++ * Copyright (C) The Asahi Linux Contributors
++ */
++
++#include <linux/io.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/nvmem-provider.h>
++#include <linux/platform_device.h>
++
++struct apple_efuses_priv {
++ void __iomem *fuses;
++};
++
++static int apple_efuses_read(void *context, unsigned int offset, void *val,
++ size_t bytes)
++{
++ struct apple_efuses_priv *priv = context;
++ u32 *dst = val;
++
++ while (bytes >= sizeof(u32)) {
++ *dst++ = readl_relaxed(priv->fuses + offset);
++ bytes -= sizeof(u32);
++ offset += sizeof(u32);
++ }
++
++ return 0;
++}
++
++static int apple_efuses_probe(struct platform_device *pdev)
++{
++ struct apple_efuses_priv *priv;
++ struct resource *res;
++ struct nvmem_config config = {
++ .dev = &pdev->dev,
++ .read_only = true,
++ .reg_read = apple_efuses_read,
++ .stride = sizeof(u32),
++ .word_size = sizeof(u32),
++ .name = "apple_efuses_nvmem",
++ .id = NVMEM_DEVID_AUTO,
++ .root_only = true,
++ };
++
++ priv = devm_kzalloc(config.dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->fuses = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
++ if (IS_ERR(priv->fuses))
++ return PTR_ERR(priv->fuses);
++
++ config.priv = priv;
++ config.size = resource_size(res);
++
++ return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config));
++}
++
++static const struct of_device_id apple_efuses_of_match[] = {
++ { .compatible = "apple,efuses", },
++ {}
++};
++
++MODULE_DEVICE_TABLE(of, apple_efuses_of_match);
++
++static struct platform_driver apple_efuses_driver = {
++ .driver = {
++ .name = "apple_efuses",
++ .of_match_table = apple_efuses_of_match,
++ },
++ .probe = apple_efuses_probe,
++};
++
++module_platform_driver(apple_efuses_driver);
++
++MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.1/805-v5.19-0007-nvmem-qfprom-using-pm_runtime_resume_and_get-instead.patch b/target/linux/generic/backport-6.1/805-v5.19-0007-nvmem-qfprom-using-pm_runtime_resume_and_get-instead.patch
new file mode 100644
index 0000000000..cd51d97006
--- /dev/null
+++ b/target/linux/generic/backport-6.1/805-v5.19-0007-nvmem-qfprom-using-pm_runtime_resume_and_get-instead.patch
@@ -0,0 +1,31 @@
+From 517f6e2641a2802dce5a5aa0d18c7d37a35678d2 Mon Sep 17 00:00:00 2001
+From: Minghao Chi <chi.minghao@zte.com.cn>
+Date: Fri, 29 Apr 2022 17:26:54 +0100
+Subject: [PATCH] nvmem: qfprom: using pm_runtime_resume_and_get instead of
+ pm_runtime_get_sync
+
+Using pm_runtime_resume_and_get is more appropriate
+for simplifing code
+
+Reported-by: Zeal Robot <zealci@zte.com.cn>
+Signed-off-by: Minghao Chi <chi.minghao@zte.com.cn>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220429162701.2222-10-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/qfprom.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/nvmem/qfprom.c
++++ b/drivers/nvmem/qfprom.c
+@@ -217,9 +217,8 @@ static int qfprom_enable_fuse_blowing(co
+ goto err_clk_rate_set;
+ }
+
+- ret = pm_runtime_get_sync(priv->dev);
++ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret < 0) {
+- pm_runtime_put_noidle(priv->dev);
+ dev_err(priv->dev, "Failed to enable power-domain\n");
+ goto err_reg_enable;
+ }
diff --git a/target/linux/generic/backport-6.1/805-v5.19-0008-nvmem-sfp-Use-regmap.patch b/target/linux/generic/backport-6.1/805-v5.19-0008-nvmem-sfp-Use-regmap.patch
new file mode 100644
index 0000000000..e187238ca3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/805-v5.19-0008-nvmem-sfp-Use-regmap.patch
@@ -0,0 +1,109 @@
+From 943eadbdb11314b41eacbcc484dfb7f93e271ff4 Mon Sep 17 00:00:00 2001
+From: Sean Anderson <sean.anderson@seco.com>
+Date: Fri, 29 Apr 2022 17:27:00 +0100
+Subject: [PATCH] nvmem: sfp: Use regmap
+
+This converts the SFP driver to use regmap. This will allow easily
+supporting devices with different endians. We disallow byte-level
+access, as regmap_bulk_read doesn't support it (and it's unclear what
+the correct result would be when we have an endianness difference).
+
+Signed-off-by: Sean Anderson <sean.anderson@seco.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220429162701.2222-16-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 1 +
+ drivers/nvmem/layerscape-sfp.c | 30 ++++++++++++++++++++++--------
+ 2 files changed, 23 insertions(+), 8 deletions(-)
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -304,6 +304,7 @@ config NVMEM_LAYERSCAPE_SFP
+ tristate "Layerscape SFP (Security Fuse Processor) support"
+ depends on ARCH_LAYERSCAPE || COMPILE_TEST
+ depends on HAS_IOMEM
++ select REGMAP_MMIO
+ help
+ This driver provides support to read the eFuses on Freescale
+ Layerscape SoC's. For example, the vendor provides a per part
+--- a/drivers/nvmem/layerscape-sfp.c
++++ b/drivers/nvmem/layerscape-sfp.c
+@@ -13,15 +13,17 @@
+ #include <linux/nvmem-provider.h>
+ #include <linux/platform_device.h>
+ #include <linux/property.h>
++#include <linux/regmap.h>
+
+ #define LAYERSCAPE_SFP_OTP_OFFSET 0x0200
+
+ struct layerscape_sfp_priv {
+- void __iomem *base;
++ struct regmap *regmap;
+ };
+
+ struct layerscape_sfp_data {
+ int size;
++ enum regmap_endian endian;
+ };
+
+ static int layerscape_sfp_read(void *context, unsigned int offset, void *val,
+@@ -29,15 +31,16 @@ static int layerscape_sfp_read(void *con
+ {
+ struct layerscape_sfp_priv *priv = context;
+
+- memcpy_fromio(val, priv->base + LAYERSCAPE_SFP_OTP_OFFSET + offset,
+- bytes);
+-
+- return 0;
++ return regmap_bulk_read(priv->regmap,
++ LAYERSCAPE_SFP_OTP_OFFSET + offset, val,
++ bytes / 4);
+ }
+
+ static struct nvmem_config layerscape_sfp_nvmem_config = {
+ .name = "fsl-sfp",
+ .reg_read = layerscape_sfp_read,
++ .word_size = 4,
++ .stride = 4,
+ };
+
+ static int layerscape_sfp_probe(struct platform_device *pdev)
+@@ -45,16 +48,26 @@ static int layerscape_sfp_probe(struct p
+ const struct layerscape_sfp_data *data;
+ struct layerscape_sfp_priv *priv;
+ struct nvmem_device *nvmem;
++ struct regmap_config config = { 0 };
++ void __iomem *base;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+- priv->base = devm_platform_ioremap_resource(pdev, 0);
+- if (IS_ERR(priv->base))
+- return PTR_ERR(priv->base);
++ base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
+
+ data = device_get_match_data(&pdev->dev);
++ config.reg_bits = 32;
++ config.reg_stride = 4;
++ config.val_bits = 32;
++ config.val_format_endian = data->endian;
++ config.max_register = LAYERSCAPE_SFP_OTP_OFFSET + data->size - 4;
++ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, &config);
++ if (IS_ERR(priv->regmap))
++ return PTR_ERR(priv->regmap);
+
+ layerscape_sfp_nvmem_config.size = data->size;
+ layerscape_sfp_nvmem_config.dev = &pdev->dev;
+@@ -67,6 +80,7 @@ static int layerscape_sfp_probe(struct p
+
+ static const struct layerscape_sfp_data ls1028a_data = {
+ .size = 0x88,
++ .endian = REGMAP_ENDIAN_LITTLE,
+ };
+
+ static const struct of_device_id layerscape_sfp_dt_ids[] = {
diff --git a/target/linux/generic/backport-6.1/805-v5.19-0009-nvmem-sfp-Add-support-for-TA-2.1-devices.patch b/target/linux/generic/backport-6.1/805-v5.19-0009-nvmem-sfp-Add-support-for-TA-2.1-devices.patch
new file mode 100644
index 0000000000..ee00098618
--- /dev/null
+++ b/target/linux/generic/backport-6.1/805-v5.19-0009-nvmem-sfp-Add-support-for-TA-2.1-devices.patch
@@ -0,0 +1,38 @@
+From 33a1c6618677fe33f8e84cb7bedc45abbce89a50 Mon Sep 17 00:00:00 2001
+From: Sean Anderson <sean.anderson@seco.com>
+Date: Fri, 29 Apr 2022 17:27:01 +0100
+Subject: [PATCH] nvmem: sfp: Add support for TA 2.1 devices
+
+This adds support for Trust Architecture (TA) 2.1 devices to the SFP driver.
+There are few differences between TA 2.1 and TA 3.0, especially for
+read-only support, so just re-use the existing data.
+
+Signed-off-by: Sean Anderson <sean.anderson@seco.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220429162701.2222-17-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layerscape-sfp.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/nvmem/layerscape-sfp.c
++++ b/drivers/nvmem/layerscape-sfp.c
+@@ -78,12 +78,18 @@ static int layerscape_sfp_probe(struct p
+ return PTR_ERR_OR_ZERO(nvmem);
+ }
+
++static const struct layerscape_sfp_data ls1021a_data = {
++ .size = 0x88,
++ .endian = REGMAP_ENDIAN_BIG,
++};
++
+ static const struct layerscape_sfp_data ls1028a_data = {
+ .size = 0x88,
+ .endian = REGMAP_ENDIAN_LITTLE,
+ };
+
+ static const struct of_device_id layerscape_sfp_dt_ids[] = {
++ { .compatible = "fsl,ls1021a-sfp", .data = &ls1021a_data },
+ { .compatible = "fsl,ls1028a-sfp", .data = &ls1028a_data },
+ {},
+ };
diff --git a/target/linux/generic/backport-6.1/806-v6.0-0001-nvmem-microchip-otpc-add-support.patch b/target/linux/generic/backport-6.1/806-v6.0-0001-nvmem-microchip-otpc-add-support.patch
new file mode 100644
index 0000000000..b1855d1f2f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/806-v6.0-0001-nvmem-microchip-otpc-add-support.patch
@@ -0,0 +1,389 @@
+From 98830350d3fc824c1ff5c338140fe20f041a5916 Mon Sep 17 00:00:00 2001
+From: Claudiu Beznea <claudiu.beznea@microchip.com>
+Date: Wed, 6 Jul 2022 11:06:22 +0100
+Subject: [PATCH] nvmem: microchip-otpc: add support
+
+Add support for Microchip OTP controller available on SAMA7G5. The OTPC
+controls the access to a non-volatile memory. The memory behind OTPC is
+organized into packets, packets are composed by a fixed length header
+(4 bytes long) and a variable length payload (payload length is available
+in the header). When software request the data at an offset in memory
+the OTPC will return (via header + data registers) the whole packet that
+has a word at that offset. For the OTP memory layout like below:
+
+offset OTP Memory layout
+
+ . .
+ . ... .
+ . .
+0x0E +-----------+ <--- packet X
+ | header X |
+0x12 +-----------+
+ | payload X |
+0x16 | |
+ | |
+0x1A | |
+ +-----------+
+ . .
+ . ... .
+ . .
+
+if user requests data at address 0x16 the data started at 0x0E will be
+returned by controller. User will be able to fetch the whole packet
+starting at 0x0E (or parts of the packet) via proper registers. The same
+packet will be returned if software request the data at offset 0x0E or
+0x12 or 0x1A.
+
+The OTP will be populated by Microchip with at least 2 packets first one
+being boot configuration packet and the 2nd one being temperature
+calibration packet. The packet order will be preserved b/w different chip
+revisions but the packet sizes may change.
+
+For the above reasons and to keep the same software able to work on all
+chip variants the read function of the driver is working with a packet
+id instead of an offset in OTP memory.
+
+Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220706100627.6534-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ MAINTAINERS | 8 +
+ drivers/nvmem/Kconfig | 7 +
+ drivers/nvmem/Makefile | 2 +
+ drivers/nvmem/microchip-otpc.c | 288 +++++++++++++++++++++++++++++++++
+ 4 files changed, 305 insertions(+)
+ create mode 100644 drivers/nvmem/microchip-otpc.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -12361,6 +12361,14 @@ S: Supported
+ F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
+ F: drivers/mtd/nand/raw/atmel/*
+
++MICROCHIP OTPC DRIVER
++M: Claudiu Beznea <claudiu.beznea@microchip.com>
++L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
++S: Supported
++F: Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml
++F: drivers/nvmem/microchip-otpc.c
++F: dt-bindings/nvmem/microchip,sama7g5-otpc.h
++
+ MICROCHIP PWM DRIVER
+ M: Claudiu Beznea <claudiu.beznea@microchip.com>
+ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -107,6 +107,13 @@ config MTK_EFUSE
+ This driver can also be built as a module. If so, the module
+ will be called efuse-mtk.
+
++config MICROCHIP_OTPC
++ tristate "Microchip OTPC support"
++ depends on ARCH_AT91 || COMPILE_TEST
++ help
++ This driver enable the OTP controller available on Microchip SAMA7G5
++ SoCs. It controlls the access to the OTP memory connected to it.
++
+ config NVMEM_NINTENDO_OTP
+ tristate "Nintendo Wii and Wii U OTP Support"
+ depends on WII || COMPILE_TEST
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -67,3 +67,5 @@ obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvm
+ nvmem_sunplus_ocotp-y := sunplus-ocotp.o
+ obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
+ nvmem-apple-efuses-y := apple-efuses.o
++obj-$(CONFIG_MICROCHIP_OTPC) += nvmem-microchip-otpc.o
++nvmem-microchip-otpc-y := microchip-otpc.o
+--- /dev/null
++++ b/drivers/nvmem/microchip-otpc.c
+@@ -0,0 +1,288 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * OTP Memory controller
++ *
++ * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
++ *
++ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/iopoll.h>
++#include <linux/module.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++
++#define MCHP_OTPC_CR (0x0)
++#define MCHP_OTPC_CR_READ BIT(6)
++#define MCHP_OTPC_MR (0x4)
++#define MCHP_OTPC_MR_ADDR GENMASK(31, 16)
++#define MCHP_OTPC_AR (0x8)
++#define MCHP_OTPC_SR (0xc)
++#define MCHP_OTPC_SR_READ BIT(6)
++#define MCHP_OTPC_HR (0x20)
++#define MCHP_OTPC_HR_SIZE GENMASK(15, 8)
++#define MCHP_OTPC_DR (0x24)
++
++#define MCHP_OTPC_NAME "mchp-otpc"
++#define MCHP_OTPC_SIZE (11 * 1024)
++
++/**
++ * struct mchp_otpc - OTPC private data structure
++ * @base: base address
++ * @dev: struct device pointer
++ * @packets: list of packets in OTP memory
++ * @npackets: number of packets in OTP memory
++ */
++struct mchp_otpc {
++ void __iomem *base;
++ struct device *dev;
++ struct list_head packets;
++ u32 npackets;
++};
++
++/**
++ * struct mchp_otpc_packet - OTPC packet data structure
++ * @list: list head
++ * @id: packet ID
++ * @offset: packet offset (in words) in OTP memory
++ */
++struct mchp_otpc_packet {
++ struct list_head list;
++ u32 id;
++ u32 offset;
++};
++
++static struct mchp_otpc_packet *mchp_otpc_id_to_packet(struct mchp_otpc *otpc,
++ u32 id)
++{
++ struct mchp_otpc_packet *packet;
++
++ if (id >= otpc->npackets)
++ return NULL;
++
++ list_for_each_entry(packet, &otpc->packets, list) {
++ if (packet->id == id)
++ return packet;
++ }
++
++ return NULL;
++}
++
++static int mchp_otpc_prepare_read(struct mchp_otpc *otpc,
++ unsigned int offset)
++{
++ u32 tmp;
++
++ /* Set address. */
++ tmp = readl_relaxed(otpc->base + MCHP_OTPC_MR);
++ tmp &= ~MCHP_OTPC_MR_ADDR;
++ tmp |= FIELD_PREP(MCHP_OTPC_MR_ADDR, offset);
++ writel_relaxed(tmp, otpc->base + MCHP_OTPC_MR);
++
++ /* Set read. */
++ tmp = readl_relaxed(otpc->base + MCHP_OTPC_CR);
++ tmp |= MCHP_OTPC_CR_READ;
++ writel_relaxed(tmp, otpc->base + MCHP_OTPC_CR);
++
++ /* Wait for packet to be transferred into temporary buffers. */
++ return read_poll_timeout(readl_relaxed, tmp, !(tmp & MCHP_OTPC_SR_READ),
++ 10000, 2000, false, otpc->base + MCHP_OTPC_SR);
++}
++
++/*
++ * OTPC memory is organized into packets. Each packets contains a header and
++ * a payload. Header is 4 bytes long and contains the size of the payload.
++ * Payload size varies. The memory footprint is something as follows:
++ *
++ * Memory offset Memory footprint Packet ID
++ * ------------- ---------------- ---------
++ *
++ * 0x0 +------------+ <-- packet 0
++ * | header 0 |
++ * 0x4 +------------+
++ * | payload 0 |
++ * . .
++ * . ... .
++ * . .
++ * offset1 +------------+ <-- packet 1
++ * | header 1 |
++ * offset1 + 0x4 +------------+
++ * | payload 1 |
++ * . .
++ * . ... .
++ * . .
++ * offset2 +------------+ <-- packet 2
++ * . .
++ * . ... .
++ * . .
++ * offsetN +------------+ <-- packet N
++ * | header N |
++ * offsetN + 0x4 +------------+
++ * | payload N |
++ * . .
++ * . ... .
++ * . .
++ * +------------+
++ *
++ * where offset1, offset2, offsetN depends on the size of payload 0, payload 1,
++ * payload N-1.
++ *
++ * The access to memory is done on a per packet basis: the control registers
++ * need to be updated with an offset address (within a packet range) and the
++ * data registers will be update by controller with information contained by
++ * that packet. E.g. if control registers are updated with any address within
++ * the range [offset1, offset2) the data registers are updated by controller
++ * with packet 1. Header data is accessible though MCHP_OTPC_HR register.
++ * Payload data is accessible though MCHP_OTPC_DR and MCHP_OTPC_AR registers.
++ * There is no direct mapping b/w the offset requested by software and the
++ * offset returned by hardware.
++ *
++ * For this, the read function will return the first requested bytes in the
++ * packet. The user will have to be aware of the memory footprint before doing
++ * the read request.
++ */
++static int mchp_otpc_read(void *priv, unsigned int off, void *val,
++ size_t bytes)
++{
++ struct mchp_otpc *otpc = priv;
++ struct mchp_otpc_packet *packet;
++ u32 *buf = val;
++ u32 offset;
++ size_t len = 0;
++ int ret, payload_size;
++
++ /*
++ * We reach this point with off being multiple of stride = 4 to
++ * be able to cross the subsystem. Inside the driver we use continuous
++ * unsigned integer numbers for packet id, thus devide off by 4
++ * before passing it to mchp_otpc_id_to_packet().
++ */
++ packet = mchp_otpc_id_to_packet(otpc, off / 4);
++ if (!packet)
++ return -EINVAL;
++ offset = packet->offset;
++
++ while (len < bytes) {
++ ret = mchp_otpc_prepare_read(otpc, offset);
++ if (ret)
++ return ret;
++
++ /* Read and save header content. */
++ *buf++ = readl_relaxed(otpc->base + MCHP_OTPC_HR);
++ len += sizeof(*buf);
++ offset++;
++ if (len >= bytes)
++ break;
++
++ /* Read and save payload content. */
++ payload_size = FIELD_GET(MCHP_OTPC_HR_SIZE, *(buf - 1));
++ writel_relaxed(0UL, otpc->base + MCHP_OTPC_AR);
++ do {
++ *buf++ = readl_relaxed(otpc->base + MCHP_OTPC_DR);
++ len += sizeof(*buf);
++ offset++;
++ payload_size--;
++ } while (payload_size >= 0 && len < bytes);
++ }
++
++ return 0;
++}
++
++static int mchp_otpc_init_packets_list(struct mchp_otpc *otpc, u32 *size)
++{
++ struct mchp_otpc_packet *packet;
++ u32 word, word_pos = 0, id = 0, npackets = 0, payload_size;
++ int ret;
++
++ INIT_LIST_HEAD(&otpc->packets);
++ *size = 0;
++
++ while (*size < MCHP_OTPC_SIZE) {
++ ret = mchp_otpc_prepare_read(otpc, word_pos);
++ if (ret)
++ return ret;
++
++ word = readl_relaxed(otpc->base + MCHP_OTPC_HR);
++ payload_size = FIELD_GET(MCHP_OTPC_HR_SIZE, word);
++ if (!payload_size)
++ break;
++
++ packet = devm_kzalloc(otpc->dev, sizeof(*packet), GFP_KERNEL);
++ if (!packet)
++ return -ENOMEM;
++
++ packet->id = id++;
++ packet->offset = word_pos;
++ INIT_LIST_HEAD(&packet->list);
++ list_add_tail(&packet->list, &otpc->packets);
++
++ /* Count size by adding header and paload sizes. */
++ *size += 4 * (payload_size + 1);
++ /* Next word: this packet (header, payload) position + 1. */
++ word_pos += payload_size + 2;
++
++ npackets++;
++ }
++
++ otpc->npackets = npackets;
++
++ return 0;
++}
++
++static struct nvmem_config mchp_nvmem_config = {
++ .name = MCHP_OTPC_NAME,
++ .type = NVMEM_TYPE_OTP,
++ .read_only = true,
++ .word_size = 4,
++ .stride = 4,
++ .reg_read = mchp_otpc_read,
++};
++
++static int mchp_otpc_probe(struct platform_device *pdev)
++{
++ struct nvmem_device *nvmem;
++ struct mchp_otpc *otpc;
++ u32 size;
++ int ret;
++
++ otpc = devm_kzalloc(&pdev->dev, sizeof(*otpc), GFP_KERNEL);
++ if (!otpc)
++ return -ENOMEM;
++
++ otpc->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(otpc->base))
++ return PTR_ERR(otpc->base);
++
++ otpc->dev = &pdev->dev;
++ ret = mchp_otpc_init_packets_list(otpc, &size);
++ if (ret)
++ return ret;
++
++ mchp_nvmem_config.dev = otpc->dev;
++ mchp_nvmem_config.size = size;
++ mchp_nvmem_config.priv = otpc;
++ nvmem = devm_nvmem_register(&pdev->dev, &mchp_nvmem_config);
++
++ return PTR_ERR_OR_ZERO(nvmem);
++}
++
++static const struct of_device_id __maybe_unused mchp_otpc_ids[] = {
++ { .compatible = "microchip,sama7g5-otpc", },
++ { },
++};
++MODULE_DEVICE_TABLE(of, mchp_otpc_ids);
++
++static struct platform_driver mchp_otpc_driver = {
++ .probe = mchp_otpc_probe,
++ .driver = {
++ .name = MCHP_OTPC_NAME,
++ .of_match_table = of_match_ptr(mchp_otpc_ids),
++ },
++};
++module_platform_driver(mchp_otpc_driver);
++
++MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea@microchip.com>");
++MODULE_DESCRIPTION("Microchip SAMA7G5 OTPC driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.1/806-v6.0-0002-nvmem-mtk-efuse-Simplify-with-devm_platform_get_and_.patch b/target/linux/generic/backport-6.1/806-v6.0-0002-nvmem-mtk-efuse-Simplify-with-devm_platform_get_and_.patch
new file mode 100644
index 0000000000..6a4126b9de
--- /dev/null
+++ b/target/linux/generic/backport-6.1/806-v6.0-0002-nvmem-mtk-efuse-Simplify-with-devm_platform_get_and_.patch
@@ -0,0 +1,32 @@
+From f5c97da8037b18d1256a58459fa96ed68e50fb41 Mon Sep 17 00:00:00 2001
+From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Date: Wed, 6 Jul 2022 11:06:27 +0100
+Subject: [PATCH] nvmem: mtk-efuse: Simplify with
+ devm_platform_get_and_ioremap_resource()
+
+Convert platform_get_resource(), devm_ioremap_resource() to a single
+call to devm_platform_get_and_ioremap_resource(), as this is exactly
+what this function does.
+
+No functional changes.
+
+Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220706100627.6534-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -41,8 +41,7 @@ static int mtk_efuse_probe(struct platfo
+ if (!priv)
+ return -ENOMEM;
+
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- priv->base = devm_ioremap_resource(dev, res);
++ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0002-nvmem-add-driver-handling-U-Boot-environment-variabl.patch b/target/linux/generic/backport-6.1/807-v6.1-0002-nvmem-add-driver-handling-U-Boot-environment-variabl.patch
new file mode 100644
index 0000000000..9138807bc9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0002-nvmem-add-driver-handling-U-Boot-environment-variabl.patch
@@ -0,0 +1,286 @@
+From d5542923f200f95bddf524f36fd495f78aa28e3c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 16 Sep 2022 13:20:48 +0100
+Subject: [PATCH] nvmem: add driver handling U-Boot environment variables
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+U-Boot stores its setup as environment variables. It's a list of
+key-value pairs stored on flash device with a custom header.
+
+This commit adds an NVMEM driver that:
+1. Provides NVMEM access to environment vars binary data
+2. Extracts variables as NVMEM cells
+
+Current Linux's NVMEM sysfs API allows reading whole NVMEM data block.
+It can be used by user-space tools for reading U-Boot env vars block
+without the hassle of finding its location. Parsing will still need to
+be re-done there.
+
+Kernel-parsed NVMEM cells can be read however by Linux drivers. This may
+be useful for Ethernet drivers for reading device MAC address which is
+often stored as U-Boot env variable.
+
+Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220916122100.170016-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ MAINTAINERS | 1 +
+ drivers/nvmem/Kconfig | 13 +++
+ drivers/nvmem/Makefile | 2 +
+ drivers/nvmem/u-boot-env.c | 218 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 234 insertions(+)
+ create mode 100644 drivers/nvmem/u-boot-env.c
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -344,4 +344,17 @@ config NVMEM_APPLE_EFUSES
+ This driver can also be built as a module. If so, the module will
+ be called nvmem-apple-efuses.
+
++config NVMEM_U_BOOT_ENV
++ tristate "U-Boot environment variables support"
++ depends on OF && MTD
++ select CRC32
++ help
++ U-Boot stores its setup as environment variables. This driver adds
++ support for verifying & exporting such data. It also exposes variables
++ as NVMEM cells so they can be referenced by other drivers.
++
++ Currently this drivers works only with env variables on top of MTD.
++
++ If compiled as module it will be called nvmem_u-boot-env.
++
+ endif
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -69,3 +69,5 @@ obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvme
+ nvmem-apple-efuses-y := apple-efuses.o
+ obj-$(CONFIG_MICROCHIP_OTPC) += nvmem-microchip-otpc.o
+ nvmem-microchip-otpc-y := microchip-otpc.o
++obj-$(CONFIG_NVMEM_U_BOOT_ENV) += nvmem_u-boot-env.o
++nvmem_u-boot-env-y := u-boot-env.o
+--- /dev/null
++++ b/drivers/nvmem/u-boot-env.c
+@@ -0,0 +1,218 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2022 Rafał Miłecki <rafal@milecki.pl>
++ */
++
++#include <linux/crc32.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++
++enum u_boot_env_format {
++ U_BOOT_FORMAT_SINGLE,
++ U_BOOT_FORMAT_REDUNDANT,
++};
++
++struct u_boot_env {
++ struct device *dev;
++ enum u_boot_env_format format;
++
++ struct mtd_info *mtd;
++
++ /* Cells */
++ struct nvmem_cell_info *cells;
++ int ncells;
++};
++
++struct u_boot_env_image_single {
++ __le32 crc32;
++ uint8_t data[];
++} __packed;
++
++struct u_boot_env_image_redundant {
++ __le32 crc32;
++ u8 mark;
++ uint8_t data[];
++} __packed;
++
++static int u_boot_env_read(void *context, unsigned int offset, void *val,
++ size_t bytes)
++{
++ struct u_boot_env *priv = context;
++ struct device *dev = priv->dev;
++ size_t bytes_read;
++ int err;
++
++ err = mtd_read(priv->mtd, offset, bytes, &bytes_read, val);
++ if (err && !mtd_is_bitflip(err)) {
++ dev_err(dev, "Failed to read from mtd: %d\n", err);
++ return err;
++ }
++
++ if (bytes_read != bytes) {
++ dev_err(dev, "Failed to read %zu bytes\n", bytes);
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf,
++ size_t data_offset, size_t data_len)
++{
++ struct device *dev = priv->dev;
++ char *data = buf + data_offset;
++ char *var, *value, *eq;
++ int idx;
++
++ priv->ncells = 0;
++ for (var = data; var < data + data_len && *var; var += strlen(var) + 1)
++ priv->ncells++;
++
++ priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL);
++ if (!priv->cells)
++ return -ENOMEM;
++
++ for (var = data, idx = 0;
++ var < data + data_len && *var;
++ var = value + strlen(value) + 1, idx++) {
++ eq = strchr(var, '=');
++ if (!eq)
++ break;
++ *eq = '\0';
++ value = eq + 1;
++
++ priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL);
++ if (!priv->cells[idx].name)
++ return -ENOMEM;
++ priv->cells[idx].offset = data_offset + value - data;
++ priv->cells[idx].bytes = strlen(value);
++ }
++
++ if (WARN_ON(idx != priv->ncells))
++ priv->ncells = idx;
++
++ return 0;
++}
++
++static int u_boot_env_parse(struct u_boot_env *priv)
++{
++ struct device *dev = priv->dev;
++ size_t crc32_data_offset;
++ size_t crc32_data_len;
++ size_t crc32_offset;
++ size_t data_offset;
++ size_t data_len;
++ uint32_t crc32;
++ uint32_t calc;
++ size_t bytes;
++ uint8_t *buf;
++ int err;
++
++ buf = kcalloc(1, priv->mtd->size, GFP_KERNEL);
++ if (!buf) {
++ err = -ENOMEM;
++ goto err_out;
++ }
++
++ err = mtd_read(priv->mtd, 0, priv->mtd->size, &bytes, buf);
++ if ((err && !mtd_is_bitflip(err)) || bytes != priv->mtd->size) {
++ dev_err(dev, "Failed to read from mtd: %d\n", err);
++ goto err_kfree;
++ }
++
++ switch (priv->format) {
++ case U_BOOT_FORMAT_SINGLE:
++ crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
++ data_offset = offsetof(struct u_boot_env_image_single, data);
++ break;
++ case U_BOOT_FORMAT_REDUNDANT:
++ crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_redundant, mark);
++ data_offset = offsetof(struct u_boot_env_image_redundant, data);
++ break;
++ }
++ crc32 = le32_to_cpu(*(uint32_t *)(buf + crc32_offset));
++ crc32_data_len = priv->mtd->size - crc32_data_offset;
++ data_len = priv->mtd->size - data_offset;
++
++ calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
++ if (calc != crc32) {
++ dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
++ err = -EINVAL;
++ goto err_kfree;
++ }
++
++ buf[priv->mtd->size - 1] = '\0';
++ err = u_boot_env_add_cells(priv, buf, data_offset, data_len);
++ if (err)
++ dev_err(dev, "Failed to add cells: %d\n", err);
++
++err_kfree:
++ kfree(buf);
++err_out:
++ return err;
++}
++
++static int u_boot_env_probe(struct platform_device *pdev)
++{
++ struct nvmem_config config = {
++ .name = "u-boot-env",
++ .reg_read = u_boot_env_read,
++ };
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++ struct u_boot_env *priv;
++ int err;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++ priv->dev = dev;
++
++ priv->format = (uintptr_t)of_device_get_match_data(dev);
++
++ priv->mtd = of_get_mtd_device_by_node(np);
++ if (IS_ERR(priv->mtd)) {
++ dev_err_probe(dev, PTR_ERR(priv->mtd), "Failed to get %pOF MTD\n", np);
++ return PTR_ERR(priv->mtd);
++ }
++
++ err = u_boot_env_parse(priv);
++ if (err)
++ return err;
++
++ config.dev = dev;
++ config.cells = priv->cells;
++ config.ncells = priv->ncells;
++ config.priv = priv;
++ config.size = priv->mtd->size;
++
++ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
++}
++
++static const struct of_device_id u_boot_env_of_match_table[] = {
++ { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, },
++ { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++ { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++ {},
++};
++
++static struct platform_driver u_boot_env_driver = {
++ .probe = u_boot_env_probe,
++ .driver = {
++ .name = "u_boot_env",
++ .of_match_table = u_boot_env_of_match_table,
++ },
++};
++module_platform_driver(u_boot_env_driver);
++
++MODULE_AUTHOR("Rafał Miłecki");
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table);
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0004-nvmem-brcm_nvram-Use-kzalloc-for-allocating-only-one.patch b/target/linux/generic/backport-6.1/807-v6.1-0004-nvmem-brcm_nvram-Use-kzalloc-for-allocating-only-one.patch
new file mode 100644
index 0000000000..48ad63fab5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0004-nvmem-brcm_nvram-Use-kzalloc-for-allocating-only-one.patch
@@ -0,0 +1,29 @@
+From d3524bb5b9a0c567b853a0024526afe87dde01ed Mon Sep 17 00:00:00 2001
+From: Kenneth Lee <klee33@uw.edu>
+Date: Fri, 16 Sep 2022 13:20:52 +0100
+Subject: [PATCH] nvmem: brcm_nvram: Use kzalloc for allocating only one
+ element
+
+Use kzalloc(...) rather than kcalloc(1, ...) because the number of
+elements we are specifying in this case is 1, so kzalloc would
+accomplish the same thing and we can simplify.
+
+Signed-off-by: Kenneth Lee <klee33@uw.edu>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220916122100.170016-6-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/brcm_nvram.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/brcm_nvram.c
++++ b/drivers/nvmem/brcm_nvram.c
+@@ -96,7 +96,7 @@ static int brcm_nvram_parse(struct brcm_
+
+ len = le32_to_cpu(header.len);
+
+- data = kcalloc(1, len, GFP_KERNEL);
++ data = kzalloc(len, GFP_KERNEL);
+ memcpy_fromio(data, priv->base, len);
+ data[len - 1] = '\0';
+
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0005-nvmem-prefix-all-symbols-with-NVMEM_.patch b/target/linux/generic/backport-6.1/807-v6.1-0005-nvmem-prefix-all-symbols-with-NVMEM_.patch
new file mode 100644
index 0000000000..ae02439bd7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0005-nvmem-prefix-all-symbols-with-NVMEM_.patch
@@ -0,0 +1,281 @@
+From 28fc7c986f01fdcfd28af648be2597624cac0e27 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 16 Sep 2022 13:20:54 +0100
+Subject: [PATCH] nvmem: prefix all symbols with NVMEM_
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This unifies all NVMEM symbols. They follow one style now.
+
+Reviewed-by: Matthias Brugger <matthias.bgg@gmail.com>
+Acked-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220916122100.170016-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm/configs/multi_v7_defconfig | 6 +++---
+ arch/arm/configs/qcom_defconfig | 2 +-
+ arch/arm64/configs/defconfig | 10 +++++-----
+ arch/mips/configs/ci20_defconfig | 2 +-
+ drivers/cpufreq/Kconfig.arm | 2 +-
+ drivers/nvmem/Kconfig | 24 ++++++++++++------------
+ drivers/nvmem/Makefile | 24 ++++++++++++------------
+ drivers/soc/mediatek/Kconfig | 2 +-
+ drivers/thermal/qcom/Kconfig | 2 +-
+ 9 files changed, 37 insertions(+), 37 deletions(-)
+
+--- a/arch/arm/configs/multi_v7_defconfig
++++ b/arch/arm/configs/multi_v7_defconfig
+@@ -1125,10 +1125,10 @@ CONFIG_TI_PIPE3=y
+ CONFIG_TWL4030_USB=m
+ CONFIG_RAS=y
+ CONFIG_NVMEM_IMX_OCOTP=y
+-CONFIG_ROCKCHIP_EFUSE=m
++CONFIG_NVMEM_ROCKCHIP_EFUSE=m
+ CONFIG_NVMEM_SUNXI_SID=y
+ CONFIG_NVMEM_VF610_OCOTP=y
+-CONFIG_MESON_MX_EFUSE=m
++CONFIG_NVMEM_MESON_MX_EFUSE=m
+ CONFIG_NVMEM_RMEM=m
+ CONFIG_FSI=m
+ CONFIG_FSI_MASTER_GPIO=m
+--- a/arch/arm/configs/qcom_defconfig
++++ b/arch/arm/configs/qcom_defconfig
+@@ -274,7 +274,7 @@ CONFIG_PHY_QCOM_USB_HS=y
+ CONFIG_PHY_QCOM_USB_HSIC=y
+ CONFIG_PHY_QCOM_QMP=y
+ CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y
+-CONFIG_QCOM_QFPROM=y
++CONFIG_NVMEM_QCOM_QFPROM=y
+ CONFIG_INTERCONNECT=y
+ CONFIG_INTERCONNECT_QCOM=y
+ CONFIG_INTERCONNECT_QCOM_MSM8974=m
+--- a/arch/arm64/configs/defconfig
++++ b/arch/arm64/configs/defconfig
+@@ -1135,11 +1135,11 @@ CONFIG_QCOM_L3_PMU=y
+ CONFIG_NVMEM_IMX_OCOTP=y
+ CONFIG_NVMEM_IMX_OCOTP_SCU=y
+ CONFIG_QCOM_QFPROM=y
+-CONFIG_MTK_EFUSE=y
+-CONFIG_ROCKCHIP_EFUSE=y
++CONFIG_NVMEM_MTK_EFUSE=y
++CONFIG_NVMEM_ROCKCHIP_EFUSE=y
+ CONFIG_NVMEM_SUNXI_SID=y
+-CONFIG_UNIPHIER_EFUSE=y
+-CONFIG_MESON_EFUSE=m
++CONFIG_NVMEM_UNIPHIER_EFUSE=y
++CONFIG_NVMEM_MESON_EFUSE=m
+ CONFIG_NVMEM_RMEM=m
+ CONFIG_FPGA=y
+ CONFIG_FPGA_MGR_STRATIX10_SOC=m
+--- a/arch/mips/configs/ci20_defconfig
++++ b/arch/mips/configs/ci20_defconfig
+@@ -137,7 +137,7 @@ CONFIG_MEMORY=y
+ CONFIG_JZ4780_NEMC=y
+ CONFIG_PWM=y
+ CONFIG_PWM_JZ4740=m
+-CONFIG_JZ4780_EFUSE=y
++CONFIG_NVMEM_JZ4780_EFUSE=y
+ CONFIG_JZ4770_PHY=y
+ CONFIG_EXT4_FS=y
+ # CONFIG_DNOTIFY is not set
+--- a/drivers/cpufreq/Kconfig.arm
++++ b/drivers/cpufreq/Kconfig.arm
+@@ -153,7 +153,7 @@ config ARM_OMAP2PLUS_CPUFREQ
+ config ARM_QCOM_CPUFREQ_NVMEM
+ tristate "Qualcomm nvmem based CPUFreq"
+ depends on ARCH_QCOM
+- depends on QCOM_QFPROM
++ depends on NVMEM_QCOM_QFPROM
+ depends on QCOM_SMEM
+ select PM_OPP
+ help
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -52,7 +52,7 @@ config NVMEM_IMX_OCOTP_SCU
+ This is a driver for the SCU On-Chip OTP Controller (OCOTP)
+ available on i.MX8 SoCs.
+
+-config JZ4780_EFUSE
++config NVMEM_JZ4780_EFUSE
+ tristate "JZ4780 EFUSE Memory Support"
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on HAS_IOMEM
+@@ -96,7 +96,7 @@ config NVMEM_MXS_OCOTP
+ This driver can also be built as a module. If so, the module
+ will be called nvmem-mxs-ocotp.
+
+-config MTK_EFUSE
++config NVMEM_MTK_EFUSE
+ tristate "Mediatek SoCs EFUSE support"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on HAS_IOMEM
+@@ -107,7 +107,7 @@ config MTK_EFUSE
+ This driver can also be built as a module. If so, the module
+ will be called efuse-mtk.
+
+-config MICROCHIP_OTPC
++config NVMEM_MICROCHIP_OTPC
+ tristate "Microchip OTPC support"
+ depends on ARCH_AT91 || COMPILE_TEST
+ help
+@@ -126,7 +126,7 @@ config NVMEM_NINTENDO_OTP
+ This driver can also be built as a module. If so, the module
+ will be called nvmem-nintendo-otp.
+
+-config QCOM_QFPROM
++config NVMEM_QCOM_QFPROM
+ tristate "QCOM QFPROM Support"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on HAS_IOMEM
+@@ -145,7 +145,7 @@ config NVMEM_SPMI_SDAM
+ Qualcomm Technologies, Inc. PMICs. It provides the clients
+ an interface to read/write to the SDAM module's shared memory.
+
+-config ROCKCHIP_EFUSE
++config NVMEM_ROCKCHIP_EFUSE
+ tristate "Rockchip eFuse Support"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on HAS_IOMEM
+@@ -156,7 +156,7 @@ config ROCKCHIP_EFUSE
+ This driver can also be built as a module. If so, the module
+ will be called nvmem_rockchip_efuse.
+
+-config ROCKCHIP_OTP
++config NVMEM_ROCKCHIP_OTP
+ tristate "Rockchip OTP controller support"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on HAS_IOMEM
+@@ -199,7 +199,7 @@ config NVMEM_SUNXI_SID
+ This driver can also be built as a module. If so, the module
+ will be called nvmem_sunxi_sid.
+
+-config UNIPHIER_EFUSE
++config NVMEM_UNIPHIER_EFUSE
+ tristate "UniPhier SoCs eFuse support"
+ depends on ARCH_UNIPHIER || COMPILE_TEST
+ depends on HAS_IOMEM
+@@ -221,7 +221,7 @@ config NVMEM_VF610_OCOTP
+ This driver can also be build as a module. If so, the module will
+ be called nvmem-vf610-ocotp.
+
+-config MESON_EFUSE
++config NVMEM_MESON_EFUSE
+ tristate "Amlogic Meson GX eFuse Support"
+ depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM
+ help
+@@ -231,7 +231,7 @@ config MESON_EFUSE
+ This driver can also be built as a module. If so, the module
+ will be called nvmem_meson_efuse.
+
+-config MESON_MX_EFUSE
++config NVMEM_MESON_MX_EFUSE
+ tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support"
+ depends on ARCH_MESON || COMPILE_TEST
+ help
+@@ -251,13 +251,13 @@ config NVMEM_SNVS_LPGPR
+ This driver can also be built as a module. If so, the module
+ will be called nvmem-snvs-lpgpr.
+
+-config RAVE_SP_EEPROM
++config NVMEM_RAVE_SP_EEPROM
+ tristate "Rave SP EEPROM Support"
+ depends on RAVE_SP_CORE
+ help
+ Say y here to enable Rave SP EEPROM support.
+
+-config SC27XX_EFUSE
++config NVMEM_SC27XX_EFUSE
+ tristate "Spreadtrum SC27XX eFuse Support"
+ depends on MFD_SC27XX_PMIC || COMPILE_TEST
+ depends on HAS_IOMEM
+@@ -278,7 +278,7 @@ config NVMEM_ZYNQMP
+
+ If sure, say yes. If unsure, say no.
+
+-config SPRD_EFUSE
++config NVMEM_SPRD_EFUSE
+ tristate "Spreadtrum SoC eFuse Support"
+ depends on ARCH_SPRD || COMPILE_TEST
+ depends on HAS_IOMEM
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -15,7 +15,7 @@ obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-i
+ nvmem-imx-ocotp-y := imx-ocotp.o
+ obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU) += nvmem-imx-ocotp-scu.o
+ nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o
+-obj-$(CONFIG_JZ4780_EFUSE) += nvmem_jz4780_efuse.o
++obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o
+ nvmem_jz4780_efuse-y := jz4780-efuse.o
+ obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o
+ nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o
+@@ -25,37 +25,37 @@ obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-m
+ nvmem-mxs-ocotp-y := mxs-ocotp.o
+ obj-$(CONFIG_NVMEM_NINTENDO_OTP) += nvmem-nintendo-otp.o
+ nvmem-nintendo-otp-y := nintendo-otp.o
+-obj-$(CONFIG_MTK_EFUSE) += nvmem_mtk-efuse.o
++obj-$(CONFIG_NVMEM_MTK_EFUSE) += nvmem_mtk-efuse.o
+ nvmem_mtk-efuse-y := mtk-efuse.o
+-obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o
++obj-$(CONFIG_NVMEM_QCOM_QFPROM) += nvmem_qfprom.o
+ nvmem_qfprom-y := qfprom.o
+ obj-$(CONFIG_NVMEM_SPMI_SDAM) += nvmem_qcom-spmi-sdam.o
+ nvmem_qcom-spmi-sdam-y += qcom-spmi-sdam.o
+-obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o
++obj-$(CONFIG_NVMEM_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o
+ nvmem_rockchip_efuse-y := rockchip-efuse.o
+-obj-$(CONFIG_ROCKCHIP_OTP) += nvmem-rockchip-otp.o
++obj-$(CONFIG_NVMEM_ROCKCHIP_OTP) += nvmem-rockchip-otp.o
+ nvmem-rockchip-otp-y := rockchip-otp.o
+ obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
+ nvmem_stm32_romem-y := stm32-romem.o
+ obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o
+ nvmem_sunxi_sid-y := sunxi_sid.o
+-obj-$(CONFIG_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o
++obj-$(CONFIG_NVMEM_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o
+ nvmem-uniphier-efuse-y := uniphier-efuse.o
+ obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
+ nvmem-vf610-ocotp-y := vf610-ocotp.o
+-obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o
++obj-$(CONFIG_NVMEM_MESON_EFUSE) += nvmem_meson_efuse.o
+ nvmem_meson_efuse-y := meson-efuse.o
+-obj-$(CONFIG_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o
++obj-$(CONFIG_NVMEM_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o
+ nvmem_meson_mx_efuse-y := meson-mx-efuse.o
+ obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o
+ nvmem_snvs_lpgpr-y := snvs_lpgpr.o
+-obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o
++obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o
+ nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o
+-obj-$(CONFIG_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o
++obj-$(CONFIG_NVMEM_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o
+ nvmem-sc27xx-efuse-y := sc27xx-efuse.o
+ obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o
+ nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o
+-obj-$(CONFIG_SPRD_EFUSE) += nvmem_sprd_efuse.o
++obj-$(CONFIG_NVMEM_SPRD_EFUSE) += nvmem_sprd_efuse.o
+ nvmem_sprd_efuse-y := sprd-efuse.o
+ obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o
+ nvmem-rmem-y := rmem.o
+@@ -67,7 +67,7 @@ obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvm
+ nvmem_sunplus_ocotp-y := sunplus-ocotp.o
+ obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
+ nvmem-apple-efuses-y := apple-efuses.o
+-obj-$(CONFIG_MICROCHIP_OTPC) += nvmem-microchip-otpc.o
++obj-$(CONFIG_NVMEM_MICROCHIP_OTPC) += nvmem-microchip-otpc.o
+ nvmem-microchip-otpc-y := microchip-otpc.o
+ obj-$(CONFIG_NVMEM_U_BOOT_ENV) += nvmem_u-boot-env.o
+ nvmem_u-boot-env-y := u-boot-env.o
+--- a/drivers/thermal/qcom/Kconfig
++++ b/drivers/thermal/qcom/Kconfig
+@@ -1,7 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config QCOM_TSENS
+ tristate "Qualcomm TSENS Temperature Alarm"
+- depends on QCOM_QFPROM
++ depends on NVMEM_QCOM_QFPROM
+ depends on ARCH_QCOM || COMPILE_TEST
+ help
+ This enables the thermal sysfs driver for the TSENS device. It shows
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0006-nvmem-sort-config-symbols-alphabetically.patch b/target/linux/generic/backport-6.1/807-v6.1-0006-nvmem-sort-config-symbols-alphabetically.patch
new file mode 100644
index 0000000000..4e45524bff
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0006-nvmem-sort-config-symbols-alphabetically.patch
@@ -0,0 +1,535 @@
+From a06d9e5a63b7c2f622c908cd9600ce735e70f7c6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 16 Sep 2022 13:20:55 +0100
+Subject: [PATCH] nvmem: sort config symbols alphabetically
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+1. Match what most subsystems do
+2. Simplify maintenance a bit
+3. Reduce amount of conflicts for new drivers patches
+
+While at it unify indent level in Makefile.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220916122100.170016-9-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 300 +++++++++++++++++++++--------------------
+ drivers/nvmem/Makefile | 114 ++++++++--------
+ 2 files changed, 208 insertions(+), 206 deletions(-)
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -21,6 +21,40 @@ config NVMEM_SYSFS
+ This interface is mostly used by userspace applications to
+ read/write directly into nvmem.
+
++# Devices
++
++config NVMEM_APPLE_EFUSES
++ tristate "Apple eFuse support"
++ depends on ARCH_APPLE || COMPILE_TEST
++ default ARCH_APPLE
++ help
++ Say y here to enable support for reading eFuses on Apple SoCs
++ such as the M1. These are e.g. used to store factory programmed
++ calibration data required for the PCIe or the USB-C PHY.
++
++ This driver can also be built as a module. If so, the module will
++ be called nvmem-apple-efuses.
++
++config NVMEM_BCM_OCOTP
++ tristate "Broadcom On-Chip OTP Controller support"
++ depends on ARCH_BCM_IPROC || COMPILE_TEST
++ depends on HAS_IOMEM
++ default ARCH_BCM_IPROC
++ help
++ Say y here to enable read/write access to the Broadcom OTP
++ controller.
++
++ This driver can also be built as a module. If so, the module
++ will be called nvmem-bcm-ocotp.
++
++config NVMEM_BRCM_NVRAM
++ tristate "Broadcom's NVRAM support"
++ depends on ARCH_BCM_5301X || COMPILE_TEST
++ depends on HAS_IOMEM
++ help
++ This driver provides support for Broadcom's NVRAM that can be accessed
++ using I/O mapping.
++
+ config NVMEM_IMX_IIM
+ tristate "i.MX IC Identification Module support"
+ depends on ARCH_MXC || COMPILE_TEST
+@@ -64,6 +98,19 @@ config NVMEM_JZ4780_EFUSE
+ To compile this driver as a module, choose M here: the module
+ will be called nvmem_jz4780_efuse.
+
++config NVMEM_LAYERSCAPE_SFP
++ tristate "Layerscape SFP (Security Fuse Processor) support"
++ depends on ARCH_LAYERSCAPE || COMPILE_TEST
++ depends on HAS_IOMEM
++ select REGMAP_MMIO
++ help
++ This driver provides support to read the eFuses on Freescale
++ Layerscape SoC's. For example, the vendor provides a per part
++ unique ID there.
++
++ This driver can also be built as a module. If so, the module
++ will be called layerscape-sfp.
++
+ config NVMEM_LPC18XX_EEPROM
+ tristate "NXP LPC18XX EEPROM Memory Support"
+ depends on ARCH_LPC18XX || COMPILE_TEST
+@@ -84,17 +131,32 @@ config NVMEM_LPC18XX_OTP
+ To compile this driver as a module, choose M here: the module
+ will be called nvmem_lpc18xx_otp.
+
+-config NVMEM_MXS_OCOTP
+- tristate "Freescale MXS On-Chip OTP Memory Support"
+- depends on ARCH_MXS || COMPILE_TEST
+- depends on HAS_IOMEM
++config NVMEM_MESON_EFUSE
++ tristate "Amlogic Meson GX eFuse Support"
++ depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM
+ help
+- If you say Y here, you will get readonly access to the
+- One Time Programmable memory pages that are stored
+- on the Freescale i.MX23/i.MX28 processor.
++ This is a driver to retrieve specific values from the eFuse found on
++ the Amlogic Meson GX SoCs.
+
+ This driver can also be built as a module. If so, the module
+- will be called nvmem-mxs-ocotp.
++ will be called nvmem_meson_efuse.
++
++config NVMEM_MESON_MX_EFUSE
++ tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support"
++ depends on ARCH_MESON || COMPILE_TEST
++ help
++ This is a driver to retrieve specific values from the eFuse found on
++ the Amlogic Meson6, Meson8 and Meson8b SoCs.
++
++ This driver can also be built as a module. If so, the module
++ will be called nvmem_meson_mx_efuse.
++
++config NVMEM_MICROCHIP_OTPC
++ tristate "Microchip OTPC support"
++ depends on ARCH_AT91 || COMPILE_TEST
++ help
++ This driver enable the OTP controller available on Microchip SAMA7G5
++ SoCs. It controlls the access to the OTP memory connected to it.
+
+ config NVMEM_MTK_EFUSE
+ tristate "Mediatek SoCs EFUSE support"
+@@ -107,12 +169,17 @@ config NVMEM_MTK_EFUSE
+ This driver can also be built as a module. If so, the module
+ will be called efuse-mtk.
+
+-config NVMEM_MICROCHIP_OTPC
+- tristate "Microchip OTPC support"
+- depends on ARCH_AT91 || COMPILE_TEST
++config NVMEM_MXS_OCOTP
++ tristate "Freescale MXS On-Chip OTP Memory Support"
++ depends on ARCH_MXS || COMPILE_TEST
++ depends on HAS_IOMEM
+ help
+- This driver enable the OTP controller available on Microchip SAMA7G5
+- SoCs. It controlls the access to the OTP memory connected to it.
++ If you say Y here, you will get readonly access to the
++ One Time Programmable memory pages that are stored
++ on the Freescale i.MX23/i.MX28 processor.
++
++ This driver can also be built as a module. If so, the module
++ will be called nvmem-mxs-ocotp.
+
+ config NVMEM_NINTENDO_OTP
+ tristate "Nintendo Wii and Wii U OTP Support"
+@@ -137,13 +204,21 @@ config NVMEM_QCOM_QFPROM
+ This driver can also be built as a module. If so, the module
+ will be called nvmem_qfprom.
+
+-config NVMEM_SPMI_SDAM
+- tristate "SPMI SDAM Support"
+- depends on SPMI
++config NVMEM_RAVE_SP_EEPROM
++ tristate "Rave SP EEPROM Support"
++ depends on RAVE_SP_CORE
+ help
+- This driver supports the Shared Direct Access Memory Module on
+- Qualcomm Technologies, Inc. PMICs. It provides the clients
+- an interface to read/write to the SDAM module's shared memory.
++ Say y here to enable Rave SP EEPROM support.
++
++config NVMEM_RMEM
++ tristate "Reserved Memory Based Driver Support"
++ depends on HAS_IOMEM
++ help
++ This driver maps reserved memory into an nvmem device. It might be
++ useful to expose information left by firmware in memory.
++
++ This driver can also be built as a module. If so, the module
++ will be called nvmem-rmem.
+
+ config NVMEM_ROCKCHIP_EFUSE
+ tristate "Rockchip eFuse Support"
+@@ -167,79 +242,16 @@ config NVMEM_ROCKCHIP_OTP
+ This driver can also be built as a module. If so, the module
+ will be called nvmem_rockchip_otp.
+
+-config NVMEM_BCM_OCOTP
+- tristate "Broadcom On-Chip OTP Controller support"
+- depends on ARCH_BCM_IPROC || COMPILE_TEST
+- depends on HAS_IOMEM
+- default ARCH_BCM_IPROC
+- help
+- Say y here to enable read/write access to the Broadcom OTP
+- controller.
+-
+- This driver can also be built as a module. If so, the module
+- will be called nvmem-bcm-ocotp.
+-
+-config NVMEM_STM32_ROMEM
+- tristate "STMicroelectronics STM32 factory-programmed memory support"
+- depends on ARCH_STM32 || COMPILE_TEST
+- help
+- Say y here to enable read-only access for STMicroelectronics STM32
+- factory-programmed memory area.
+-
+- This driver can also be built as a module. If so, the module
+- will be called nvmem-stm32-romem.
+-
+-config NVMEM_SUNXI_SID
+- tristate "Allwinner SoCs SID support"
+- depends on ARCH_SUNXI
+- help
+- This is a driver for the 'security ID' available on various Allwinner
+- devices.
+-
+- This driver can also be built as a module. If so, the module
+- will be called nvmem_sunxi_sid.
+-
+-config NVMEM_UNIPHIER_EFUSE
+- tristate "UniPhier SoCs eFuse support"
+- depends on ARCH_UNIPHIER || COMPILE_TEST
+- depends on HAS_IOMEM
+- help
+- This is a simple driver to dump specified values of UniPhier SoC
+- from eFuse.
+-
+- This driver can also be built as a module. If so, the module
+- will be called nvmem-uniphier-efuse.
+-
+-config NVMEM_VF610_OCOTP
+- tristate "VF610 SoC OCOTP support"
+- depends on SOC_VF610 || COMPILE_TEST
++config NVMEM_SC27XX_EFUSE
++ tristate "Spreadtrum SC27XX eFuse Support"
++ depends on MFD_SC27XX_PMIC || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+- This is a driver for the 'OCOTP' peripheral available on Vybrid
+- devices like VF5xx and VF6xx.
+-
+- This driver can also be build as a module. If so, the module will
+- be called nvmem-vf610-ocotp.
+-
+-config NVMEM_MESON_EFUSE
+- tristate "Amlogic Meson GX eFuse Support"
+- depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM
+- help
+- This is a driver to retrieve specific values from the eFuse found on
+- the Amlogic Meson GX SoCs.
+-
+- This driver can also be built as a module. If so, the module
+- will be called nvmem_meson_efuse.
+-
+-config NVMEM_MESON_MX_EFUSE
+- tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support"
+- depends on ARCH_MESON || COMPILE_TEST
+- help
+- This is a driver to retrieve specific values from the eFuse found on
+- the Amlogic Meson6, Meson8 and Meson8b SoCs.
++ This is a simple driver to dump specified values of Spreadtrum
++ SC27XX PMICs from eFuse.
+
+ This driver can also be built as a module. If so, the module
+- will be called nvmem_meson_mx_efuse.
++ will be called nvmem-sc27xx-efuse.
+
+ config NVMEM_SNVS_LPGPR
+ tristate "Support for Low Power General Purpose Register"
+@@ -251,32 +263,13 @@ config NVMEM_SNVS_LPGPR
+ This driver can also be built as a module. If so, the module
+ will be called nvmem-snvs-lpgpr.
+
+-config NVMEM_RAVE_SP_EEPROM
+- tristate "Rave SP EEPROM Support"
+- depends on RAVE_SP_CORE
+- help
+- Say y here to enable Rave SP EEPROM support.
+-
+-config NVMEM_SC27XX_EFUSE
+- tristate "Spreadtrum SC27XX eFuse Support"
+- depends on MFD_SC27XX_PMIC || COMPILE_TEST
+- depends on HAS_IOMEM
+- help
+- This is a simple driver to dump specified values of Spreadtrum
+- SC27XX PMICs from eFuse.
+-
+- This driver can also be built as a module. If so, the module
+- will be called nvmem-sc27xx-efuse.
+-
+-config NVMEM_ZYNQMP
+- bool "Xilinx ZYNQMP SoC nvmem firmware support"
+- depends on ARCH_ZYNQMP
++config NVMEM_SPMI_SDAM
++ tristate "SPMI SDAM Support"
++ depends on SPMI
+ help
+- This is a driver to access hardware related data like
+- soc revision, IDCODE... etc by using the firmware
+- interface.
+-
+- If sure, say yes. If unsure, say no.
++ This driver supports the Shared Direct Access Memory Module on
++ Qualcomm Technologies, Inc. PMICs. It provides the clients
++ an interface to read/write to the SDAM module's shared memory.
+
+ config NVMEM_SPRD_EFUSE
+ tristate "Spreadtrum SoC eFuse Support"
+@@ -289,36 +282,15 @@ config NVMEM_SPRD_EFUSE
+ This driver can also be built as a module. If so, the module
+ will be called nvmem-sprd-efuse.
+
+-config NVMEM_RMEM
+- tristate "Reserved Memory Based Driver Support"
+- depends on HAS_IOMEM
+- help
+- This driver maps reserved memory into an nvmem device. It might be
+- useful to expose information left by firmware in memory.
+-
+- This driver can also be built as a module. If so, the module
+- will be called nvmem-rmem.
+-
+-config NVMEM_BRCM_NVRAM
+- tristate "Broadcom's NVRAM support"
+- depends on ARCH_BCM_5301X || COMPILE_TEST
+- depends on HAS_IOMEM
+- help
+- This driver provides support for Broadcom's NVRAM that can be accessed
+- using I/O mapping.
+-
+-config NVMEM_LAYERSCAPE_SFP
+- tristate "Layerscape SFP (Security Fuse Processor) support"
+- depends on ARCH_LAYERSCAPE || COMPILE_TEST
+- depends on HAS_IOMEM
+- select REGMAP_MMIO
++config NVMEM_STM32_ROMEM
++ tristate "STMicroelectronics STM32 factory-programmed memory support"
++ depends on ARCH_STM32 || COMPILE_TEST
+ help
+- This driver provides support to read the eFuses on Freescale
+- Layerscape SoC's. For example, the vendor provides a per part
+- unique ID there.
++ Say y here to enable read-only access for STMicroelectronics STM32
++ factory-programmed memory area.
+
+ This driver can also be built as a module. If so, the module
+- will be called layerscape-sfp.
++ will be called nvmem-stm32-romem.
+
+ config NVMEM_SUNPLUS_OCOTP
+ tristate "Sunplus SoC OTP support"
+@@ -332,17 +304,15 @@ config NVMEM_SUNPLUS_OCOTP
+ This driver can also be built as a module. If so, the module
+ will be called nvmem-sunplus-ocotp.
+
+-config NVMEM_APPLE_EFUSES
+- tristate "Apple eFuse support"
+- depends on ARCH_APPLE || COMPILE_TEST
+- default ARCH_APPLE
++config NVMEM_SUNXI_SID
++ tristate "Allwinner SoCs SID support"
++ depends on ARCH_SUNXI
+ help
+- Say y here to enable support for reading eFuses on Apple SoCs
+- such as the M1. These are e.g. used to store factory programmed
+- calibration data required for the PCIe or the USB-C PHY.
++ This is a driver for the 'security ID' available on various Allwinner
++ devices.
+
+- This driver can also be built as a module. If so, the module will
+- be called nvmem-apple-efuses.
++ This driver can also be built as a module. If so, the module
++ will be called nvmem_sunxi_sid.
+
+ config NVMEM_U_BOOT_ENV
+ tristate "U-Boot environment variables support"
+@@ -357,4 +327,36 @@ config NVMEM_U_BOOT_ENV
+
+ If compiled as module it will be called nvmem_u-boot-env.
+
++config NVMEM_UNIPHIER_EFUSE
++ tristate "UniPhier SoCs eFuse support"
++ depends on ARCH_UNIPHIER || COMPILE_TEST
++ depends on HAS_IOMEM
++ help
++ This is a simple driver to dump specified values of UniPhier SoC
++ from eFuse.
++
++ This driver can also be built as a module. If so, the module
++ will be called nvmem-uniphier-efuse.
++
++config NVMEM_VF610_OCOTP
++ tristate "VF610 SoC OCOTP support"
++ depends on SOC_VF610 || COMPILE_TEST
++ depends on HAS_IOMEM
++ help
++ This is a driver for the 'OCOTP' peripheral available on Vybrid
++ devices like VF5xx and VF6xx.
++
++ This driver can also be build as a module. If so, the module will
++ be called nvmem-vf610-ocotp.
++
++config NVMEM_ZYNQMP
++ bool "Xilinx ZYNQMP SoC nvmem firmware support"
++ depends on ARCH_ZYNQMP
++ help
++ This is a driver to access hardware related data like
++ soc revision, IDCODE... etc by using the firmware
++ interface.
++
++ If sure, say yes. If unsure, say no.
++
+ endif
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -7,67 +7,67 @@ obj-$(CONFIG_NVMEM) += nvmem_core.o
+ nvmem_core-y := core.o
+
+ # Devices
+-obj-$(CONFIG_NVMEM_BCM_OCOTP) += nvmem-bcm-ocotp.o
+-nvmem-bcm-ocotp-y := bcm-ocotp.o
+-obj-$(CONFIG_NVMEM_IMX_IIM) += nvmem-imx-iim.o
+-nvmem-imx-iim-y := imx-iim.o
+-obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
+-nvmem-imx-ocotp-y := imx-ocotp.o
++obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
++nvmem-apple-efuses-y := apple-efuses.o
++obj-$(CONFIG_NVMEM_BCM_OCOTP) += nvmem-bcm-ocotp.o
++nvmem-bcm-ocotp-y := bcm-ocotp.o
++obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o
++nvmem_brcm_nvram-y := brcm_nvram.o
++obj-$(CONFIG_NVMEM_IMX_IIM) += nvmem-imx-iim.o
++nvmem-imx-iim-y := imx-iim.o
++obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
++nvmem-imx-ocotp-y := imx-ocotp.o
+ obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU) += nvmem-imx-ocotp-scu.o
+-nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o
+-obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o
+-nvmem_jz4780_efuse-y := jz4780-efuse.o
++nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o
++obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o
++nvmem_jz4780_efuse-y := jz4780-efuse.o
++obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o
++nvmem-layerscape-sfp-y := layerscape-sfp.o
+ obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o
+-nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o
+-obj-$(CONFIG_NVMEM_LPC18XX_OTP) += nvmem_lpc18xx_otp.o
+-nvmem_lpc18xx_otp-y := lpc18xx_otp.o
+-obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o
+-nvmem-mxs-ocotp-y := mxs-ocotp.o
+-obj-$(CONFIG_NVMEM_NINTENDO_OTP) += nvmem-nintendo-otp.o
+-nvmem-nintendo-otp-y := nintendo-otp.o
++nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o
++obj-$(CONFIG_NVMEM_LPC18XX_OTP) += nvmem_lpc18xx_otp.o
++nvmem_lpc18xx_otp-y := lpc18xx_otp.o
++obj-$(CONFIG_NVMEM_MESON_EFUSE) += nvmem_meson_efuse.o
++nvmem_meson_efuse-y := meson-efuse.o
++obj-$(CONFIG_NVMEM_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o
++nvmem_meson_mx_efuse-y := meson-mx-efuse.o
++obj-$(CONFIG_NVMEM_MICROCHIP_OTPC) += nvmem-microchip-otpc.o
++nvmem-microchip-otpc-y := microchip-otpc.o
+ obj-$(CONFIG_NVMEM_MTK_EFUSE) += nvmem_mtk-efuse.o
+-nvmem_mtk-efuse-y := mtk-efuse.o
+-obj-$(CONFIG_NVMEM_QCOM_QFPROM) += nvmem_qfprom.o
+-nvmem_qfprom-y := qfprom.o
+-obj-$(CONFIG_NVMEM_SPMI_SDAM) += nvmem_qcom-spmi-sdam.o
+-nvmem_qcom-spmi-sdam-y += qcom-spmi-sdam.o
++nvmem_mtk-efuse-y := mtk-efuse.o
++obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o
++nvmem-mxs-ocotp-y := mxs-ocotp.o
++obj-$(CONFIG_NVMEM_NINTENDO_OTP) += nvmem-nintendo-otp.o
++nvmem-nintendo-otp-y := nintendo-otp.o
++obj-$(CONFIG_NVMEM_QCOM_QFPROM) += nvmem_qfprom.o
++nvmem_qfprom-y := qfprom.o
++obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o
++nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o
++obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o
++nvmem-rmem-y := rmem.o
+ obj-$(CONFIG_NVMEM_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o
+-nvmem_rockchip_efuse-y := rockchip-efuse.o
++nvmem_rockchip_efuse-y := rockchip-efuse.o
+ obj-$(CONFIG_NVMEM_ROCKCHIP_OTP) += nvmem-rockchip-otp.o
+-nvmem-rockchip-otp-y := rockchip-otp.o
+-obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
+-nvmem_stm32_romem-y := stm32-romem.o
+-obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o
+-nvmem_sunxi_sid-y := sunxi_sid.o
+-obj-$(CONFIG_NVMEM_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o
+-nvmem-uniphier-efuse-y := uniphier-efuse.o
+-obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
+-nvmem-vf610-ocotp-y := vf610-ocotp.o
+-obj-$(CONFIG_NVMEM_MESON_EFUSE) += nvmem_meson_efuse.o
+-nvmem_meson_efuse-y := meson-efuse.o
+-obj-$(CONFIG_NVMEM_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o
+-nvmem_meson_mx_efuse-y := meson-mx-efuse.o
+-obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o
+-nvmem_snvs_lpgpr-y := snvs_lpgpr.o
+-obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o
+-nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o
++nvmem-rockchip-otp-y := rockchip-otp.o
+ obj-$(CONFIG_NVMEM_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o
+-nvmem-sc27xx-efuse-y := sc27xx-efuse.o
+-obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o
+-nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o
+-obj-$(CONFIG_NVMEM_SPRD_EFUSE) += nvmem_sprd_efuse.o
+-nvmem_sprd_efuse-y := sprd-efuse.o
+-obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o
+-nvmem-rmem-y := rmem.o
+-obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o
+-nvmem_brcm_nvram-y := brcm_nvram.o
+-obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o
+-nvmem-layerscape-sfp-y := layerscape-sfp.o
++nvmem-sc27xx-efuse-y := sc27xx-efuse.o
++obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o
++nvmem_snvs_lpgpr-y := snvs_lpgpr.o
++obj-$(CONFIG_NVMEM_SPMI_SDAM) += nvmem_qcom-spmi-sdam.o
++nvmem_qcom-spmi-sdam-y += qcom-spmi-sdam.o
++obj-$(CONFIG_NVMEM_SPRD_EFUSE) += nvmem_sprd_efuse.o
++nvmem_sprd_efuse-y := sprd-efuse.o
++obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o
++nvmem_stm32_romem-y := stm32-romem.o
+ obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o
+-nvmem_sunplus_ocotp-y := sunplus-ocotp.o
+-obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
+-nvmem-apple-efuses-y := apple-efuses.o
+-obj-$(CONFIG_NVMEM_MICROCHIP_OTPC) += nvmem-microchip-otpc.o
+-nvmem-microchip-otpc-y := microchip-otpc.o
+-obj-$(CONFIG_NVMEM_U_BOOT_ENV) += nvmem_u-boot-env.o
+-nvmem_u-boot-env-y := u-boot-env.o
++nvmem_sunplus_ocotp-y := sunplus-ocotp.o
++obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
++nvmem_sunxi_sid-y := sunxi_sid.o
++obj-$(CONFIG_NVMEM_U_BOOT_ENV) += nvmem_u-boot-env.o
++nvmem_u-boot-env-y := u-boot-env.o
++obj-$(CONFIG_NVMEM_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o
++nvmem-uniphier-efuse-y := uniphier-efuse.o
++obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
++nvmem-vf610-ocotp-y := vf610-ocotp.o
++obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o
++nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0007-nvmem-u-boot-env-find-Device-Tree-nodes-for-NVMEM-ce.patch b/target/linux/generic/backport-6.1/807-v6.1-0007-nvmem-u-boot-env-find-Device-Tree-nodes-for-NVMEM-ce.patch
new file mode 100644
index 0000000000..e0a082adc4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0007-nvmem-u-boot-env-find-Device-Tree-nodes-for-NVMEM-ce.patch
@@ -0,0 +1,31 @@
+From d4d432670f7dee0a5432fcffcfc8699b25181ace Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 16 Sep 2022 13:20:57 +0100
+Subject: [PATCH] nvmem: u-boot-env: find Device Tree nodes for NVMEM cells
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+DT binding allows specifying NVMEM cells as NVMEM device (provider)
+subnodes. Looks for such subnodes when building NVMEM cells.
+
+This allows NVMEM consumers to use U-Boot environment variables.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220916122100.170016-11-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/u-boot-env.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -92,6 +92,7 @@ static int u_boot_env_add_cells(struct u
+ return -ENOMEM;
+ priv->cells[idx].offset = data_offset + value - data;
+ priv->cells[idx].bytes = strlen(value);
++ priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
+ }
+
+ if (WARN_ON(idx != priv->ncells))
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0008-nvmem-lan9662-otp-add-support.patch b/target/linux/generic/backport-6.1/807-v6.1-0008-nvmem-lan9662-otp-add-support.patch
new file mode 100644
index 0000000000..945c6128ff
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0008-nvmem-lan9662-otp-add-support.patch
@@ -0,0 +1,274 @@
+From 9e8f208ad5229ddda97cd4a83ecf89c735d99592 Mon Sep 17 00:00:00 2001
+From: Horatiu Vultur <horatiu.vultur@microchip.com>
+Date: Fri, 16 Sep 2022 13:20:59 +0100
+Subject: [PATCH] nvmem: lan9662-otp: add support
+
+Add support for OTP controller available on LAN9662. The OTPC controls
+the access to a non-volatile memory. The size of the memory is 8KB.
+The OTPC can access the memory based on an offset.
+Implement both the read and the write functionality.
+
+Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220916122100.170016-13-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 8 ++
+ drivers/nvmem/Makefile | 2 +
+ drivers/nvmem/lan9662-otpc.c | 222 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 232 insertions(+)
+ create mode 100644 drivers/nvmem/lan9662-otpc.c
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -98,6 +98,14 @@ config NVMEM_JZ4780_EFUSE
+ To compile this driver as a module, choose M here: the module
+ will be called nvmem_jz4780_efuse.
+
++config NVMEM_LAN9662_OTPC
++ tristate "Microchip LAN9662 OTP controller support"
++ depends on SOC_LAN966 || COMPILE_TEST
++ depends on HAS_IOMEM
++ help
++ This driver enables the OTP controller available on Microchip LAN9662
++ SoCs. It controls the access to the OTP memory connected to it.
++
+ config NVMEM_LAYERSCAPE_SFP
+ tristate "Layerscape SFP (Security Fuse Processor) support"
+ depends on ARCH_LAYERSCAPE || COMPILE_TEST
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -21,6 +21,8 @@ obj-$(CONFIG_NVMEM_IMX_OCOTP_SCU) += nvm
+ nvmem-imx-ocotp-scu-y := imx-ocotp-scu.o
+ obj-$(CONFIG_NVMEM_JZ4780_EFUSE) += nvmem_jz4780_efuse.o
+ nvmem_jz4780_efuse-y := jz4780-efuse.o
++obj-$(CONFIG_NVMEM_LAN9662_OTPC) += nvmem-lan9662-otpc.o
++nvmem-lan9662-otpc-y := lan9662-otpc.o
+ obj-$(CONFIG_NVMEM_LAYERSCAPE_SFP) += nvmem-layerscape-sfp.o
+ nvmem-layerscape-sfp-y := layerscape-sfp.o
+ obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o
+--- /dev/null
++++ b/drivers/nvmem/lan9662-otpc.c
+@@ -0,0 +1,222 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <linux/iopoll.h>
++#include <linux/module.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++
++#define OTP_OTP_PWR_DN(t) (t + 0x00)
++#define OTP_OTP_PWR_DN_OTP_PWRDN_N BIT(0)
++#define OTP_OTP_ADDR_HI(t) (t + 0x04)
++#define OTP_OTP_ADDR_LO(t) (t + 0x08)
++#define OTP_OTP_PRGM_DATA(t) (t + 0x10)
++#define OTP_OTP_PRGM_MODE(t) (t + 0x14)
++#define OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE BIT(0)
++#define OTP_OTP_RD_DATA(t) (t + 0x18)
++#define OTP_OTP_FUNC_CMD(t) (t + 0x20)
++#define OTP_OTP_FUNC_CMD_OTP_PROGRAM BIT(1)
++#define OTP_OTP_FUNC_CMD_OTP_READ BIT(0)
++#define OTP_OTP_CMD_GO(t) (t + 0x28)
++#define OTP_OTP_CMD_GO_OTP_GO BIT(0)
++#define OTP_OTP_PASS_FAIL(t) (t + 0x2c)
++#define OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED BIT(3)
++#define OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED BIT(2)
++#define OTP_OTP_PASS_FAIL_OTP_FAIL BIT(0)
++#define OTP_OTP_STATUS(t) (t + 0x30)
++#define OTP_OTP_STATUS_OTP_CPUMPEN BIT(1)
++#define OTP_OTP_STATUS_OTP_BUSY BIT(0)
++
++#define OTP_MEM_SIZE 8192
++#define OTP_SLEEP_US 10
++#define OTP_TIMEOUT_US 500000
++
++struct lan9662_otp {
++ struct device *dev;
++ void __iomem *base;
++};
++
++static bool lan9662_otp_wait_flag_clear(void __iomem *reg, u32 flag)
++{
++ u32 val;
++
++ return readl_poll_timeout(reg, val, !(val & flag),
++ OTP_SLEEP_US, OTP_TIMEOUT_US);
++}
++
++static int lan9662_otp_power(struct lan9662_otp *otp, bool up)
++{
++ void __iomem *pwrdn = OTP_OTP_PWR_DN(otp->base);
++
++ if (up) {
++ writel(readl(pwrdn) & ~OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn);
++ if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base),
++ OTP_OTP_STATUS_OTP_CPUMPEN))
++ return -ETIMEDOUT;
++ } else {
++ writel(readl(pwrdn) | OTP_OTP_PWR_DN_OTP_PWRDN_N, pwrdn);
++ }
++
++ return 0;
++}
++
++static int lan9662_otp_execute(struct lan9662_otp *otp)
++{
++ if (lan9662_otp_wait_flag_clear(OTP_OTP_CMD_GO(otp->base),
++ OTP_OTP_CMD_GO_OTP_GO))
++ return -ETIMEDOUT;
++
++ if (lan9662_otp_wait_flag_clear(OTP_OTP_STATUS(otp->base),
++ OTP_OTP_STATUS_OTP_BUSY))
++ return -ETIMEDOUT;
++
++ return 0;
++}
++
++static void lan9662_otp_set_address(struct lan9662_otp *otp, u32 offset)
++{
++ writel(0xff & (offset >> 8), OTP_OTP_ADDR_HI(otp->base));
++ writel(0xff & offset, OTP_OTP_ADDR_LO(otp->base));
++}
++
++static int lan9662_otp_read_byte(struct lan9662_otp *otp, u32 offset, u8 *dst)
++{
++ u32 pass;
++ int rc;
++
++ lan9662_otp_set_address(otp, offset);
++ writel(OTP_OTP_FUNC_CMD_OTP_READ, OTP_OTP_FUNC_CMD(otp->base));
++ writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base));
++ rc = lan9662_otp_execute(otp);
++ if (!rc) {
++ pass = readl(OTP_OTP_PASS_FAIL(otp->base));
++ if (pass & OTP_OTP_PASS_FAIL_OTP_READ_PROHIBITED)
++ return -EACCES;
++ *dst = (u8) readl(OTP_OTP_RD_DATA(otp->base));
++ }
++ return rc;
++}
++
++static int lan9662_otp_write_byte(struct lan9662_otp *otp, u32 offset, u8 data)
++{
++ u32 pass;
++ int rc;
++
++ lan9662_otp_set_address(otp, offset);
++ writel(OTP_OTP_PRGM_MODE_OTP_PGM_MODE_BYTE, OTP_OTP_PRGM_MODE(otp->base));
++ writel(data, OTP_OTP_PRGM_DATA(otp->base));
++ writel(OTP_OTP_FUNC_CMD_OTP_PROGRAM, OTP_OTP_FUNC_CMD(otp->base));
++ writel(OTP_OTP_CMD_GO_OTP_GO, OTP_OTP_CMD_GO(otp->base));
++
++ rc = lan9662_otp_execute(otp);
++ if (!rc) {
++ pass = readl(OTP_OTP_PASS_FAIL(otp->base));
++ if (pass & OTP_OTP_PASS_FAIL_OTP_WRITE_PROHIBITED)
++ return -EACCES;
++ if (pass & OTP_OTP_PASS_FAIL_OTP_FAIL)
++ return -EIO;
++ }
++ return rc;
++}
++
++static int lan9662_otp_read(void *context, unsigned int offset,
++ void *_val, size_t bytes)
++{
++ struct lan9662_otp *otp = context;
++ u8 *val = _val;
++ uint8_t data;
++ int i, rc = 0;
++
++ lan9662_otp_power(otp, true);
++ for (i = 0; i < bytes; i++) {
++ rc = lan9662_otp_read_byte(otp, offset + i, &data);
++ if (rc < 0)
++ break;
++ *val++ = data;
++ }
++ lan9662_otp_power(otp, false);
++
++ return rc;
++}
++
++static int lan9662_otp_write(void *context, unsigned int offset,
++ void *_val, size_t bytes)
++{
++ struct lan9662_otp *otp = context;
++ u8 *val = _val;
++ u8 data, newdata;
++ int i, rc = 0;
++
++ lan9662_otp_power(otp, true);
++ for (i = 0; i < bytes; i++) {
++ /* Skip zero bytes */
++ if (val[i]) {
++ rc = lan9662_otp_read_byte(otp, offset + i, &data);
++ if (rc < 0)
++ break;
++
++ newdata = data | val[i];
++ if (newdata == data)
++ continue;
++
++ rc = lan9662_otp_write_byte(otp, offset + i,
++ newdata);
++ if (rc < 0)
++ break;
++ }
++ }
++ lan9662_otp_power(otp, false);
++
++ return rc;
++}
++
++static struct nvmem_config otp_config = {
++ .name = "lan9662-otp",
++ .stride = 1,
++ .word_size = 1,
++ .reg_read = lan9662_otp_read,
++ .reg_write = lan9662_otp_write,
++ .size = OTP_MEM_SIZE,
++};
++
++static int lan9662_otp_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct nvmem_device *nvmem;
++ struct lan9662_otp *otp;
++
++ otp = devm_kzalloc(&pdev->dev, sizeof(*otp), GFP_KERNEL);
++ if (!otp)
++ return -ENOMEM;
++
++ otp->dev = dev;
++ otp->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(otp->base))
++ return PTR_ERR(otp->base);
++
++ otp_config.priv = otp;
++ otp_config.dev = dev;
++
++ nvmem = devm_nvmem_register(dev, &otp_config);
++
++ return PTR_ERR_OR_ZERO(nvmem);
++}
++
++static const struct of_device_id lan9662_otp_match[] = {
++ { .compatible = "microchip,lan9662-otp", },
++ { },
++};
++MODULE_DEVICE_TABLE(of, lan9662_otp_match);
++
++static struct platform_driver lan9662_otp_driver = {
++ .probe = lan9662_otp_probe,
++ .driver = {
++ .name = "lan9662-otp",
++ .of_match_table = lan9662_otp_match,
++ },
++};
++module_platform_driver(lan9662_otp_driver);
++
++MODULE_AUTHOR("Horatiu Vultur <horatiu.vultur@microchip.com>");
++MODULE_DESCRIPTION("lan9662 OTP driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0009-nvmem-u-boot-env-fix-crc32-casting-type.patch b/target/linux/generic/backport-6.1/807-v6.1-0009-nvmem-u-boot-env-fix-crc32-casting-type.patch
new file mode 100644
index 0000000000..633a668a96
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0009-nvmem-u-boot-env-fix-crc32-casting-type.patch
@@ -0,0 +1,32 @@
+From 3717ca3e0cc8683f93b41d3f06ca79631eb58715 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 16 Sep 2022 13:21:00 +0100
+Subject: [PATCH] nvmem: u-boot-env: fix crc32 casting type
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This fixes:
+drivers/nvmem/u-boot-env.c:141:17: sparse: sparse: cast to restricted __le32
+
+Fixes: d5542923f200 ("nvmem: add driver handling U-Boot environment variables")
+Reported-by: kernel test robot <lkp@intel.com>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220916122100.170016-14-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/u-boot-env.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -139,7 +139,7 @@ static int u_boot_env_parse(struct u_boo
+ data_offset = offsetof(struct u_boot_env_image_redundant, data);
+ break;
+ }
+- crc32 = le32_to_cpu(*(uint32_t *)(buf + crc32_offset));
++ crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset));
+ crc32_data_len = priv->mtd->size - crc32_data_offset;
+ data_len = priv->mtd->size - data_offset;
+
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0010-nvmem-lan9662-otp-Fix-compatible-string.patch b/target/linux/generic/backport-6.1/807-v6.1-0010-nvmem-lan9662-otp-Fix-compatible-string.patch
new file mode 100644
index 0000000000..b663a1328d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0010-nvmem-lan9662-otp-Fix-compatible-string.patch
@@ -0,0 +1,34 @@
+From 1aeb122d214b92474c86fde00a03d6e2d69381b5 Mon Sep 17 00:00:00 2001
+From: Horatiu Vultur <horatiu.vultur@microchip.com>
+Date: Wed, 28 Sep 2022 21:51:12 +0200
+Subject: [PATCH] nvmem: lan9662-otp: Fix compatible string
+
+The device tree bindings for lan9662-otp expects the compatible string
+to be one of following compatible strings:
+microchip,lan9662-otpc
+microchip,lan9668-otpc
+
+The problem is that the lan9662-otp driver contains the
+microchip,lan9662-otp compatible string instead of
+microchip,lan9662-otpc.
+Fix this by updating the compatible string in the driver.
+
+Fixes: 9e8f208ad5229d ("nvmem: lan9662-otp: add support")
+Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
+Link: https://lore.kernel.org/r/20220928195112.630351-1-horatiu.vultur@microchip.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/lan9662-otpc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/lan9662-otpc.c
++++ b/drivers/nvmem/lan9662-otpc.c
+@@ -203,7 +203,7 @@ static int lan9662_otp_probe(struct plat
+ }
+
+ static const struct of_device_id lan9662_otp_match[] = {
+- { .compatible = "microchip,lan9662-otp", },
++ { .compatible = "microchip,lan9662-otpc", },
+ { },
+ };
+ MODULE_DEVICE_TABLE(of, lan9662_otp_match);
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0011-nvmem-u-boot-env-fix-crc32_data_offset-on-redundant-.patch b/target/linux/generic/backport-6.1/807-v6.1-0011-nvmem-u-boot-env-fix-crc32_data_offset-on-redundant-.patch
new file mode 100644
index 0000000000..967e891dbd
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0011-nvmem-u-boot-env-fix-crc32_data_offset-on-redundant-.patch
@@ -0,0 +1,59 @@
+From ee424f7d3960152f5f862bbb6943e59828dc7917 Mon Sep 17 00:00:00 2001
+From: Christian Lamparter <chunkeey@gmail.com>
+Date: Fri, 4 Nov 2022 17:52:03 +0100
+Subject: [PATCH] nvmem: u-boot-env: fix crc32_data_offset on redundant
+ u-boot-env
+
+The Western Digital MyBook Live (PowerPC 464/APM82181)
+has a set of redundant u-boot-env. Loading up the driver
+the following error:
+
+| u_boot_env: Invalid calculated CRC32: 0x4f8f2c86 (expected: 0x98b14514)
+| u_boot_env: probe of partition@1e000 failed with error -22
+
+Looking up the userspace libubootenv utilities source [0],
+it looks like the "mark" or "flag" is not part of the
+crc32 sum... which is unfortunate :(
+
+|static int libuboot_load(struct uboot_ctx *ctx)
+|{
+|[...]
+| if (ctx->redundant) {
+| [...]
+| offsetdata = offsetof(struct uboot_env_redund, data);
+| [...] //-----^^
+| }
+| usable_envsize = ctx->size - offsetdata;
+| buf[0] = malloc(bufsize);
+|[...]
+| for (i = 0; i < copies; i++) {
+| data = (uint8_t *)(buf[i] + offsetdata);
+| uint32_t crc;
+|
+| ret = devread(ctx, i, buf[i]);
+| [...]
+| crc = *(uint32_t *)(buf[i] + offsetcrc);
+| dev->crc = crc32(0, (uint8_t *)data, usable_envsize);
+|
+
+[0] https://github.com/sbabic/libubootenv/blob/master/src/uboot_env.c#L951
+
+Fixes: d5542923f200 ("nvmem: add driver handling U-Boot environment variables")
+Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
+Link: https://lore.kernel.org/r/70a16eae113e08db2390b76e174f4837caa135c3.1667580636.git.chunkeey@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/u-boot-env.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -135,7 +135,7 @@ static int u_boot_env_parse(struct u_boo
+ break;
+ case U_BOOT_FORMAT_REDUNDANT:
+ crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_redundant, mark);
++ crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
+ data_offset = offsetof(struct u_boot_env_image_redundant, data);
+ break;
+ }
diff --git a/target/linux/generic/backport-6.1/807-v6.1-0013-nvmem-lan9662-otp-Change-return-type-of-lan9662_otp_.patch b/target/linux/generic/backport-6.1/807-v6.1-0013-nvmem-lan9662-otp-Change-return-type-of-lan9662_otp_.patch
new file mode 100644
index 0000000000..0c842f0793
--- /dev/null
+++ b/target/linux/generic/backport-6.1/807-v6.1-0013-nvmem-lan9662-otp-Change-return-type-of-lan9662_otp_.patch
@@ -0,0 +1,35 @@
+From 022b68f271de0e53024e6d5e96fee8e76d25eb95 Mon Sep 17 00:00:00 2001
+From: Horatiu Vultur <horatiu.vultur@microchip.com>
+Date: Fri, 18 Nov 2022 06:38:40 +0000
+Subject: [PATCH] nvmem: lan9662-otp: Change return type of
+ lan9662_otp_wait_flag_clear()
+
+The blamed commit introduced the following smatch warning in the
+function lan9662_otp_wait_flag_clear:
+drivers/nvmem/lan9662-otpc.c:43 lan9662_otp_wait_flag_clear() warn: signedness bug returning '(-110)'
+
+Fix this by changing the return type of the function
+lan9662_otp_wait_flag_clear() to be int instead of bool.
+
+Fixes: 9e8f208ad5229d ("nvmem: lan9662-otp: add support")
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20221118063840.6357-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/lan9662-otpc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/lan9662-otpc.c
++++ b/drivers/nvmem/lan9662-otpc.c
+@@ -36,7 +36,7 @@ struct lan9662_otp {
+ void __iomem *base;
+ };
+
+-static bool lan9662_otp_wait_flag_clear(void __iomem *reg, u32 flag)
++static int lan9662_otp_wait_flag_clear(void __iomem *reg, u32 flag)
+ {
+ u32 val;
+
diff --git a/target/linux/generic/backport-6.1/808-v6.2-0001-nvmem-stm32-move-STM32MP15_BSEC_NUM_LOWER-in-config.patch b/target/linux/generic/backport-6.1/808-v6.2-0001-nvmem-stm32-move-STM32MP15_BSEC_NUM_LOWER-in-config.patch
new file mode 100644
index 0000000000..33759632eb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/808-v6.2-0001-nvmem-stm32-move-STM32MP15_BSEC_NUM_LOWER-in-config.patch
@@ -0,0 +1,82 @@
+From fbfc4ca465a1f8d81bf2d67d95bf7fc67c3cf0c2 Mon Sep 17 00:00:00 2001
+From: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Date: Fri, 18 Nov 2022 06:39:20 +0000
+Subject: [PATCH] nvmem: stm32: move STM32MP15_BSEC_NUM_LOWER in config
+
+Support STM32MP15_BSEC_NUM_LOWER in stm32 romem config to prepare
+the next SoC in STM32MP family.
+
+Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20221118063932.6418-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/stm32-romem.c | 21 ++++++++++++++++-----
+ 1 file changed, 16 insertions(+), 5 deletions(-)
+
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -22,16 +22,15 @@
+ /* shadow registers offest */
+ #define STM32MP15_BSEC_DATA0 0x200
+
+-/* 32 (x 32-bits) lower shadow registers */
+-#define STM32MP15_BSEC_NUM_LOWER 32
+-
+ struct stm32_romem_cfg {
+ int size;
++ u8 lower;
+ };
+
+ struct stm32_romem_priv {
+ void __iomem *base;
+ struct nvmem_config cfg;
++ u8 lower;
+ };
+
+ static int stm32_romem_read(void *context, unsigned int offset, void *buf,
+@@ -85,7 +84,7 @@ static int stm32_bsec_read(void *context
+ for (i = roffset; (i < roffset + rbytes); i += 4) {
+ u32 otp = i >> 2;
+
+- if (otp < STM32MP15_BSEC_NUM_LOWER) {
++ if (otp < priv->lower) {
+ /* read lower data from shadow registers */
+ val = readl_relaxed(
+ priv->base + STM32MP15_BSEC_DATA0 + i);
+@@ -159,6 +158,8 @@ static int stm32_romem_probe(struct plat
+ priv->cfg.priv = priv;
+ priv->cfg.owner = THIS_MODULE;
+
++ priv->lower = 0;
++
+ cfg = (const struct stm32_romem_cfg *)
+ of_match_device(dev->driver->of_match_table, dev)->data;
+ if (!cfg) {
+@@ -167,6 +168,7 @@ static int stm32_romem_probe(struct plat
+ priv->cfg.reg_read = stm32_romem_read;
+ } else {
+ priv->cfg.size = cfg->size;
++ priv->lower = cfg->lower;
+ priv->cfg.reg_read = stm32_bsec_read;
+ priv->cfg.reg_write = stm32_bsec_write;
+ }
+@@ -174,8 +176,17 @@ static int stm32_romem_probe(struct plat
+ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg));
+ }
+
++/*
++ * STM32MP15 BSEC OTP regions: 4096 OTP bits (with 3072 effective bits)
++ * => 96 x 32-bits data words
++ * - Lower: 1K bits, 2:1 redundancy, incremental bit programming
++ * => 32 (x 32-bits) lower shadow registers = words 0 to 31
++ * - Upper: 2K bits, ECC protection, word programming only
++ * => 64 (x 32-bits) = words 32 to 95
++ */
+ static const struct stm32_romem_cfg stm32mp15_bsec_cfg = {
+- .size = 384, /* 96 x 32-bits data words */
++ .size = 384,
++ .lower = 32,
+ };
+
+ static const struct of_device_id stm32_romem_of_match[] = {
diff --git a/target/linux/generic/backport-6.1/808-v6.2-0002-nvmem-stm32-add-warning-when-upper-OTPs-are-updated.patch b/target/linux/generic/backport-6.1/808-v6.2-0002-nvmem-stm32-add-warning-when-upper-OTPs-are-updated.patch
new file mode 100644
index 0000000000..5791df2606
--- /dev/null
+++ b/target/linux/generic/backport-6.1/808-v6.2-0002-nvmem-stm32-add-warning-when-upper-OTPs-are-updated.patch
@@ -0,0 +1,34 @@
+From d61784e6410f3df2028e6eb91b06ffed37a660e0 Mon Sep 17 00:00:00 2001
+From: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Date: Fri, 18 Nov 2022 06:39:21 +0000
+Subject: [PATCH] nvmem: stm32: add warning when upper OTPs are updated
+
+As the upper OTPs are ECC protected, they support only one 32 bits word
+programming.
+For a second modification of this word, these ECC become invalid and
+this OTP will be no more accessible, the shadowed value is invalid.
+
+This patch adds a warning to indicate an upper OTP update, because this
+operation is dangerous as OTP is not locked by the driver after the first
+update to avoid a second update.
+
+Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20221118063932.6418-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/stm32-romem.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -132,6 +132,9 @@ static int stm32_bsec_write(void *contex
+ }
+ }
+
++ if (offset + bytes >= priv->lower * 4)
++ dev_warn(dev, "Update of upper OTPs with ECC protection (word programming, only once)\n");
++
+ return 0;
+ }
+
diff --git a/target/linux/generic/backport-6.1/808-v6.2-0003-nvmem-stm32-add-nvmem-type-attribute.patch b/target/linux/generic/backport-6.1/808-v6.2-0003-nvmem-stm32-add-nvmem-type-attribute.patch
new file mode 100644
index 0000000000..b83ad69c6b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/808-v6.2-0003-nvmem-stm32-add-nvmem-type-attribute.patch
@@ -0,0 +1,26 @@
+From a3816a7d7c097c1da46aad5f5d1e229b607dce04 Mon Sep 17 00:00:00 2001
+From: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Date: Fri, 18 Nov 2022 06:39:22 +0000
+Subject: [PATCH] nvmem: stm32: add nvmem type attribute
+
+Inform NVMEM framework of type attribute for stm32-romem as NVMEM_TYPE_OTP
+so userspace is able to know how the data is stored in BSEC.
+
+Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20221118063932.6418-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/stm32-romem.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -160,6 +160,7 @@ static int stm32_romem_probe(struct plat
+ priv->cfg.dev = dev;
+ priv->cfg.priv = priv;
+ priv->cfg.owner = THIS_MODULE;
++ priv->cfg.type = NVMEM_TYPE_OTP;
+
+ priv->lower = 0;
+
diff --git a/target/linux/generic/backport-6.1/808-v6.2-0004-nvmem-stm32-fix-spelling-typo-in-comment.patch b/target/linux/generic/backport-6.1/808-v6.2-0004-nvmem-stm32-fix-spelling-typo-in-comment.patch
new file mode 100644
index 0000000000..52ba1e65b5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/808-v6.2-0004-nvmem-stm32-fix-spelling-typo-in-comment.patch
@@ -0,0 +1,27 @@
+From 06aac0e11960a7ddccc1888326b5906d017e0f24 Mon Sep 17 00:00:00 2001
+From: Jiangshan Yi <yijiangshan@kylinos.cn>
+Date: Fri, 18 Nov 2022 06:39:24 +0000
+Subject: [PATCH] nvmem: stm32: fix spelling typo in comment
+
+Fix spelling typo in comment.
+
+Reported-by: k2ci <kernel-bot@kylinos.cn>
+Signed-off-by: Jiangshan Yi <yijiangshan@kylinos.cn>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20221118063932.6418-6-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/stm32-romem.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -19,7 +19,7 @@
+ #define STM32_SMC_WRITE_SHADOW 0x03
+ #define STM32_SMC_READ_OTP 0x04
+
+-/* shadow registers offest */
++/* shadow registers offset */
+ #define STM32MP15_BSEC_DATA0 0x200
+
+ struct stm32_romem_cfg {
diff --git a/target/linux/generic/backport-6.1/808-v6.2-0005-nvmem-Kconfig-Fix-spelling-mistake-controlls-control.patch b/target/linux/generic/backport-6.1/808-v6.2-0005-nvmem-Kconfig-Fix-spelling-mistake-controlls-control.patch
new file mode 100644
index 0000000000..8f024f4c1a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/808-v6.2-0005-nvmem-Kconfig-Fix-spelling-mistake-controlls-control.patch
@@ -0,0 +1,27 @@
+From fb817c4ef63e8cfb6e77ae4a2875ae854c80708f Mon Sep 17 00:00:00 2001
+From: Colin Ian King <colin.i.king@gmail.com>
+Date: Fri, 18 Nov 2022 06:39:26 +0000
+Subject: [PATCH] nvmem: Kconfig: Fix spelling mistake "controlls" ->
+ "controls"
+
+There is a spelling mistake in a Kconfig description. Fix it.
+
+Signed-off-by: Colin Ian King <colin.i.king@gmail.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20221118063932.6418-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -164,7 +164,7 @@ config NVMEM_MICROCHIP_OTPC
+ depends on ARCH_AT91 || COMPILE_TEST
+ help
+ This driver enable the OTP controller available on Microchip SAMA7G5
+- SoCs. It controlls the access to the OTP memory connected to it.
++ SoCs. It controls the access to the OTP memory connected to it.
+
+ config NVMEM_MTK_EFUSE
+ tristate "Mediatek SoCs EFUSE support"
diff --git a/target/linux/generic/backport-6.1/808-v6.2-0006-nvmem-u-boot-env-add-Broadcom-format-support.patch b/target/linux/generic/backport-6.1/808-v6.2-0006-nvmem-u-boot-env-add-Broadcom-format-support.patch
new file mode 100644
index 0000000000..861386ad31
--- /dev/null
+++ b/target/linux/generic/backport-6.1/808-v6.2-0006-nvmem-u-boot-env-add-Broadcom-format-support.patch
@@ -0,0 +1,67 @@
+From ada84d07af6097b2addd18262668ce6cb9e15206 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 18 Nov 2022 06:39:27 +0000
+Subject: [PATCH] nvmem: u-boot-env: add Broadcom format support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Broadcom uses U-Boot for a lot of their bcmbca familiy chipsets. They
+decided to store U-Boot environment data inside U-Boot partition and to
+use a custom header (with "uEnv" magic and env data length).
+
+Add support for Broadcom's specific binding and their custom format.
+
+Ref: 6b0584c19d87 ("dt-bindings: nvmem: u-boot,env: add Broadcom's variant binding")
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20221118063932.6418-9-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/u-boot-env.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -16,6 +16,7 @@
+ enum u_boot_env_format {
+ U_BOOT_FORMAT_SINGLE,
+ U_BOOT_FORMAT_REDUNDANT,
++ U_BOOT_FORMAT_BROADCOM,
+ };
+
+ struct u_boot_env {
+@@ -40,6 +41,13 @@ struct u_boot_env_image_redundant {
+ uint8_t data[];
+ } __packed;
+
++struct u_boot_env_image_broadcom {
++ __le32 magic;
++ __le32 len;
++ __le32 crc32;
++ uint8_t data[0];
++} __packed;
++
+ static int u_boot_env_read(void *context, unsigned int offset, void *val,
+ size_t bytes)
+ {
+@@ -138,6 +146,11 @@ static int u_boot_env_parse(struct u_boo
+ crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
+ data_offset = offsetof(struct u_boot_env_image_redundant, data);
+ break;
++ case U_BOOT_FORMAT_BROADCOM:
++ crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++ data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++ break;
+ }
+ crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset));
+ crc32_data_len = priv->mtd->size - crc32_data_offset;
+@@ -202,6 +215,7 @@ static const struct of_device_id u_boot_
+ { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, },
+ { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
+ { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++ { .compatible = "brcm,env", .data = (void *)U_BOOT_FORMAT_BROADCOM, },
+ {},
+ };
+
diff --git a/target/linux/generic/backport-6.1/808-v6.2-0007-nvmem-brcm_nvram-Add-check-for-kzalloc.patch b/target/linux/generic/backport-6.1/808-v6.2-0007-nvmem-brcm_nvram-Add-check-for-kzalloc.patch
new file mode 100644
index 0000000000..14108b5927
--- /dev/null
+++ b/target/linux/generic/backport-6.1/808-v6.2-0007-nvmem-brcm_nvram-Add-check-for-kzalloc.patch
@@ -0,0 +1,30 @@
+From b0576ade3aaf24b376ea1a4406ae138e2a22b0c0 Mon Sep 17 00:00:00 2001
+From: Jiasheng Jiang <jiasheng@iscas.ac.cn>
+Date: Fri, 27 Jan 2023 10:40:06 +0000
+Subject: [PATCH] nvmem: brcm_nvram: Add check for kzalloc
+
+Add the check for the return value of kzalloc in order to avoid
+NULL pointer dereference.
+
+Fixes: 6e977eaa8280 ("nvmem: brcm_nvram: parse NVRAM content into NVMEM cells")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jiasheng Jiang <jiasheng@iscas.ac.cn>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230127104015.23839-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/brcm_nvram.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/nvmem/brcm_nvram.c
++++ b/drivers/nvmem/brcm_nvram.c
+@@ -97,6 +97,9 @@ static int brcm_nvram_parse(struct brcm_
+ len = le32_to_cpu(header.len);
+
+ data = kzalloc(len, GFP_KERNEL);
++ if (!data)
++ return -ENOMEM;
++
+ memcpy_fromio(data, priv->base, len);
+ data[len - 1] = '\0';
+
diff --git a/target/linux/generic/backport-6.1/808-v6.2-0008-nvmem-sunxi_sid-Always-use-32-bit-MMIO-reads.patch b/target/linux/generic/backport-6.1/808-v6.2-0008-nvmem-sunxi_sid-Always-use-32-bit-MMIO-reads.patch
new file mode 100644
index 0000000000..632b01cb2a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/808-v6.2-0008-nvmem-sunxi_sid-Always-use-32-bit-MMIO-reads.patch
@@ -0,0 +1,55 @@
+From c151d5ed8e8fe0474bd61dce7f2076ca5916c683 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Fri, 27 Jan 2023 10:40:07 +0000
+Subject: [PATCH] nvmem: sunxi_sid: Always use 32-bit MMIO reads
+
+The SID SRAM on at least some SoCs (A64 and D1) returns different values
+when read with bus cycles narrower than 32 bits. This is not immediately
+obvious, because memcpy_fromio() uses word-size accesses as long as
+enough data is being copied.
+
+The vendor driver always uses 32-bit MMIO reads, so do the same here.
+This is faster than the register-based method, which is currently used
+as a workaround on A64. And it fixes the values returned on D1, where
+the SRAM method was being used.
+
+The special case for the last word is needed to maintain .word_size == 1
+for sysfs ABI compatibility, as noted previously in commit de2a3eaea552
+("nvmem: sunxi_sid: Optimize register read-out method").
+
+Fixes: 07ae4fde9efa ("nvmem: sunxi_sid: Add support for D1 variant")
+Cc: stable@vger.kernel.org
+Tested-by: Heiko Stuebner <heiko@sntech.de>
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230127104015.23839-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sunxi_sid.c | 15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/sunxi_sid.c
++++ b/drivers/nvmem/sunxi_sid.c
+@@ -41,8 +41,21 @@ static int sunxi_sid_read(void *context,
+ void *val, size_t bytes)
+ {
+ struct sunxi_sid *sid = context;
++ u32 word;
+
+- memcpy_fromio(val, sid->base + sid->value_offset + offset, bytes);
++ /* .stride = 4 so offset is guaranteed to be aligned */
++ __ioread32_copy(val, sid->base + sid->value_offset + offset, bytes / 4);
++
++ val += round_down(bytes, 4);
++ offset += round_down(bytes, 4);
++ bytes = bytes % 4;
++
++ if (!bytes)
++ return 0;
++
++ /* Handle any trailing bytes */
++ word = readl_relaxed(sid->base + sid->value_offset + offset);
++ memcpy(val, &word, bytes);
+
+ return 0;
+ }
diff --git a/target/linux/generic/backport-6.1/808-v6.2-0013-nvmem-core-fix-device-node-refcounting.patch b/target/linux/generic/backport-6.1/808-v6.2-0013-nvmem-core-fix-device-node-refcounting.patch
new file mode 100644
index 0000000000..a229c303ad
--- /dev/null
+++ b/target/linux/generic/backport-6.1/808-v6.2-0013-nvmem-core-fix-device-node-refcounting.patch
@@ -0,0 +1,48 @@
+From edcf2fb660526b5ed29f93bd17328a2b4835c8b2 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Fri, 27 Jan 2023 10:40:12 +0000
+Subject: [PATCH] nvmem: core: fix device node refcounting
+
+In of_nvmem_cell_get(), of_get_next_parent() is used on cell_np. This
+will decrement the refcount on cell_np, but cell_np is still used later
+in the code. Use of_get_parent() instead and of_node_put() in the
+appropriate places.
+
+Fixes: 69aba7948cbe ("nvmem: Add a simple NVMEM framework for consumers")
+Fixes: 7ae6478b304b ("nvmem: core: rework nvmem cell instance creation")
+Cc: stable@vger.kernel.org
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230127104015.23839-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -1237,16 +1237,21 @@ struct nvmem_cell *of_nvmem_cell_get(str
+ if (!cell_np)
+ return ERR_PTR(-ENOENT);
+
+- nvmem_np = of_get_next_parent(cell_np);
+- if (!nvmem_np)
++ nvmem_np = of_get_parent(cell_np);
++ if (!nvmem_np) {
++ of_node_put(cell_np);
+ return ERR_PTR(-EINVAL);
++ }
+
+ nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
+ of_node_put(nvmem_np);
+- if (IS_ERR(nvmem))
++ if (IS_ERR(nvmem)) {
++ of_node_put(cell_np);
+ return ERR_CAST(nvmem);
++ }
+
+ cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
++ of_node_put(cell_np);
+ if (!cell_entry) {
+ __nvmem_device_put(nvmem);
+ return ERR_PTR(-ENOENT);
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0001-nvmem-core-remove-spurious-white-space.patch b/target/linux/generic/backport-6.1/809-v6.3-0001-nvmem-core-remove-spurious-white-space.patch
new file mode 100644
index 0000000000..01400fd490
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0001-nvmem-core-remove-spurious-white-space.patch
@@ -0,0 +1,26 @@
+From 2e8dc541ae207349b51c65391be625ffe1f86e0c Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 6 Feb 2023 13:43:41 +0000
+Subject: [PATCH] nvmem: core: remove spurious white space
+
+Remove a spurious white space in for the ida_alloc() call.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -764,7 +764,7 @@ struct nvmem_device *nvmem_register(cons
+ if (!nvmem)
+ return ERR_PTR(-ENOMEM);
+
+- rval = ida_alloc(&nvmem_ida, GFP_KERNEL);
++ rval = ida_alloc(&nvmem_ida, GFP_KERNEL);
+ if (rval < 0) {
+ kfree(nvmem);
+ return ERR_PTR(rval);
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0002-nvmem-core-add-an-index-parameter-to-the-cell.patch b/target/linux/generic/backport-6.1/809-v6.3-0002-nvmem-core-add-an-index-parameter-to-the-cell.patch
new file mode 100644
index 0000000000..454d3bf0ed
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0002-nvmem-core-add-an-index-parameter-to-the-cell.patch
@@ -0,0 +1,180 @@
+From 5d8e6e6c10a3d37486d263b16ddc15991a7e4a88 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 6 Feb 2023 13:43:46 +0000
+Subject: [PATCH] nvmem: core: add an index parameter to the cell
+
+Sometimes a cell can represend multiple values. For example, a base
+ethernet address stored in the NVMEM can be expanded into multiple
+discreet ones by adding an offset.
+
+For this use case, introduce an index parameter which is then used to
+distiguish between values. This parameter will then be passed to the
+post process hook which can then use it to create different values
+during reading.
+
+At the moment, there is only support for the device tree path. You can
+add the index to the phandle, e.g.
+
+ &net {
+ nvmem-cells = <&base_mac_address 2>;
+ nvmem-cell-names = "mac-address";
+ };
+
+ &nvmem_provider {
+ base_mac_address: base-mac-address@0 {
+ #nvmem-cell-cells = <1>;
+ reg = <0 6>;
+ };
+ };
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-13-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 37 ++++++++++++++++++++++++----------
+ drivers/nvmem/imx-ocotp.c | 4 ++--
+ include/linux/nvmem-provider.h | 4 ++--
+ 3 files changed, 30 insertions(+), 15 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -60,6 +60,7 @@ struct nvmem_cell_entry {
+ struct nvmem_cell {
+ struct nvmem_cell_entry *entry;
+ const char *id;
++ int index;
+ };
+
+ static DEFINE_MUTEX(nvmem_mutex);
+@@ -1122,7 +1123,8 @@ struct nvmem_device *devm_nvmem_device_g
+ }
+ EXPORT_SYMBOL_GPL(devm_nvmem_device_get);
+
+-static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, const char *id)
++static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
++ const char *id, int index)
+ {
+ struct nvmem_cell *cell;
+ const char *name = NULL;
+@@ -1141,6 +1143,7 @@ static struct nvmem_cell *nvmem_create_c
+
+ cell->id = name;
+ cell->entry = entry;
++ cell->index = index;
+
+ return cell;
+ }
+@@ -1179,7 +1182,7 @@ nvmem_cell_get_from_lookup(struct device
+ __nvmem_device_put(nvmem);
+ cell = ERR_PTR(-ENOENT);
+ } else {
+- cell = nvmem_create_cell(cell_entry, con_id);
++ cell = nvmem_create_cell(cell_entry, con_id, 0);
+ if (IS_ERR(cell))
+ __nvmem_device_put(nvmem);
+ }
+@@ -1227,15 +1230,27 @@ struct nvmem_cell *of_nvmem_cell_get(str
+ struct nvmem_device *nvmem;
+ struct nvmem_cell_entry *cell_entry;
+ struct nvmem_cell *cell;
++ struct of_phandle_args cell_spec;
+ int index = 0;
++ int cell_index = 0;
++ int ret;
+
+ /* if cell name exists, find index to the name */
+ if (id)
+ index = of_property_match_string(np, "nvmem-cell-names", id);
+
+- cell_np = of_parse_phandle(np, "nvmem-cells", index);
+- if (!cell_np)
+- return ERR_PTR(-ENOENT);
++ ret = of_parse_phandle_with_optional_args(np, "nvmem-cells",
++ "#nvmem-cell-cells",
++ index, &cell_spec);
++ if (ret)
++ return ERR_PTR(ret);
++
++ if (cell_spec.args_count > 1)
++ return ERR_PTR(-EINVAL);
++
++ cell_np = cell_spec.np;
++ if (cell_spec.args_count)
++ cell_index = cell_spec.args[0];
+
+ nvmem_np = of_get_parent(cell_np);
+ if (!nvmem_np) {
+@@ -1257,7 +1272,7 @@ struct nvmem_cell *of_nvmem_cell_get(str
+ return ERR_PTR(-ENOENT);
+ }
+
+- cell = nvmem_create_cell(cell_entry, id);
++ cell = nvmem_create_cell(cell_entry, id, cell_index);
+ if (IS_ERR(cell))
+ __nvmem_device_put(nvmem);
+
+@@ -1410,8 +1425,8 @@ static void nvmem_shift_read_buffer_in_p
+ }
+
+ static int __nvmem_cell_read(struct nvmem_device *nvmem,
+- struct nvmem_cell_entry *cell,
+- void *buf, size_t *len, const char *id)
++ struct nvmem_cell_entry *cell,
++ void *buf, size_t *len, const char *id, int index)
+ {
+ int rc;
+
+@@ -1425,7 +1440,7 @@ static int __nvmem_cell_read(struct nvme
+ nvmem_shift_read_buffer_in_place(cell, buf);
+
+ if (nvmem->cell_post_process) {
+- rc = nvmem->cell_post_process(nvmem->priv, id,
++ rc = nvmem->cell_post_process(nvmem->priv, id, index,
+ cell->offset, buf, cell->bytes);
+ if (rc)
+ return rc;
+@@ -1460,7 +1475,7 @@ void *nvmem_cell_read(struct nvmem_cell
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+- rc = __nvmem_cell_read(nvmem, cell->entry, buf, len, cell->id);
++ rc = __nvmem_cell_read(nvmem, cell->entry, buf, len, cell->id, cell->index);
+ if (rc) {
+ kfree(buf);
+ return ERR_PTR(rc);
+@@ -1773,7 +1788,7 @@ ssize_t nvmem_device_cell_read(struct nv
+ if (rc)
+ return rc;
+
+- rc = __nvmem_cell_read(nvmem, &cell, buf, &len, NULL);
++ rc = __nvmem_cell_read(nvmem, &cell, buf, &len, NULL, 0);
+ if (rc)
+ return rc;
+
+--- a/drivers/nvmem/imx-ocotp.c
++++ b/drivers/nvmem/imx-ocotp.c
+@@ -222,8 +222,8 @@ read_end:
+ return ret;
+ }
+
+-static int imx_ocotp_cell_pp(void *context, const char *id, unsigned int offset,
+- void *data, size_t bytes)
++static int imx_ocotp_cell_pp(void *context, const char *id, int index,
++ unsigned int offset, void *data, size_t bytes)
+ {
+ struct ocotp_priv *priv = context;
+
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -20,8 +20,8 @@ typedef int (*nvmem_reg_read_t)(void *pr
+ typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset,
+ void *val, size_t bytes);
+ /* used for vendor specific post processing of cell data */
+-typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, unsigned int offset,
+- void *buf, size_t bytes);
++typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, int index,
++ unsigned int offset, void *buf, size_t bytes);
+
+ enum nvmem_type {
+ NVMEM_TYPE_UNKNOWN = 0,
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0003-nvmem-core-move-struct-nvmem_cell_info-to-nvmem-prov.patch b/target/linux/generic/backport-6.1/809-v6.3-0003-nvmem-core-move-struct-nvmem_cell_info-to-nvmem-prov.patch
new file mode 100644
index 0000000000..f3829b3e17
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0003-nvmem-core-move-struct-nvmem_cell_info-to-nvmem-prov.patch
@@ -0,0 +1,78 @@
+From fbd03d27776c6121a483921601418e3c8f0ff37e Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 6 Feb 2023 13:43:47 +0000
+Subject: [PATCH] nvmem: core: move struct nvmem_cell_info to nvmem-provider.h
+
+struct nvmem_cell_info is used to describe a cell. Thus this should
+really be in the nvmem-provider's header. There are two (unused) nvmem
+access methods which use the nvmem_cell_info to describe the cell to be
+accesses. One can argue, that they will create a cell before accessing,
+thus they are both a provider and a consumer.
+
+struct nvmem_cell_info will get used more and more by nvmem-providers,
+don't force them to also include the consumer header, although they are
+not.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-14-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/nvmem-consumer.h | 10 +---------
+ include/linux/nvmem-provider.h | 19 ++++++++++++++++++-
+ 2 files changed, 19 insertions(+), 10 deletions(-)
+
+--- a/include/linux/nvmem-consumer.h
++++ b/include/linux/nvmem-consumer.h
+@@ -18,15 +18,7 @@ struct device_node;
+ /* consumer cookie */
+ struct nvmem_cell;
+ struct nvmem_device;
+-
+-struct nvmem_cell_info {
+- const char *name;
+- unsigned int offset;
+- unsigned int bytes;
+- unsigned int bit_offset;
+- unsigned int nbits;
+- struct device_node *np;
+-};
++struct nvmem_cell_info;
+
+ /**
+ * struct nvmem_cell_lookup - cell lookup entry
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -14,7 +14,6 @@
+ #include <linux/gpio/consumer.h>
+
+ struct nvmem_device;
+-struct nvmem_cell_info;
+ typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset,
+ void *val, size_t bytes);
+ typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset,
+@@ -48,6 +47,24 @@ struct nvmem_keepout {
+ };
+
+ /**
++ * struct nvmem_cell_info - NVMEM cell description
++ * @name: Name.
++ * @offset: Offset within the NVMEM device.
++ * @bytes: Length of the cell.
++ * @bit_offset: Bit offset if cell is smaller than a byte.
++ * @nbits: Number of bits.
++ * @np: Optional device_node pointer.
++ */
++struct nvmem_cell_info {
++ const char *name;
++ unsigned int offset;
++ unsigned int bytes;
++ unsigned int bit_offset;
++ unsigned int nbits;
++ struct device_node *np;
++};
++
++/**
+ * struct nvmem_config - NVMEM device configuration
+ *
+ * @dev: Parent device.
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0004-nvmem-core-drop-the-removal-of-the-cells-in-nvmem_ad.patch b/target/linux/generic/backport-6.1/809-v6.3-0004-nvmem-core-drop-the-removal-of-the-cells-in-nvmem_ad.patch
new file mode 100644
index 0000000000..8f996eab34
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0004-nvmem-core-drop-the-removal-of-the-cells-in-nvmem_ad.patch
@@ -0,0 +1,65 @@
+From cc5bdd323dde6494623f3ffe3a5b887fa21cd375 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 6 Feb 2023 13:43:48 +0000
+Subject: [PATCH] nvmem: core: drop the removal of the cells in
+ nvmem_add_cells()
+
+If nvmem_add_cells() fails, the whole nvmem_register() will fail
+and the cells will then be removed anyway. This is a preparation
+to introduce a nvmem_add_one_cell() which can then be used by
+nvmem_add_cells().
+
+This is then the same to what nvmem_add_cells_from_table() and
+nvmem_add_cells_from_of() do.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-15-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 14 ++++----------
+ 1 file changed, 4 insertions(+), 10 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -515,7 +515,7 @@ static int nvmem_add_cells(struct nvmem_
+ int ncells)
+ {
+ struct nvmem_cell_entry **cells;
+- int i, rval;
++ int i, rval = 0;
+
+ cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL);
+ if (!cells)
+@@ -525,28 +525,22 @@ static int nvmem_add_cells(struct nvmem_
+ cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL);
+ if (!cells[i]) {
+ rval = -ENOMEM;
+- goto err;
++ goto out;
+ }
+
+ rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, &info[i], cells[i]);
+ if (rval) {
+ kfree(cells[i]);
+- goto err;
++ goto out;
+ }
+
+ nvmem_cell_entry_add(cells[i]);
+ }
+
++out:
+ /* remove tmp array */
+ kfree(cells);
+
+- return 0;
+-err:
+- while (i--)
+- nvmem_cell_entry_drop(cells[i]);
+-
+- kfree(cells);
+-
+ return rval;
+ }
+
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0005-nvmem-core-add-nvmem_add_one_cell.patch b/target/linux/generic/backport-6.1/809-v6.3-0005-nvmem-core-add-nvmem_add_one_cell.patch
new file mode 100644
index 0000000000..711ce229b2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0005-nvmem-core-add-nvmem_add_one_cell.patch
@@ -0,0 +1,122 @@
+From 2ded6830d376d5e7bf43d59f7f7fdf1a59abc676 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 6 Feb 2023 13:43:49 +0000
+Subject: [PATCH] nvmem: core: add nvmem_add_one_cell()
+
+Add a new function to add exactly one cell. This will be used by the
+nvmem layout drivers to add custom cells. In contrast to the
+nvmem_add_cells(), this has the advantage that we don't have to assemble
+a list of cells on runtime.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-16-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 59 ++++++++++++++++++++--------------
+ include/linux/nvmem-provider.h | 8 +++++
+ 2 files changed, 43 insertions(+), 24 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -502,6 +502,36 @@ static int nvmem_cell_info_to_nvmem_cell
+ }
+
+ /**
++ * nvmem_add_one_cell() - Add one cell information to an nvmem device
++ *
++ * @nvmem: nvmem device to add cells to.
++ * @info: nvmem cell info to add to the device
++ *
++ * Return: 0 or negative error code on failure.
++ */
++int nvmem_add_one_cell(struct nvmem_device *nvmem,
++ const struct nvmem_cell_info *info)
++{
++ struct nvmem_cell_entry *cell;
++ int rval;
++
++ cell = kzalloc(sizeof(*cell), GFP_KERNEL);
++ if (!cell)
++ return -ENOMEM;
++
++ rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, info, cell);
++ if (rval) {
++ kfree(cell);
++ return rval;
++ }
++
++ nvmem_cell_entry_add(cell);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(nvmem_add_one_cell);
++
++/**
+ * nvmem_add_cells() - Add cell information to an nvmem device
+ *
+ * @nvmem: nvmem device to add cells to.
+@@ -514,34 +544,15 @@ static int nvmem_add_cells(struct nvmem_
+ const struct nvmem_cell_info *info,
+ int ncells)
+ {
+- struct nvmem_cell_entry **cells;
+- int i, rval = 0;
+-
+- cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL);
+- if (!cells)
+- return -ENOMEM;
++ int i, rval;
+
+ for (i = 0; i < ncells; i++) {
+- cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL);
+- if (!cells[i]) {
+- rval = -ENOMEM;
+- goto out;
+- }
+-
+- rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, &info[i], cells[i]);
+- if (rval) {
+- kfree(cells[i]);
+- goto out;
+- }
+-
+- nvmem_cell_entry_add(cells[i]);
++ rval = nvmem_add_one_cell(nvmem, &info[i]);
++ if (rval)
++ return rval;
+ }
+
+-out:
+- /* remove tmp array */
+- kfree(cells);
+-
+- return rval;
++ return 0;
+ }
+
+ /**
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -153,6 +153,9 @@ struct nvmem_device *devm_nvmem_register
+ void nvmem_add_cell_table(struct nvmem_cell_table *table);
+ void nvmem_del_cell_table(struct nvmem_cell_table *table);
+
++int nvmem_add_one_cell(struct nvmem_device *nvmem,
++ const struct nvmem_cell_info *info);
++
+ #else
+
+ static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
+@@ -170,6 +173,11 @@ devm_nvmem_register(struct device *dev,
+
+ static inline void nvmem_add_cell_table(struct nvmem_cell_table *table) {}
+ static inline void nvmem_del_cell_table(struct nvmem_cell_table *table) {}
++static inline int nvmem_add_one_cell(struct nvmem_device *nvmem,
++ const struct nvmem_cell_info *info)
++{
++ return -EOPNOTSUPP;
++}
+
+ #endif /* CONFIG_NVMEM */
+ #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0006-nvmem-core-use-nvmem_add_one_cell-in-nvmem_add_cells.patch b/target/linux/generic/backport-6.1/809-v6.3-0006-nvmem-core-use-nvmem_add_one_cell-in-nvmem_add_cells.patch
new file mode 100644
index 0000000000..e1791e5c83
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0006-nvmem-core-use-nvmem_add_one_cell-in-nvmem_add_cells.patch
@@ -0,0 +1,93 @@
+From 50014d659617dc58780a5d31ceb76c82779a9d8b Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 6 Feb 2023 13:43:50 +0000
+Subject: [PATCH] nvmem: core: use nvmem_add_one_cell() in
+ nvmem_add_cells_from_of()
+
+Convert nvmem_add_cells_from_of() to use the new nvmem_add_one_cell().
+This will remove duplicate code and it will make it possible to add a
+hook to a nvmem layout in between, which can change fields before the
+cell is finally added.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-17-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 45 ++++++++++++++------------------------------
+ 1 file changed, 14 insertions(+), 31 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -688,15 +688,14 @@ static int nvmem_validate_keepouts(struc
+
+ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
+ {
+- struct device_node *parent, *child;
+ struct device *dev = &nvmem->dev;
+- struct nvmem_cell_entry *cell;
++ struct device_node *child;
+ const __be32 *addr;
+- int len;
++ int len, ret;
+
+- parent = dev->of_node;
++ for_each_child_of_node(dev->of_node, child) {
++ struct nvmem_cell_info info = {0};
+
+- for_each_child_of_node(parent, child) {
+ addr = of_get_property(child, "reg", &len);
+ if (!addr)
+ continue;
+@@ -706,40 +705,24 @@ static int nvmem_add_cells_from_of(struc
+ return -EINVAL;
+ }
+
+- cell = kzalloc(sizeof(*cell), GFP_KERNEL);
+- if (!cell) {
+- of_node_put(child);
+- return -ENOMEM;
+- }
+-
+- cell->nvmem = nvmem;
+- cell->offset = be32_to_cpup(addr++);
+- cell->bytes = be32_to_cpup(addr);
+- cell->name = kasprintf(GFP_KERNEL, "%pOFn", child);
++ info.offset = be32_to_cpup(addr++);
++ info.bytes = be32_to_cpup(addr);
++ info.name = kasprintf(GFP_KERNEL, "%pOFn", child);
+
+ addr = of_get_property(child, "bits", &len);
+ if (addr && len == (2 * sizeof(u32))) {
+- cell->bit_offset = be32_to_cpup(addr++);
+- cell->nbits = be32_to_cpup(addr);
++ info.bit_offset = be32_to_cpup(addr++);
++ info.nbits = be32_to_cpup(addr);
+ }
+
+- if (cell->nbits)
+- cell->bytes = DIV_ROUND_UP(
+- cell->nbits + cell->bit_offset,
+- BITS_PER_BYTE);
+-
+- if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
+- dev_err(dev, "cell %s unaligned to nvmem stride %d\n",
+- cell->name, nvmem->stride);
+- /* Cells already added will be freed later. */
+- kfree_const(cell->name);
+- kfree(cell);
++ info.np = of_node_get(child);
++
++ ret = nvmem_add_one_cell(nvmem, &info);
++ kfree(info.name);
++ if (ret) {
+ of_node_put(child);
+- return -EINVAL;
++ return ret;
+ }
+-
+- cell->np = of_node_get(child);
+- nvmem_cell_entry_add(cell);
+ }
+
+ return 0;
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0007-nvmem-stm32-add-OP-TEE-support-for-STM32MP13x.patch b/target/linux/generic/backport-6.1/809-v6.3-0007-nvmem-stm32-add-OP-TEE-support-for-STM32MP13x.patch
new file mode 100644
index 0000000000..172a78b76a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0007-nvmem-stm32-add-OP-TEE-support-for-STM32MP13x.patch
@@ -0,0 +1,562 @@
+From 6a0bc3522e746025e2d9a63ab2cb5d7062c2d39c Mon Sep 17 00:00:00 2001
+From: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Date: Mon, 6 Feb 2023 13:43:51 +0000
+Subject: [PATCH] nvmem: stm32: add OP-TEE support for STM32MP13x
+
+For boot with OP-TEE on STM32MP13, the communication with the secure
+world no more use STMicroelectronics SMC but communication with the
+STM32MP BSEC TA, for data access (read/write) or lock operation:
+- all the request are sent to OP-TEE trusted application,
+- for upper OTP with ECC protection and with word programming only
+ each OTP are permanently locked when programmed to avoid ECC error
+ on the second write operation
+
+Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Reviewed-by: Etienne Carriere <etienne.carriere@linaro.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-18-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 11 +
+ drivers/nvmem/Makefile | 1 +
+ drivers/nvmem/stm32-bsec-optee-ta.c | 298 ++++++++++++++++++++++++++++
+ drivers/nvmem/stm32-bsec-optee-ta.h | 80 ++++++++
+ drivers/nvmem/stm32-romem.c | 54 ++++-
+ 5 files changed, 441 insertions(+), 3 deletions(-)
+ create mode 100644 drivers/nvmem/stm32-bsec-optee-ta.c
+ create mode 100644 drivers/nvmem/stm32-bsec-optee-ta.h
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -290,9 +290,20 @@ config NVMEM_SPRD_EFUSE
+ This driver can also be built as a module. If so, the module
+ will be called nvmem-sprd-efuse.
+
++config NVMEM_STM32_BSEC_OPTEE_TA
++ bool "STM32MP BSEC OP-TEE TA support for nvmem-stm32-romem driver"
++ depends on OPTEE
++ help
++ Say y here to enable the accesses to STM32MP SoC OTPs by the OP-TEE
++ trusted application STM32MP BSEC.
++
++ This library is a used by stm32-romem driver or included in the module
++ called nvmem-stm32-romem.
++
+ config NVMEM_STM32_ROMEM
+ tristate "STMicroelectronics STM32 factory-programmed memory support"
+ depends on ARCH_STM32 || COMPILE_TEST
++ imply NVMEM_STM32_BSEC_OPTEE_TA
+ help
+ Say y here to enable read-only access for STMicroelectronics STM32
+ factory-programmed memory area.
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -61,6 +61,7 @@ obj-$(CONFIG_NVMEM_SPRD_EFUSE) += nvmem
+ nvmem_sprd_efuse-y := sprd-efuse.o
+ obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o
+ nvmem_stm32_romem-y := stm32-romem.o
++nvmem_stm32_romem-$(CONFIG_NVMEM_STM32_BSEC_OPTEE_TA) += stm32-bsec-optee-ta.o
+ obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvmem_sunplus_ocotp.o
+ nvmem_sunplus_ocotp-y := sunplus-ocotp.o
+ obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
+--- /dev/null
++++ b/drivers/nvmem/stm32-bsec-optee-ta.c
+@@ -0,0 +1,298 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver
++ *
++ * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
++ */
++
++#include <linux/tee_drv.h>
++
++#include "stm32-bsec-optee-ta.h"
++
++/*
++ * Read OTP memory
++ *
++ * [in] value[0].a OTP start offset in byte
++ * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock)
++ * [out] memref[1].buffer Output buffer to store read values
++ * [out] memref[1].size Size of OTP to be read
++ *
++ * Return codes:
++ * TEE_SUCCESS - Invoke command success
++ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
++ * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
++ */
++#define PTA_BSEC_READ_MEM 0x0
++
++/*
++ * Write OTP memory
++ *
++ * [in] value[0].a OTP start offset in byte
++ * [in] value[0].b Access type (0:shadow, 1:fuse, 2:lock)
++ * [in] memref[1].buffer Input buffer to read values
++ * [in] memref[1].size Size of OTP to be written
++ *
++ * Return codes:
++ * TEE_SUCCESS - Invoke command success
++ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
++ * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
++ */
++#define PTA_BSEC_WRITE_MEM 0x1
++
++/* value of PTA_BSEC access type = value[in] b */
++#define SHADOW_ACCESS 0
++#define FUSE_ACCESS 1
++#define LOCK_ACCESS 2
++
++/* Bitfield definition for LOCK status */
++#define LOCK_PERM BIT(30)
++
++/* OP-TEE STM32MP BSEC TA UUID */
++static const uuid_t stm32mp_bsec_ta_uuid =
++ UUID_INIT(0x94cf71ad, 0x80e6, 0x40b5,
++ 0xa7, 0xc6, 0x3d, 0xc5, 0x01, 0xeb, 0x28, 0x03);
++
++/*
++ * Check whether this driver supports the BSEC TA in the TEE instance
++ * represented by the params (ver/data) to this function.
++ */
++static int stm32_bsec_optee_ta_match(struct tee_ioctl_version_data *ver,
++ const void *data)
++{
++ /* Currently this driver only supports GP compliant, OP-TEE based TA */
++ if ((ver->impl_id == TEE_IMPL_ID_OPTEE) &&
++ (ver->gen_caps & TEE_GEN_CAP_GP))
++ return 1;
++ else
++ return 0;
++}
++
++/* Open a session to OP-TEE for STM32MP BSEC TA */
++static int stm32_bsec_ta_open_session(struct tee_context *ctx, u32 *id)
++{
++ struct tee_ioctl_open_session_arg sess_arg;
++ int rc;
++
++ memset(&sess_arg, 0, sizeof(sess_arg));
++ export_uuid(sess_arg.uuid, &stm32mp_bsec_ta_uuid);
++ sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
++ sess_arg.num_params = 0;
++
++ rc = tee_client_open_session(ctx, &sess_arg, NULL);
++ if ((rc < 0) || (sess_arg.ret != 0)) {
++ pr_err("%s: tee_client_open_session failed err:%#x, ret:%#x\n",
++ __func__, sess_arg.ret, rc);
++ if (!rc)
++ rc = -EINVAL;
++ } else {
++ *id = sess_arg.session;
++ }
++
++ return rc;
++}
++
++/* close a session to OP-TEE for STM32MP BSEC TA */
++static void stm32_bsec_ta_close_session(void *ctx, u32 id)
++{
++ tee_client_close_session(ctx, id);
++}
++
++/* stm32_bsec_optee_ta_open() - initialize the STM32MP BSEC TA */
++int stm32_bsec_optee_ta_open(struct tee_context **ctx)
++{
++ struct tee_context *tee_ctx;
++ u32 session_id;
++ int rc;
++
++ /* Open context with TEE driver */
++ tee_ctx = tee_client_open_context(NULL, stm32_bsec_optee_ta_match, NULL, NULL);
++ if (IS_ERR(tee_ctx)) {
++ rc = PTR_ERR(tee_ctx);
++ if (rc == -ENOENT)
++ return -EPROBE_DEFER;
++ pr_err("%s: tee_client_open_context failed (%d)\n", __func__, rc);
++
++ return rc;
++ }
++
++ /* Check STM32MP BSEC TA presence */
++ rc = stm32_bsec_ta_open_session(tee_ctx, &session_id);
++ if (rc) {
++ tee_client_close_context(tee_ctx);
++ return rc;
++ }
++
++ stm32_bsec_ta_close_session(tee_ctx, session_id);
++
++ *ctx = tee_ctx;
++
++ return 0;
++}
++
++/* stm32_bsec_optee_ta_open() - release the PTA STM32MP BSEC TA */
++void stm32_bsec_optee_ta_close(void *ctx)
++{
++ tee_client_close_context(ctx);
++}
++
++/* stm32_bsec_optee_ta_read() - nvmem read access using PTA client driver */
++int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
++ void *buf, size_t bytes)
++{
++ struct tee_shm *shm;
++ struct tee_ioctl_invoke_arg arg;
++ struct tee_param param[2];
++ u8 *shm_buf;
++ u32 start, num_bytes;
++ int ret;
++ u32 session_id;
++
++ ret = stm32_bsec_ta_open_session(ctx, &session_id);
++ if (ret)
++ return ret;
++
++ memset(&arg, 0, sizeof(arg));
++ memset(&param, 0, sizeof(param));
++
++ arg.func = PTA_BSEC_READ_MEM;
++ arg.session = session_id;
++ arg.num_params = 2;
++
++ /* align access on 32bits */
++ start = ALIGN_DOWN(offset, 4);
++ num_bytes = round_up(offset + bytes - start, 4);
++ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
++ param[0].u.value.a = start;
++ param[0].u.value.b = SHADOW_ACCESS;
++
++ shm = tee_shm_alloc_kernel_buf(ctx, num_bytes);
++ if (IS_ERR(shm)) {
++ ret = PTR_ERR(shm);
++ goto out_tee_session;
++ }
++
++ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
++ param[1].u.memref.shm = shm;
++ param[1].u.memref.size = num_bytes;
++
++ ret = tee_client_invoke_func(ctx, &arg, param);
++ if (ret < 0 || arg.ret != 0) {
++ pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n",
++ arg.ret, ret);
++ if (!ret)
++ ret = -EIO;
++ }
++ if (!ret) {
++ shm_buf = tee_shm_get_va(shm, 0);
++ if (IS_ERR(shm_buf)) {
++ ret = PTR_ERR(shm_buf);
++ pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
++ } else {
++ /* read data from 32 bits aligned buffer */
++ memcpy(buf, &shm_buf[offset % 4], bytes);
++ }
++ }
++
++ tee_shm_free(shm);
++
++out_tee_session:
++ stm32_bsec_ta_close_session(ctx, session_id);
++
++ return ret;
++}
++
++/* stm32_bsec_optee_ta_write() - nvmem write access using PTA client driver */
++int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
++ unsigned int offset, void *buf, size_t bytes)
++{ struct tee_shm *shm;
++ struct tee_ioctl_invoke_arg arg;
++ struct tee_param param[2];
++ u8 *shm_buf;
++ int ret;
++ u32 session_id;
++
++ ret = stm32_bsec_ta_open_session(ctx, &session_id);
++ if (ret)
++ return ret;
++
++ /* Allow only writing complete 32-bits aligned words */
++ if ((bytes % 4) || (offset % 4))
++ return -EINVAL;
++
++ memset(&arg, 0, sizeof(arg));
++ memset(&param, 0, sizeof(param));
++
++ arg.func = PTA_BSEC_WRITE_MEM;
++ arg.session = session_id;
++ arg.num_params = 2;
++
++ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
++ param[0].u.value.a = offset;
++ param[0].u.value.b = FUSE_ACCESS;
++
++ shm = tee_shm_alloc_kernel_buf(ctx, bytes);
++ if (IS_ERR(shm)) {
++ ret = PTR_ERR(shm);
++ goto out_tee_session;
++ }
++
++ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
++ param[1].u.memref.shm = shm;
++ param[1].u.memref.size = bytes;
++
++ shm_buf = tee_shm_get_va(shm, 0);
++ if (IS_ERR(shm_buf)) {
++ ret = PTR_ERR(shm_buf);
++ pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
++ tee_shm_free(shm);
++
++ goto out_tee_session;
++ }
++
++ memcpy(shm_buf, buf, bytes);
++
++ ret = tee_client_invoke_func(ctx, &arg, param);
++ if (ret < 0 || arg.ret != 0) {
++ pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
++ if (!ret)
++ ret = -EIO;
++ }
++ pr_debug("Write OTPs %d to %zu, ret=%d\n", offset / 4, (offset + bytes) / 4, ret);
++
++ /* Lock the upper OTPs with ECC protection, word programming only */
++ if (!ret && ((offset + bytes) >= (lower * 4))) {
++ u32 start, nb_lock;
++ u32 *lock = (u32 *)shm_buf;
++ int i;
++
++ /*
++ * don't lock the lower OTPs, no ECC protection and incremental
++ * bit programming, a second write is allowed
++ */
++ start = max_t(u32, offset, lower * 4);
++ nb_lock = (offset + bytes - start) / 4;
++
++ param[0].u.value.a = start;
++ param[0].u.value.b = LOCK_ACCESS;
++ param[1].u.memref.size = nb_lock * 4;
++
++ for (i = 0; i < nb_lock; i++)
++ lock[i] = LOCK_PERM;
++
++ ret = tee_client_invoke_func(ctx, &arg, param);
++ if (ret < 0 || arg.ret != 0) {
++ pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
++ if (!ret)
++ ret = -EIO;
++ }
++ pr_debug("Lock upper OTPs %d to %d, ret=%d\n",
++ start / 4, start / 4 + nb_lock, ret);
++ }
++
++ tee_shm_free(shm);
++
++out_tee_session:
++ stm32_bsec_ta_close_session(ctx, session_id);
++
++ return ret;
++}
+--- /dev/null
++++ b/drivers/nvmem/stm32-bsec-optee-ta.h
+@@ -0,0 +1,80 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver
++ *
++ * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
++ */
++
++#if IS_ENABLED(CONFIG_NVMEM_STM32_BSEC_OPTEE_TA)
++/**
++ * stm32_bsec_optee_ta_open() - initialize the STM32 BSEC TA
++ * @ctx: the OP-TEE context on success
++ *
++ * Return:
++ * On success, 0. On failure, -errno.
++ */
++int stm32_bsec_optee_ta_open(struct tee_context **ctx);
++
++/**
++ * stm32_bsec_optee_ta_close() - release the STM32 BSEC TA
++ * @ctx: the OP-TEE context
++ *
++ * This function used to clean the OP-TEE resources initialized in
++ * stm32_bsec_optee_ta_open(); it can be used as callback to
++ * devm_add_action_or_reset()
++ */
++void stm32_bsec_optee_ta_close(void *ctx);
++
++/**
++ * stm32_bsec_optee_ta_read() - nvmem read access using TA client driver
++ * @ctx: the OP-TEE context provided by stm32_bsec_optee_ta_open
++ * @offset: nvmem offset
++ * @buf: buffer to fill with nvem values
++ * @bytes: number of bytes to read
++ *
++ * Return:
++ * On success, 0. On failure, -errno.
++ */
++int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
++ void *buf, size_t bytes);
++
++/**
++ * stm32_bsec_optee_ta_write() - nvmem write access using TA client driver
++ * @ctx: the OP-TEE context provided by stm32_bsec_optee_ta_open
++ * @lower: number of lower OTP, not protected by ECC
++ * @offset: nvmem offset
++ * @buf: buffer with nvem values
++ * @bytes: number of bytes to write
++ *
++ * Return:
++ * On success, 0. On failure, -errno.
++ */
++int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
++ unsigned int offset, void *buf, size_t bytes);
++
++#else
++
++static inline int stm32_bsec_optee_ta_open(struct tee_context **ctx)
++{
++ return -EOPNOTSUPP;
++}
++
++static inline void stm32_bsec_optee_ta_close(void *ctx)
++{
++}
++
++static inline int stm32_bsec_optee_ta_read(struct tee_context *ctx,
++ unsigned int offset, void *buf,
++ size_t bytes)
++{
++ return -EOPNOTSUPP;
++}
++
++static inline int stm32_bsec_optee_ta_write(struct tee_context *ctx,
++ unsigned int lower,
++ unsigned int offset, void *buf,
++ size_t bytes)
++{
++ return -EOPNOTSUPP;
++}
++#endif /* CONFIG_NVMEM_STM32_BSEC_OPTEE_TA */
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -11,6 +11,9 @@
+ #include <linux/module.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/of_device.h>
++#include <linux/tee_drv.h>
++
++#include "stm32-bsec-optee-ta.h"
+
+ /* BSEC secure service access from non-secure */
+ #define STM32_SMC_BSEC 0x82001003
+@@ -25,12 +28,14 @@
+ struct stm32_romem_cfg {
+ int size;
+ u8 lower;
++ bool ta;
+ };
+
+ struct stm32_romem_priv {
+ void __iomem *base;
+ struct nvmem_config cfg;
+ u8 lower;
++ struct tee_context *ctx;
+ };
+
+ static int stm32_romem_read(void *context, unsigned int offset, void *buf,
+@@ -138,12 +143,29 @@ static int stm32_bsec_write(void *contex
+ return 0;
+ }
+
++static int stm32_bsec_pta_read(void *context, unsigned int offset, void *buf,
++ size_t bytes)
++{
++ struct stm32_romem_priv *priv = context;
++
++ return stm32_bsec_optee_ta_read(priv->ctx, offset, buf, bytes);
++}
++
++static int stm32_bsec_pta_write(void *context, unsigned int offset, void *buf,
++ size_t bytes)
++{
++ struct stm32_romem_priv *priv = context;
++
++ return stm32_bsec_optee_ta_write(priv->ctx, priv->lower, offset, buf, bytes);
++}
++
+ static int stm32_romem_probe(struct platform_device *pdev)
+ {
+ const struct stm32_romem_cfg *cfg;
+ struct device *dev = &pdev->dev;
+ struct stm32_romem_priv *priv;
+ struct resource *res;
++ int rc;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -173,15 +195,31 @@ static int stm32_romem_probe(struct plat
+ } else {
+ priv->cfg.size = cfg->size;
+ priv->lower = cfg->lower;
+- priv->cfg.reg_read = stm32_bsec_read;
+- priv->cfg.reg_write = stm32_bsec_write;
++ if (cfg->ta) {
++ rc = stm32_bsec_optee_ta_open(&priv->ctx);
++ /* wait for OP-TEE client driver to be up and ready */
++ if (rc)
++ return rc;
++ }
++ if (priv->ctx) {
++ rc = devm_add_action_or_reset(dev, stm32_bsec_optee_ta_close, priv->ctx);
++ if (rc) {
++ dev_err(dev, "devm_add_action_or_reset() failed (%d)\n", rc);
++ return rc;
++ }
++ priv->cfg.reg_read = stm32_bsec_pta_read;
++ priv->cfg.reg_write = stm32_bsec_pta_write;
++ } else {
++ priv->cfg.reg_read = stm32_bsec_read;
++ priv->cfg.reg_write = stm32_bsec_write;
++ }
+ }
+
+ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg));
+ }
+
+ /*
+- * STM32MP15 BSEC OTP regions: 4096 OTP bits (with 3072 effective bits)
++ * STM32MP15/13 BSEC OTP regions: 4096 OTP bits (with 3072 effective bits)
+ * => 96 x 32-bits data words
+ * - Lower: 1K bits, 2:1 redundancy, incremental bit programming
+ * => 32 (x 32-bits) lower shadow registers = words 0 to 31
+@@ -191,6 +229,13 @@ static int stm32_romem_probe(struct plat
+ static const struct stm32_romem_cfg stm32mp15_bsec_cfg = {
+ .size = 384,
+ .lower = 32,
++ .ta = false,
++};
++
++static const struct stm32_romem_cfg stm32mp13_bsec_cfg = {
++ .size = 384,
++ .lower = 32,
++ .ta = true,
+ };
+
+ static const struct of_device_id stm32_romem_of_match[] = {
+@@ -198,7 +243,10 @@ static const struct of_device_id stm32_r
+ .compatible = "st,stm32mp15-bsec",
+ .data = (void *)&stm32mp15_bsec_cfg,
+ }, {
++ .compatible = "st,stm32mp13-bsec",
++ .data = (void *)&stm32mp13_bsec_cfg,
+ },
++ { /* sentinel */ },
+ };
+ MODULE_DEVICE_TABLE(of, stm32_romem_of_match);
+
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0008-nvmem-stm32-detect-bsec-pta-presence-for-STM32MP15x.patch b/target/linux/generic/backport-6.1/809-v6.3-0008-nvmem-stm32-detect-bsec-pta-presence-for-STM32MP15x.patch
new file mode 100644
index 0000000000..cea8e93858
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0008-nvmem-stm32-detect-bsec-pta-presence-for-STM32MP15x.patch
@@ -0,0 +1,85 @@
+From df2f34ef1d924125ffaf29dfdaf7cdbd3183c321 Mon Sep 17 00:00:00 2001
+From: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Date: Mon, 6 Feb 2023 13:43:52 +0000
+Subject: [PATCH] nvmem: stm32: detect bsec pta presence for STM32MP15x
+
+On STM32MP15x SoC, the SMC backend is optional when OP-TEE is used;
+the PTA BSEC should be used as it is done on STM32MP13x platform,
+but the BSEC SMC can be also used: it is a legacy mode in OP-TEE,
+not recommended but used in previous OP-TEE firmware.
+
+The presence of OP-TEE is dynamically detected in STM32MP15x device tree
+and the supported NVMEM backend is dynamically detected:
+- PTA with stm32_bsec_pta_find
+- SMC with stm32_bsec_check
+
+With OP-TEE but without PTA and SMC detection, the probe is deferred for
+STM32MP15x devices.
+
+On STM32MP13x platform, only the PTA is supported with cfg->ta = true
+and this detection is skipped.
+
+Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Reviewed-by: Etienne Carriere <etienne.carriere@linaro.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-19-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/stm32-romem.c | 38 +++++++++++++++++++++++++++++++++----
+ 1 file changed, 34 insertions(+), 4 deletions(-)
+
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -159,6 +159,31 @@ static int stm32_bsec_pta_write(void *co
+ return stm32_bsec_optee_ta_write(priv->ctx, priv->lower, offset, buf, bytes);
+ }
+
++static bool stm32_bsec_smc_check(void)
++{
++ u32 val;
++ int ret;
++
++ /* check that the OP-TEE support the BSEC SMC (legacy mode) */
++ ret = stm32_bsec_smc(STM32_SMC_READ_SHADOW, 0, 0, &val);
++
++ return !ret;
++}
++
++static bool optee_presence_check(void)
++{
++ struct device_node *np;
++ bool tee_detected = false;
++
++ /* check that the OP-TEE node is present and available. */
++ np = of_find_compatible_node(NULL, NULL, "linaro,optee-tz");
++ if (np && of_device_is_available(np))
++ tee_detected = true;
++ of_node_put(np);
++
++ return tee_detected;
++}
++
+ static int stm32_romem_probe(struct platform_device *pdev)
+ {
+ const struct stm32_romem_cfg *cfg;
+@@ -195,11 +220,16 @@ static int stm32_romem_probe(struct plat
+ } else {
+ priv->cfg.size = cfg->size;
+ priv->lower = cfg->lower;
+- if (cfg->ta) {
++ if (cfg->ta || optee_presence_check()) {
+ rc = stm32_bsec_optee_ta_open(&priv->ctx);
+- /* wait for OP-TEE client driver to be up and ready */
+- if (rc)
+- return rc;
++ if (rc) {
++ /* wait for OP-TEE client driver to be up and ready */
++ if (rc == -EPROBE_DEFER)
++ return -EPROBE_DEFER;
++ /* BSEC PTA is required or SMC not supported */
++ if (cfg->ta || !stm32_bsec_smc_check())
++ return rc;
++ }
+ }
+ if (priv->ctx) {
+ rc = devm_add_action_or_reset(dev, stm32_bsec_optee_ta_close, priv->ctx);
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0009-nvmem-rave-sp-eeprm-fix-kernel-doc-bad-line-warning.patch b/target/linux/generic/backport-6.1/809-v6.3-0009-nvmem-rave-sp-eeprm-fix-kernel-doc-bad-line-warning.patch
new file mode 100644
index 0000000000..9d6275a737
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0009-nvmem-rave-sp-eeprm-fix-kernel-doc-bad-line-warning.patch
@@ -0,0 +1,32 @@
+From 3e5ac22aa564026e99defc3a8e02082521a5b231 Mon Sep 17 00:00:00 2001
+From: Randy Dunlap <rdunlap@infradead.org>
+Date: Mon, 6 Feb 2023 13:43:53 +0000
+Subject: [PATCH] nvmem: rave-sp-eeprm: fix kernel-doc bad line warning
+
+Convert an empty line to " *" to avoid a kernel-doc warning:
+
+drivers/nvmem/rave-sp-eeprom.c:48: warning: bad line:
+
+Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
+Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Cc: Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>
+Cc: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
+Cc: Andrey Smirnov <andrew.smirnov@gmail.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-20-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/rave-sp-eeprom.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/rave-sp-eeprom.c
++++ b/drivers/nvmem/rave-sp-eeprom.c
+@@ -45,7 +45,7 @@ enum rave_sp_eeprom_header_size {
+ * @type: Access type (see enum rave_sp_eeprom_access_type)
+ * @success: Success flag (Success = 1, Failure = 0)
+ * @data: Read data
+-
++ *
+ * Note this structure corresponds to RSP_*_EEPROM payload from RAVE
+ * SP ICD
+ */
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0010-nvmem-qcom-spmi-sdam-register-at-device-init-time.patch b/target/linux/generic/backport-6.1/809-v6.3-0010-nvmem-qcom-spmi-sdam-register-at-device-init-time.patch
new file mode 100644
index 0000000000..1ab9e609d3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0010-nvmem-qcom-spmi-sdam-register-at-device-init-time.patch
@@ -0,0 +1,43 @@
+From eb7dda20f42a9137e9ee53d5ed3b743d49338cb5 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan+linaro@kernel.org>
+Date: Mon, 6 Feb 2023 13:43:54 +0000
+Subject: [PATCH] nvmem: qcom-spmi-sdam: register at device init time
+
+There are currently no in-tree users of the Qualcomm SDAM nvmem driver
+and there is generally no point in registering a driver that can be
+built as a module at subsys init time.
+
+Register the driver at the normal device init time instead and let
+driver core sort out the probe order.
+
+Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
+Reviewed-by: Bjorn Andersson <andersson@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-21-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/qcom-spmi-sdam.c | 13 +------------
+ 1 file changed, 1 insertion(+), 12 deletions(-)
+
+--- a/drivers/nvmem/qcom-spmi-sdam.c
++++ b/drivers/nvmem/qcom-spmi-sdam.c
+@@ -175,18 +175,7 @@ static struct platform_driver sdam_drive
+ },
+ .probe = sdam_probe,
+ };
+-
+-static int __init sdam_init(void)
+-{
+- return platform_driver_register(&sdam_driver);
+-}
+-subsys_initcall(sdam_init);
+-
+-static void __exit sdam_exit(void)
+-{
+- return platform_driver_unregister(&sdam_driver);
+-}
+-module_exit(sdam_exit);
++module_platform_driver(sdam_driver);
+
+ MODULE_DESCRIPTION("QCOM SPMI SDAM driver");
+ MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/backport-6.1/809-v6.3-0011-nvmem-stm32-fix-OPTEE-dependency.patch b/target/linux/generic/backport-6.1/809-v6.3-0011-nvmem-stm32-fix-OPTEE-dependency.patch
new file mode 100644
index 0000000000..dcf704c6ff
--- /dev/null
+++ b/target/linux/generic/backport-6.1/809-v6.3-0011-nvmem-stm32-fix-OPTEE-dependency.patch
@@ -0,0 +1,46 @@
+From 1dc7e37bb0ec1c997fac82031332a38c7610352f Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Mon, 6 Feb 2023 13:43:56 +0000
+Subject: [PATCH] nvmem: stm32: fix OPTEE dependency
+
+The stm32 nvmem driver fails to link as built-in when OPTEE
+is a loadable module:
+
+aarch64-linux-ld: drivers/nvmem/stm32-bsec-optee-ta.o: in function `stm32_bsec:
+stm32-bsec-optee-ta.c:(.text+0xc8): undefined reference to `tee_client_open_session'
+aarch64-linux-ld: drivers/nvmem/stm32-bsec-optee-ta.o: in function `stm32_bsec:
+stm32-bsec-optee-ta.c:(.text+0x1fc): undefined reference to `tee_client_open_context'
+
+Change the CONFIG_NVMEM_STM32_ROMEM definition so it can only
+be built-in if OPTEE is either built-in or disabled, and
+make NVMEM_STM32_BSEC_OPTEE_TA a hidden symbol instead.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-23-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -291,8 +291,7 @@ config NVMEM_SPRD_EFUSE
+ will be called nvmem-sprd-efuse.
+
+ config NVMEM_STM32_BSEC_OPTEE_TA
+- bool "STM32MP BSEC OP-TEE TA support for nvmem-stm32-romem driver"
+- depends on OPTEE
++ def_bool NVMEM_STM32_ROMEM && OPTEE
+ help
+ Say y here to enable the accesses to STM32MP SoC OTPs by the OP-TEE
+ trusted application STM32MP BSEC.
+@@ -303,7 +302,7 @@ config NVMEM_STM32_BSEC_OPTEE_TA
+ config NVMEM_STM32_ROMEM
+ tristate "STMicroelectronics STM32 factory-programmed memory support"
+ depends on ARCH_STM32 || COMPILE_TEST
+- imply NVMEM_STM32_BSEC_OPTEE_TA
++ depends on OPTEE || !OPTEE
+ help
+ Say y here to enable read-only access for STMicroelectronics STM32
+ factory-programmed memory area.
diff --git a/target/linux/generic/backport-6.1/810-v5.17-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch b/target/linux/generic/backport-6.1/810-v5.17-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch
new file mode 100644
index 0000000000..25802be7e3
--- /dev/null
+++ b/target/linux/generic/backport-6.1/810-v5.17-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 1/1] net: qmi_wwan: add ZTE MF286D modem 19d2:1485
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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
+
+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>
+---
+ 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
+@@ -1314,6 +1314,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/backport-6.1/811-v6.4-0001-nvmem-xilinx-zynqmp-make-modular.patch b/target/linux/generic/backport-6.1/811-v6.4-0001-nvmem-xilinx-zynqmp-make-modular.patch
new file mode 100644
index 0000000000..8328e87c0a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0001-nvmem-xilinx-zynqmp-make-modular.patch
@@ -0,0 +1,35 @@
+From bcd1fe07def0f070eb5f31594620aaee6f81d31a Mon Sep 17 00:00:00 2001
+From: Nick Alcock <nick.alcock@oracle.com>
+Date: Tue, 4 Apr 2023 18:21:11 +0100
+Subject: [PATCH] nvmem: xilinx: zynqmp: make modular
+
+This driver has a MODULE_LICENSE but is not tristate so cannot be
+built as a module, unlike all its peers: make it modular to match.
+
+Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
+Suggested-by: Michal Simek <michal.simek@amd.com>
+Cc: Luis Chamberlain <mcgrof@kernel.org>
+Cc: linux-modules@vger.kernel.org
+Cc: linux-kernel@vger.kernel.org
+Cc: Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>
+Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Cc: Michal Simek <michal.simek@xilinx.com>
+Cc: linux-arm-kernel@lists.infradead.org
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -368,7 +368,7 @@ config NVMEM_VF610_OCOTP
+ be called nvmem-vf610-ocotp.
+
+ config NVMEM_ZYNQMP
+- bool "Xilinx ZYNQMP SoC nvmem firmware support"
++ tristate "Xilinx ZYNQMP SoC nvmem firmware support"
+ depends on ARCH_ZYNQMP
+ help
+ This is a driver to access hardware related data like
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch b/target/linux/generic/backport-6.1/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch
new file mode 100644
index 0000000000..23518d21f7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch
@@ -0,0 +1,387 @@
+From 266570f496b90dea8fda893c2cf7c28d63ae2bd9 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 4 Apr 2023 18:21:21 +0100
+Subject: [PATCH] nvmem: core: introduce NVMEM layouts
+
+NVMEM layouts are used to generate NVMEM cells during runtime. Think of
+an EEPROM with a well-defined conent. For now, the content can be
+described by a device tree or a board file. But this only works if the
+offsets and lengths are static and don't change. One could also argue
+that putting the layout of the EEPROM in the device tree is the wrong
+place. Instead, the device tree should just have a specific compatible
+string.
+
+Right now there are two use cases:
+ (1) The NVMEM cell needs special processing. E.g. if it only specifies
+ a base MAC address offset and you need to add an offset, or it
+ needs to parse a MAC from ASCII format or some proprietary format.
+ (Post processing of cells is added in a later commit).
+ (2) u-boot environment parsing. The cells don't have a particular
+ offset but it needs parsing the content to determine the offsets
+ and length.
+
+Co-developed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-14-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/driver-api/nvmem.rst | 15 ++++
+ drivers/nvmem/Kconfig | 4 +
+ drivers/nvmem/Makefile | 1 +
+ drivers/nvmem/core.c | 120 +++++++++++++++++++++++++++++
+ drivers/nvmem/layouts/Kconfig | 5 ++
+ drivers/nvmem/layouts/Makefile | 4 +
+ include/linux/nvmem-consumer.h | 7 ++
+ include/linux/nvmem-provider.h | 51 ++++++++++++
+ 8 files changed, 207 insertions(+)
+ create mode 100644 drivers/nvmem/layouts/Kconfig
+ create mode 100644 drivers/nvmem/layouts/Makefile
+
+--- a/Documentation/driver-api/nvmem.rst
++++ b/Documentation/driver-api/nvmem.rst
+@@ -189,3 +189,18 @@ ex::
+ =====================
+
+ See Documentation/devicetree/bindings/nvmem/nvmem.txt
++
++8. NVMEM layouts
++================
++
++NVMEM layouts are yet another mechanism to create cells. With the device
++tree binding it is possible to specify simple cells by using an offset
++and a length. Sometimes, the cells doesn't have a static offset, but
++the content is still well defined, e.g. tag-length-values. In this case,
++the NVMEM device content has to be first parsed and the cells need to
++be added accordingly. Layouts let you read the content of the NVMEM device
++and let you add cells dynamically.
++
++Another use case for layouts is the post processing of cells. With layouts,
++it is possible to associate a custom post processing hook to a cell. It
++even possible to add this hook to cells not created by the layout itself.
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -21,6 +21,10 @@ config NVMEM_SYSFS
+ This interface is mostly used by userspace applications to
+ read/write directly into nvmem.
+
++# Layouts
++
++source "drivers/nvmem/layouts/Kconfig"
++
+ # Devices
+
+ config NVMEM_APPLE_EFUSES
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -5,6 +5,7 @@
+
+ obj-$(CONFIG_NVMEM) += nvmem_core.o
+ nvmem_core-y := core.o
++obj-y += layouts/
+
+ # Devices
+ obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -40,6 +40,7 @@ struct nvmem_device {
+ nvmem_reg_write_t reg_write;
+ nvmem_cell_post_process_t cell_post_process;
+ struct gpio_desc *wp_gpio;
++ struct nvmem_layout *layout;
+ void *priv;
+ };
+
+@@ -74,6 +75,9 @@ static LIST_HEAD(nvmem_lookup_list);
+
+ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
+
++static DEFINE_SPINLOCK(nvmem_layout_lock);
++static LIST_HEAD(nvmem_layouts);
++
+ static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
+ void *val, size_t bytes)
+ {
+@@ -728,6 +732,101 @@ static int nvmem_add_cells_from_of(struc
+ return 0;
+ }
+
++int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
++{
++ layout->owner = owner;
++
++ spin_lock(&nvmem_layout_lock);
++ list_add(&layout->node, &nvmem_layouts);
++ spin_unlock(&nvmem_layout_lock);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(__nvmem_layout_register);
++
++void nvmem_layout_unregister(struct nvmem_layout *layout)
++{
++ spin_lock(&nvmem_layout_lock);
++ list_del(&layout->node);
++ spin_unlock(&nvmem_layout_lock);
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
++
++static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
++{
++ struct device_node *layout_np, *np = nvmem->dev.of_node;
++ struct nvmem_layout *l, *layout = NULL;
++
++ layout_np = of_get_child_by_name(np, "nvmem-layout");
++ if (!layout_np)
++ return NULL;
++
++ spin_lock(&nvmem_layout_lock);
++
++ list_for_each_entry(l, &nvmem_layouts, node) {
++ if (of_match_node(l->of_match_table, layout_np)) {
++ if (try_module_get(l->owner))
++ layout = l;
++
++ break;
++ }
++ }
++
++ spin_unlock(&nvmem_layout_lock);
++ of_node_put(layout_np);
++
++ return layout;
++}
++
++static void nvmem_layout_put(struct nvmem_layout *layout)
++{
++ if (layout)
++ module_put(layout->owner);
++}
++
++static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
++{
++ struct nvmem_layout *layout = nvmem->layout;
++ int ret;
++
++ if (layout && layout->add_cells) {
++ ret = layout->add_cells(&nvmem->dev, nvmem, layout);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++#if IS_ENABLED(CONFIG_OF)
++/**
++ * of_nvmem_layout_get_container() - Get OF node to layout container.
++ *
++ * @nvmem: nvmem device.
++ *
++ * Return: a node pointer with refcount incremented or NULL if no
++ * container exists. Use of_node_put() on it when done.
++ */
++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
++{
++ return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
++}
++EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
++#endif
++
++const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
++ struct nvmem_layout *layout)
++{
++ struct device_node __maybe_unused *layout_np;
++ const struct of_device_id *match;
++
++ layout_np = of_nvmem_layout_get_container(nvmem);
++ match = of_match_node(layout->of_match_table, layout_np);
++
++ return match ? match->data : NULL;
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_get_match_data);
++
+ /**
+ * nvmem_register() - Register a nvmem device for given nvmem_config.
+ * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
+@@ -834,6 +933,12 @@ struct nvmem_device *nvmem_register(cons
+ goto err_put_device;
+ }
+
++ /*
++ * If the driver supplied a layout by config->layout, the module
++ * pointer will be NULL and nvmem_layout_put() will be a noop.
++ */
++ nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
++
+ if (config->cells) {
+ rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
+ if (rval)
+@@ -854,12 +959,17 @@ struct nvmem_device *nvmem_register(cons
+ if (rval)
+ goto err_remove_cells;
+
++ rval = nvmem_add_cells_from_layout(nvmem);
++ if (rval)
++ goto err_remove_cells;
++
+ blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
+
+ return nvmem;
+
+ err_remove_cells:
+ nvmem_device_remove_all_cells(nvmem);
++ nvmem_layout_put(nvmem->layout);
+ if (config->compat)
+ nvmem_sysfs_remove_compat(nvmem, config);
+ err_put_device:
+@@ -881,6 +991,7 @@ static void nvmem_device_release(struct
+ device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+
+ nvmem_device_remove_all_cells(nvmem);
++ nvmem_layout_put(nvmem->layout);
+ device_unregister(&nvmem->dev);
+ }
+
+@@ -1246,6 +1357,15 @@ struct nvmem_cell *of_nvmem_cell_get(str
+ return ERR_PTR(-EINVAL);
+ }
+
++ /* nvmem layouts produce cells within the nvmem-layout container */
++ if (of_node_name_eq(nvmem_np, "nvmem-layout")) {
++ nvmem_np = of_get_next_parent(nvmem_np);
++ if (!nvmem_np) {
++ of_node_put(cell_np);
++ return ERR_PTR(-EINVAL);
++ }
++ }
++
+ nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
+ of_node_put(nvmem_np);
+ if (IS_ERR(nvmem)) {
+--- /dev/null
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0
++
++menu "Layout Types"
++
++endmenu
+--- /dev/null
++++ b/drivers/nvmem/layouts/Makefile
+@@ -0,0 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# Makefile for nvmem layouts.
++#
+--- a/include/linux/nvmem-consumer.h
++++ b/include/linux/nvmem-consumer.h
+@@ -239,6 +239,7 @@ struct nvmem_cell *of_nvmem_cell_get(str
+ const char *id);
+ struct nvmem_device *of_nvmem_device_get(struct device_node *np,
+ const char *name);
++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
+ #else
+ static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
+ const char *id)
+@@ -251,6 +252,12 @@ static inline struct nvmem_device *of_nv
+ {
+ return ERR_PTR(-EOPNOTSUPP);
+ }
++
++static inline struct device_node *
++of_nvmem_layout_get_container(struct nvmem_device *nvmem)
++{
++ return ERR_PTR(-EOPNOTSUPP);
++}
+ #endif /* CONFIG_NVMEM && CONFIG_OF */
+
+ #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -88,6 +88,7 @@ struct nvmem_cell_info {
+ * @stride: Minimum read/write access stride.
+ * @priv: User context passed to read/write callbacks.
+ * @ignore_wp: Write Protect pin is managed by the provider.
++ * @layout: Fixed layout associated with this nvmem device.
+ *
+ * Note: A default "nvmem<id>" name will be assigned to the device if
+ * no name is specified in its configuration. In such case "<id>" is
+@@ -109,6 +110,7 @@ struct nvmem_config {
+ bool read_only;
+ bool root_only;
+ bool ignore_wp;
++ struct nvmem_layout *layout;
+ struct device_node *of_node;
+ bool no_of_node;
+ nvmem_reg_read_t reg_read;
+@@ -142,6 +144,33 @@ struct nvmem_cell_table {
+ struct list_head node;
+ };
+
++/**
++ * struct nvmem_layout - NVMEM layout definitions
++ *
++ * @name: Layout name.
++ * @of_match_table: Open firmware match table.
++ * @add_cells: Will be called if a nvmem device is found which
++ * has this layout. The function will add layout
++ * specific cells with nvmem_add_one_cell().
++ * @owner: Pointer to struct module.
++ * @node: List node.
++ *
++ * A nvmem device can hold a well defined structure which can just be
++ * evaluated during runtime. For example a TLV list, or a list of "name=val"
++ * pairs. A nvmem layout can parse the nvmem device and add appropriate
++ * cells.
++ */
++struct nvmem_layout {
++ const char *name;
++ const struct of_device_id *of_match_table;
++ int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
++ struct nvmem_layout *layout);
++
++ /* private */
++ struct module *owner;
++ struct list_head node;
++};
++
+ #if IS_ENABLED(CONFIG_NVMEM)
+
+ struct nvmem_device *nvmem_register(const struct nvmem_config *cfg);
+@@ -156,6 +185,14 @@ void nvmem_del_cell_table(struct nvmem_c
+ int nvmem_add_one_cell(struct nvmem_device *nvmem,
+ const struct nvmem_cell_info *info);
+
++int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner);
++#define nvmem_layout_register(layout) \
++ __nvmem_layout_register(layout, THIS_MODULE)
++void nvmem_layout_unregister(struct nvmem_layout *layout);
++
++const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
++ struct nvmem_layout *layout);
++
+ #else
+
+ static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
+@@ -179,5 +216,19 @@ static inline int nvmem_add_one_cell(str
+ return -EOPNOTSUPP;
+ }
+
++static inline int nvmem_layout_register(struct nvmem_layout *layout)
++{
++ return -EOPNOTSUPP;
++}
++
++static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {}
++
++static inline const void *
++nvmem_layout_get_match_data(struct nvmem_device *nvmem,
++ struct nvmem_layout *layout)
++{
++ return NULL;
++}
++
+ #endif /* CONFIG_NVMEM */
+ #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0003-nvmem-core-handle-the-absence-of-expected-layouts.patch b/target/linux/generic/backport-6.1/811-v6.4-0003-nvmem-core-handle-the-absence-of-expected-layouts.patch
new file mode 100644
index 0000000000..6fa7b6382d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0003-nvmem-core-handle-the-absence-of-expected-layouts.patch
@@ -0,0 +1,61 @@
+From 6468a6f45148fb5e95c86b4efebf63f9abcd2137 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:22 +0100
+Subject: [PATCH] nvmem: core: handle the absence of expected layouts
+
+Make nvmem_layout_get() return -EPROBE_DEFER while the expected layout
+is not available. This condition cannot be triggered today as nvmem
+layout drivers are initialed as part of an early init call, but soon
+these drivers will be converted into modules and be initialized with a
+standard priority, so the unavailability of the drivers might become a
+reality that must be taken care of.
+
+Let's anticipate this by telling the caller the layout might not yet be
+available. A probe deferral is requested in this case.
+
+Please note this does not affect any nvmem device not using layouts,
+because an early check against the "nvmem-layout" container presence
+will return NULL in this case.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Tested-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-15-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -755,7 +755,7 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregiste
+ static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
+ {
+ struct device_node *layout_np, *np = nvmem->dev.of_node;
+- struct nvmem_layout *l, *layout = NULL;
++ struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
+
+ layout_np = of_get_child_by_name(np, "nvmem-layout");
+ if (!layout_np)
+@@ -938,6 +938,13 @@ struct nvmem_device *nvmem_register(cons
+ * pointer will be NULL and nvmem_layout_put() will be a noop.
+ */
+ nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
++ if (IS_ERR(nvmem->layout)) {
++ rval = PTR_ERR(nvmem->layout);
++ nvmem->layout = NULL;
++
++ if (rval == -EPROBE_DEFER)
++ goto err_teardown_compat;
++ }
+
+ if (config->cells) {
+ rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
+@@ -970,6 +977,7 @@ struct nvmem_device *nvmem_register(cons
+ err_remove_cells:
+ nvmem_device_remove_all_cells(nvmem);
+ nvmem_layout_put(nvmem->layout);
++err_teardown_compat:
+ if (config->compat)
+ nvmem_sysfs_remove_compat(nvmem, config);
+ err_put_device:
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0004-nvmem-core-request-layout-modules-loading.patch b/target/linux/generic/backport-6.1/811-v6.4-0004-nvmem-core-request-layout-modules-loading.patch
new file mode 100644
index 0000000000..b9341666f9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0004-nvmem-core-request-layout-modules-loading.patch
@@ -0,0 +1,52 @@
+From b1c37bec1ccfe5ccab72bc0ddc0dfa45c43e2de2 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:23 +0100
+Subject: [PATCH] nvmem: core: request layout modules loading
+
+When a storage device like an eeprom or an mtd device probes, it
+registers an nvmem device if the nvmem subsystem has been enabled (bool
+symbol). During nvmem registration, if the device is using layouts to
+expose dynamic nvmem cells, the core will first try to get a reference
+over the layout driver callbacks. In practice there is not relationship
+that can be described between the storage driver and the nvmem
+layout. So there is no way we can enforce both drivers will be built-in
+or both will be modules. If the storage device driver is built-in but
+the layout is built as a module, instead of badly failing with an
+endless probe deferral loop, lets just make a modprobe call in case the
+driver was made available in an initramfs with
+of_device_node_request_module(), and offer a fully functional system to
+the user.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Tested-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-16-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -17,6 +17,7 @@
+ #include <linux/nvmem-provider.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/of.h>
++#include <linux/of_device.h>
+ #include <linux/slab.h>
+
+ struct nvmem_device {
+@@ -761,6 +762,13 @@ static struct nvmem_layout *nvmem_layout
+ if (!layout_np)
+ return NULL;
+
++ /*
++ * In case the nvmem device was built-in while the layout was built as a
++ * module, we shall manually request the layout driver loading otherwise
++ * we'll never have any match.
++ */
++ of_request_module(layout_np);
++
+ spin_lock(&nvmem_layout_lock);
+
+ list_for_each_entry(l, &nvmem_layouts, node) {
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0005-nvmem-core-add-per-cell-post-processing.patch b/target/linux/generic/backport-6.1/811-v6.4-0005-nvmem-core-add-per-cell-post-processing.patch
new file mode 100644
index 0000000000..53628cd4e4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0005-nvmem-core-add-per-cell-post-processing.patch
@@ -0,0 +1,86 @@
+From 345ec382cd4b736c36e01f155d08c913b225b736 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 4 Apr 2023 18:21:24 +0100
+Subject: [PATCH] nvmem: core: add per-cell post processing
+
+Instead of relying on the name the consumer is using for the cell, like
+it is done for the nvmem .cell_post_process configuration parameter,
+provide a per-cell post processing hook. This can then be populated by
+the NVMEM provider (or the NVMEM layout) when adding the cell.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-17-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 17 +++++++++++++++++
+ include/linux/nvmem-provider.h | 3 +++
+ 2 files changed, 20 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -54,6 +54,7 @@ struct nvmem_cell_entry {
+ int bytes;
+ int bit_offset;
+ int nbits;
++ nvmem_cell_post_process_t read_post_process;
+ struct device_node *np;
+ struct nvmem_device *nvmem;
+ struct list_head node;
+@@ -470,6 +471,7 @@ static int nvmem_cell_info_to_nvmem_cell
+ cell->offset = info->offset;
+ cell->bytes = info->bytes;
+ cell->name = info->name;
++ cell->read_post_process = info->read_post_process;
+
+ cell->bit_offset = info->bit_offset;
+ cell->nbits = info->nbits;
+@@ -1563,6 +1565,13 @@ static int __nvmem_cell_read(struct nvme
+ if (cell->bit_offset || cell->nbits)
+ nvmem_shift_read_buffer_in_place(cell, buf);
+
++ if (cell->read_post_process) {
++ rc = cell->read_post_process(nvmem->priv, id, index,
++ cell->offset, buf, cell->bytes);
++ if (rc)
++ return rc;
++ }
++
+ if (nvmem->cell_post_process) {
+ rc = nvmem->cell_post_process(nvmem->priv, id, index,
+ cell->offset, buf, cell->bytes);
+@@ -1671,6 +1680,14 @@ static int __nvmem_cell_entry_write(stru
+ (cell->bit_offset == 0 && len != cell->bytes))
+ return -EINVAL;
+
++ /*
++ * Any cells which have a read_post_process hook are read-only because
++ * we cannot reverse the operation and it might affect other cells,
++ * too.
++ */
++ if (cell->read_post_process)
++ return -EINVAL;
++
+ if (cell->bit_offset || cell->nbits) {
+ buf = nvmem_cell_prepare_write_buffer(cell, buf, len);
+ if (IS_ERR(buf))
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -54,6 +54,8 @@ struct nvmem_keepout {
+ * @bit_offset: Bit offset if cell is smaller than a byte.
+ * @nbits: Number of bits.
+ * @np: Optional device_node pointer.
++ * @read_post_process: Callback for optional post processing of cell data
++ * on reads.
+ */
+ struct nvmem_cell_info {
+ const char *name;
+@@ -62,6 +64,7 @@ struct nvmem_cell_info {
+ unsigned int bit_offset;
+ unsigned int nbits;
+ struct device_node *np;
++ nvmem_cell_post_process_t read_post_process;
+ };
+
+ /**
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0006-nvmem-core-allow-to-modify-a-cell-before-adding-it.patch b/target/linux/generic/backport-6.1/811-v6.4-0006-nvmem-core-allow-to-modify-a-cell-before-adding-it.patch
new file mode 100644
index 0000000000..32990148c8
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0006-nvmem-core-allow-to-modify-a-cell-before-adding-it.patch
@@ -0,0 +1,59 @@
+From de12c9691501ccba41a154c223869f82be4c12fd Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 4 Apr 2023 18:21:25 +0100
+Subject: [PATCH] nvmem: core: allow to modify a cell before adding it
+
+Provide a way to modify a cell before it will get added. This is useful
+to attach a custom post processing hook via a layout.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-18-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 4 ++++
+ include/linux/nvmem-provider.h | 5 +++++
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -695,6 +695,7 @@ static int nvmem_validate_keepouts(struc
+
+ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
+ {
++ struct nvmem_layout *layout = nvmem->layout;
+ struct device *dev = &nvmem->dev;
+ struct device_node *child;
+ const __be32 *addr;
+@@ -724,6 +725,9 @@ static int nvmem_add_cells_from_of(struc
+
+ info.np = of_node_get(child);
+
++ if (layout && layout->fixup_cell_info)
++ layout->fixup_cell_info(nvmem, layout, &info);
++
+ ret = nvmem_add_one_cell(nvmem, &info);
+ kfree(info.name);
+ if (ret) {
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -155,6 +155,8 @@ struct nvmem_cell_table {
+ * @add_cells: Will be called if a nvmem device is found which
+ * has this layout. The function will add layout
+ * specific cells with nvmem_add_one_cell().
++ * @fixup_cell_info: Will be called before a cell is added. Can be
++ * used to modify the nvmem_cell_info.
+ * @owner: Pointer to struct module.
+ * @node: List node.
+ *
+@@ -168,6 +170,9 @@ struct nvmem_layout {
+ const struct of_device_id *of_match_table;
+ int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
+ struct nvmem_layout *layout);
++ void (*fixup_cell_info)(struct nvmem_device *nvmem,
++ struct nvmem_layout *layout,
++ struct nvmem_cell_info *cell);
+
+ /* private */
+ struct module *owner;
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0007-nvmem-imx-ocotp-replace-global-post-processing-with-.patch b/target/linux/generic/backport-6.1/811-v6.4-0007-nvmem-imx-ocotp-replace-global-post-processing-with-.patch
new file mode 100644
index 0000000000..2a5fa618ea
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0007-nvmem-imx-ocotp-replace-global-post-processing-with-.patch
@@ -0,0 +1,81 @@
+From 6c56a82d7895a213a43182a5d01a21a906a79847 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 4 Apr 2023 18:21:26 +0100
+Subject: [PATCH] nvmem: imx-ocotp: replace global post processing with layouts
+
+In preparation of retiring the global post processing hook change this
+driver to use layouts. The layout will be supplied during registration
+and will be used to add the post processing hook to all added cells.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Tested-by: Michael Walle <michael@walle.cc> # on kontron-pitx-imx8m
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-19-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/imx-ocotp.c | 30 +++++++++++++++++++-----------
+ 1 file changed, 19 insertions(+), 11 deletions(-)
+
+--- a/drivers/nvmem/imx-ocotp.c
++++ b/drivers/nvmem/imx-ocotp.c
+@@ -225,18 +225,13 @@ read_end:
+ static int imx_ocotp_cell_pp(void *context, const char *id, int index,
+ unsigned int offset, void *data, size_t bytes)
+ {
+- struct ocotp_priv *priv = context;
++ u8 *buf = data;
++ int i;
+
+ /* Deal with some post processing of nvmem cell data */
+- if (id && !strcmp(id, "mac-address")) {
+- if (priv->params->reverse_mac_address) {
+- u8 *buf = data;
+- int i;
+-
+- for (i = 0; i < bytes/2; i++)
+- swap(buf[i], buf[bytes - i - 1]);
+- }
+- }
++ if (id && !strcmp(id, "mac-address"))
++ for (i = 0; i < bytes / 2; i++)
++ swap(buf[i], buf[bytes - i - 1]);
+
+ return 0;
+ }
+@@ -488,7 +483,6 @@ static struct nvmem_config imx_ocotp_nvm
+ .stride = 1,
+ .reg_read = imx_ocotp_read,
+ .reg_write = imx_ocotp_write,
+- .cell_post_process = imx_ocotp_cell_pp,
+ };
+
+ static const struct ocotp_params imx6q_params = {
+@@ -595,6 +589,17 @@ static const struct of_device_id imx_oco
+ };
+ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
+
++static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem,
++ struct nvmem_layout *layout,
++ struct nvmem_cell_info *cell)
++{
++ cell->read_post_process = imx_ocotp_cell_pp;
++}
++
++struct nvmem_layout imx_ocotp_layout = {
++ .fixup_cell_info = imx_ocotp_fixup_cell_info,
++};
++
+ static int imx_ocotp_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -619,6 +624,9 @@ static int imx_ocotp_probe(struct platfo
+ imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
+ imx_ocotp_nvmem_config.dev = dev;
+ imx_ocotp_nvmem_config.priv = priv;
++ if (priv->params->reverse_mac_address)
++ imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
++
+ priv->config = &imx_ocotp_nvmem_config;
+
+ clk_prepare_enable(priv->clk);
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0008-nvmem-cell-drop-global-cell_post_process.patch b/target/linux/generic/backport-6.1/811-v6.4-0008-nvmem-cell-drop-global-cell_post_process.patch
new file mode 100644
index 0000000000..eac202b882
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0008-nvmem-cell-drop-global-cell_post_process.patch
@@ -0,0 +1,68 @@
+From 011e40a166fdaa65fb9946b7cd91efec85b70dbb Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 4 Apr 2023 18:21:27 +0100
+Subject: [PATCH] nvmem: cell: drop global cell_post_process
+
+There are no users anymore for the global cell_post_process callback
+anymore. New users should use proper nvmem layouts.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-20-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 9 ---------
+ include/linux/nvmem-provider.h | 2 --
+ 2 files changed, 11 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -39,7 +39,6 @@ struct nvmem_device {
+ unsigned int nkeepout;
+ nvmem_reg_read_t reg_read;
+ nvmem_reg_write_t reg_write;
+- nvmem_cell_post_process_t cell_post_process;
+ struct gpio_desc *wp_gpio;
+ struct nvmem_layout *layout;
+ void *priv;
+@@ -903,7 +902,6 @@ struct nvmem_device *nvmem_register(cons
+ nvmem->type = config->type;
+ nvmem->reg_read = config->reg_read;
+ nvmem->reg_write = config->reg_write;
+- nvmem->cell_post_process = config->cell_post_process;
+ nvmem->keepout = config->keepout;
+ nvmem->nkeepout = config->nkeepout;
+ if (config->of_node)
+@@ -1575,13 +1573,6 @@ static int __nvmem_cell_read(struct nvme
+ if (rc)
+ return rc;
+ }
+-
+- if (nvmem->cell_post_process) {
+- rc = nvmem->cell_post_process(nvmem->priv, id, index,
+- cell->offset, buf, cell->bytes);
+- if (rc)
+- return rc;
+- }
+
+ if (len)
+ *len = cell->bytes;
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -85,7 +85,6 @@ struct nvmem_cell_info {
+ * @no_of_node: Device should not use the parent's of_node even if it's !NULL.
+ * @reg_read: Callback to read data.
+ * @reg_write: Callback to write data.
+- * @cell_post_process: Callback for vendor specific post processing of cell data
+ * @size: Device size.
+ * @word_size: Minimum read/write access granularity.
+ * @stride: Minimum read/write access stride.
+@@ -118,7 +117,6 @@ struct nvmem_config {
+ bool no_of_node;
+ nvmem_reg_read_t reg_read;
+ nvmem_reg_write_t reg_write;
+- nvmem_cell_post_process_t cell_post_process;
+ int size;
+ int word_size;
+ int stride;
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0009-nvmem-core-provide-own-priv-pointer-in-post-process-.patch b/target/linux/generic/backport-6.1/811-v6.4-0009-nvmem-core-provide-own-priv-pointer-in-post-process-.patch
new file mode 100644
index 0000000000..46b30a2ed9
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0009-nvmem-core-provide-own-priv-pointer-in-post-process-.patch
@@ -0,0 +1,76 @@
+From 8a134fd9f9323f4c39ec27055b3d3723cfb5c1e9 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 4 Apr 2023 18:21:28 +0100
+Subject: [PATCH] nvmem: core: provide own priv pointer in post process
+ callback
+
+It doesn't make any more sense to have a opaque pointer set up by the
+nvmem device. Usually, the layout isn't associated with a particular
+nvmem device. Instead, let the caller who set the post process callback
+provide the priv pointer.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-21-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 4 +++-
+ include/linux/nvmem-provider.h | 5 ++++-
+ 2 files changed, 7 insertions(+), 2 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -54,6 +54,7 @@ struct nvmem_cell_entry {
+ int bit_offset;
+ int nbits;
+ nvmem_cell_post_process_t read_post_process;
++ void *priv;
+ struct device_node *np;
+ struct nvmem_device *nvmem;
+ struct list_head node;
+@@ -471,6 +472,7 @@ static int nvmem_cell_info_to_nvmem_cell
+ cell->bytes = info->bytes;
+ cell->name = info->name;
+ cell->read_post_process = info->read_post_process;
++ cell->priv = info->priv;
+
+ cell->bit_offset = info->bit_offset;
+ cell->nbits = info->nbits;
+@@ -1568,7 +1570,7 @@ static int __nvmem_cell_read(struct nvme
+ nvmem_shift_read_buffer_in_place(cell, buf);
+
+ if (cell->read_post_process) {
+- rc = cell->read_post_process(nvmem->priv, id, index,
++ rc = cell->read_post_process(cell->priv, id, index,
+ cell->offset, buf, cell->bytes);
+ if (rc)
+ return rc;
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -20,7 +20,8 @@ typedef int (*nvmem_reg_write_t)(void *p
+ void *val, size_t bytes);
+ /* used for vendor specific post processing of cell data */
+ typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, int index,
+- unsigned int offset, void *buf, size_t bytes);
++ unsigned int offset, void *buf,
++ size_t bytes);
+
+ enum nvmem_type {
+ NVMEM_TYPE_UNKNOWN = 0,
+@@ -56,6 +57,7 @@ struct nvmem_keepout {
+ * @np: Optional device_node pointer.
+ * @read_post_process: Callback for optional post processing of cell data
+ * on reads.
++ * @priv: Opaque data passed to the read_post_process hook.
+ */
+ struct nvmem_cell_info {
+ const char *name;
+@@ -65,6 +67,7 @@ struct nvmem_cell_info {
+ unsigned int nbits;
+ struct device_node *np;
+ nvmem_cell_post_process_t read_post_process;
++ void *priv;
+ };
+
+ /**
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0010-nvmem-layouts-sl28vpd-Add-new-layout-driver.patch b/target/linux/generic/backport-6.1/811-v6.4-0010-nvmem-layouts-sl28vpd-Add-new-layout-driver.patch
new file mode 100644
index 0000000000..7d97658b60
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0010-nvmem-layouts-sl28vpd-Add-new-layout-driver.patch
@@ -0,0 +1,215 @@
+From d9fae023fe86069750092fc1c2f3a73e2fb18512 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 4 Apr 2023 18:21:29 +0100
+Subject: [PATCH] nvmem: layouts: sl28vpd: Add new layout driver
+
+This layout applies to the VPD of the Kontron sl28 boards. The VPD only
+contains a base MAC address. Therefore, we have to add an individual
+offset to it. This is done by taking the second argument of the nvmem
+phandle into account. Also this let us checking the VPD version and the
+checksum.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-22-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts/Kconfig | 9 ++
+ drivers/nvmem/layouts/Makefile | 2 +
+ drivers/nvmem/layouts/sl28vpd.c | 165 ++++++++++++++++++++++++++++++++
+ 3 files changed, 176 insertions(+)
+ create mode 100644 drivers/nvmem/layouts/sl28vpd.c
+
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -2,4 +2,13 @@
+
+ menu "Layout Types"
+
++config NVMEM_LAYOUT_SL28_VPD
++ tristate "Kontron sl28 VPD layout support"
++ select CRC8
++ help
++ Say Y here if you want to support the VPD layout of the Kontron
++ SMARC-sAL28 boards.
++
++ If unsure, say N.
++
+ endmenu
+--- a/drivers/nvmem/layouts/Makefile
++++ b/drivers/nvmem/layouts/Makefile
+@@ -2,3 +2,5 @@
+ #
+ # Makefile for nvmem layouts.
+ #
++
++obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
+--- /dev/null
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -0,0 +1,165 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <linux/crc8.h>
++#include <linux/etherdevice.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <uapi/linux/if_ether.h>
++
++#define SL28VPD_MAGIC 'V'
++
++struct sl28vpd_header {
++ u8 magic;
++ u8 version;
++} __packed;
++
++struct sl28vpd_v1 {
++ struct sl28vpd_header header;
++ char serial_number[15];
++ u8 base_mac_address[ETH_ALEN];
++ u8 crc8;
++} __packed;
++
++static int sl28vpd_mac_address_pp(void *priv, const char *id, int index,
++ unsigned int offset, void *buf,
++ size_t bytes)
++{
++ if (bytes != ETH_ALEN)
++ return -EINVAL;
++
++ if (index < 0)
++ return -EINVAL;
++
++ if (!is_valid_ether_addr(buf))
++ return -EINVAL;
++
++ eth_addr_add(buf, index);
++
++ return 0;
++}
++
++static const struct nvmem_cell_info sl28vpd_v1_entries[] = {
++ {
++ .name = "serial-number",
++ .offset = offsetof(struct sl28vpd_v1, serial_number),
++ .bytes = sizeof_field(struct sl28vpd_v1, serial_number),
++ },
++ {
++ .name = "base-mac-address",
++ .offset = offsetof(struct sl28vpd_v1, base_mac_address),
++ .bytes = sizeof_field(struct sl28vpd_v1, base_mac_address),
++ .read_post_process = sl28vpd_mac_address_pp,
++ },
++};
++
++static int sl28vpd_v1_check_crc(struct device *dev, struct nvmem_device *nvmem)
++{
++ struct sl28vpd_v1 data_v1;
++ u8 table[CRC8_TABLE_SIZE];
++ int ret;
++ u8 crc;
++
++ crc8_populate_msb(table, 0x07);
++
++ ret = nvmem_device_read(nvmem, 0, sizeof(data_v1), &data_v1);
++ if (ret < 0)
++ return ret;
++ else if (ret != sizeof(data_v1))
++ return -EIO;
++
++ crc = crc8(table, (void *)&data_v1, sizeof(data_v1) - 1, 0);
++
++ if (crc != data_v1.crc8) {
++ dev_err(dev,
++ "Checksum is invalid (got %02x, expected %02x).\n",
++ crc, data_v1.crc8);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem,
++ struct nvmem_layout *layout)
++{
++ const struct nvmem_cell_info *pinfo;
++ struct nvmem_cell_info info = {0};
++ struct device_node *layout_np;
++ struct sl28vpd_header hdr;
++ int ret, i;
++
++ /* check header */
++ ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
++ if (ret < 0)
++ return ret;
++ else if (ret != sizeof(hdr))
++ return -EIO;
++
++ if (hdr.magic != SL28VPD_MAGIC) {
++ dev_err(dev, "Invalid magic value (%02x)\n", hdr.magic);
++ return -EINVAL;
++ }
++
++ if (hdr.version != 1) {
++ dev_err(dev, "Version %d is unsupported.\n", hdr.version);
++ return -EINVAL;
++ }
++
++ ret = sl28vpd_v1_check_crc(dev, nvmem);
++ if (ret)
++ return ret;
++
++ layout_np = of_nvmem_layout_get_container(nvmem);
++ if (!layout_np)
++ return -ENOENT;
++
++ for (i = 0; i < ARRAY_SIZE(sl28vpd_v1_entries); i++) {
++ pinfo = &sl28vpd_v1_entries[i];
++
++ info.name = pinfo->name;
++ info.offset = pinfo->offset;
++ info.bytes = pinfo->bytes;
++ info.read_post_process = pinfo->read_post_process;
++ info.np = of_get_child_by_name(layout_np, pinfo->name);
++
++ ret = nvmem_add_one_cell(nvmem, &info);
++ if (ret) {
++ of_node_put(layout_np);
++ return ret;
++ }
++ }
++
++ of_node_put(layout_np);
++
++ return 0;
++}
++
++static const struct of_device_id sl28vpd_of_match_table[] = {
++ { .compatible = "kontron,sl28-vpd" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
++
++struct nvmem_layout sl28vpd_layout = {
++ .name = "sl28-vpd",
++ .of_match_table = sl28vpd_of_match_table,
++ .add_cells = sl28vpd_add_cells,
++};
++
++static int __init sl28vpd_init(void)
++{
++ return nvmem_layout_register(&sl28vpd_layout);
++}
++
++static void __exit sl28vpd_exit(void)
++{
++ nvmem_layout_unregister(&sl28vpd_layout);
++}
++
++module_init(sl28vpd_init);
++module_exit(sl28vpd_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
++MODULE_DESCRIPTION("NVMEM layout driver for the VPD of Kontron sl28 boards");
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0011-nvmem-layouts-onie-tlv-Add-new-layout-driver.patch b/target/linux/generic/backport-6.1/811-v6.4-0011-nvmem-layouts-onie-tlv-Add-new-layout-driver.patch
new file mode 100644
index 0000000000..ca8b4bc069
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0011-nvmem-layouts-onie-tlv-Add-new-layout-driver.patch
@@ -0,0 +1,306 @@
+From d3c0d12f6474216bf386101e2449cc73e5c5b61d Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:31 +0100
+Subject: [PATCH] nvmem: layouts: onie-tlv: Add new layout driver
+
+This layout applies on top of any non volatile storage device containing
+an ONIE table factory flashed. This table follows the tlv
+(type-length-value) organization described in the link below. We cannot
+afford using regular parsers because the content of these tables is
+manufacturer specific and must be dynamically discovered.
+
+Link: https://opencomputeproject.github.io/onie/design-spec/hw_requirements.html
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-24-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts/Kconfig | 9 ++
+ drivers/nvmem/layouts/Makefile | 1 +
+ drivers/nvmem/layouts/onie-tlv.c | 257 +++++++++++++++++++++++++++++++
+ 3 files changed, 267 insertions(+)
+ create mode 100644 drivers/nvmem/layouts/onie-tlv.c
+
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -11,4 +11,13 @@ config NVMEM_LAYOUT_SL28_VPD
+
+ If unsure, say N.
+
++config NVMEM_LAYOUT_ONIE_TLV
++ tristate "ONIE tlv support"
++ select CRC32
++ help
++ Say Y here if you want to support the Open Compute Project ONIE
++ Type-Length-Value standard table.
++
++ If unsure, say N.
++
+ endmenu
+--- a/drivers/nvmem/layouts/Makefile
++++ b/drivers/nvmem/layouts/Makefile
+@@ -4,3 +4,4 @@
+ #
+
+ obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
++obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
+--- /dev/null
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -0,0 +1,257 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * ONIE tlv NVMEM cells provider
++ *
++ * Copyright (C) 2022 Open Compute Group ONIE
++ * Author: Miquel Raynal <miquel.raynal@bootlin.com>
++ * Based on the nvmem driver written by: Vadym Kochan <vadym.kochan@plvision.eu>
++ * Inspired by the first layout written by: Rafał Miłecki <rafal@milecki.pl>
++ */
++
++#include <linux/crc32.h>
++#include <linux/etherdevice.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++
++#define ONIE_TLV_MAX_LEN 2048
++#define ONIE_TLV_CRC_FIELD_SZ 6
++#define ONIE_TLV_CRC_SZ 4
++#define ONIE_TLV_HDR_ID "TlvInfo"
++
++struct onie_tlv_hdr {
++ u8 id[8];
++ u8 version;
++ __be16 data_len;
++} __packed;
++
++struct onie_tlv {
++ u8 type;
++ u8 len;
++} __packed;
++
++static const char *onie_tlv_cell_name(u8 type)
++{
++ switch (type) {
++ case 0x21:
++ return "product-name";
++ case 0x22:
++ return "part-number";
++ case 0x23:
++ return "serial-number";
++ case 0x24:
++ return "mac-address";
++ case 0x25:
++ return "manufacture-date";
++ case 0x26:
++ return "device-version";
++ case 0x27:
++ return "label-revision";
++ case 0x28:
++ return "platform-name";
++ case 0x29:
++ return "onie-version";
++ case 0x2A:
++ return "num-macs";
++ case 0x2B:
++ return "manufacturer";
++ case 0x2C:
++ return "country-code";
++ case 0x2D:
++ return "vendor";
++ case 0x2E:
++ return "diag-version";
++ case 0x2F:
++ return "service-tag";
++ case 0xFD:
++ return "vendor-extension";
++ case 0xFE:
++ return "crc32";
++ default:
++ break;
++ }
++
++ return NULL;
++}
++
++static int onie_tlv_mac_read_cb(void *priv, const char *id, int index,
++ unsigned int offset, void *buf,
++ size_t bytes)
++{
++ eth_addr_add(buf, index);
++
++ return 0;
++}
++
++static nvmem_cell_post_process_t onie_tlv_read_cb(u8 type, u8 *buf)
++{
++ switch (type) {
++ case 0x24:
++ return &onie_tlv_mac_read_cb;
++ default:
++ break;
++ }
++
++ return NULL;
++}
++
++static int onie_tlv_add_cells(struct device *dev, struct nvmem_device *nvmem,
++ size_t data_len, u8 *data)
++{
++ struct nvmem_cell_info cell = {};
++ struct device_node *layout;
++ struct onie_tlv tlv;
++ unsigned int hdr_len = sizeof(struct onie_tlv_hdr);
++ unsigned int offset = 0;
++ int ret;
++
++ layout = of_nvmem_layout_get_container(nvmem);
++ if (!layout)
++ return -ENOENT;
++
++ while (offset < data_len) {
++ memcpy(&tlv, data + offset, sizeof(tlv));
++ if (offset + tlv.len >= data_len) {
++ dev_err(dev, "Out of bounds field (0x%x bytes at 0x%x)\n",
++ tlv.len, hdr_len + offset);
++ break;
++ }
++
++ cell.name = onie_tlv_cell_name(tlv.type);
++ if (!cell.name)
++ continue;
++
++ cell.offset = hdr_len + offset + sizeof(tlv.type) + sizeof(tlv.len);
++ cell.bytes = tlv.len;
++ cell.np = of_get_child_by_name(layout, cell.name);
++ cell.read_post_process = onie_tlv_read_cb(tlv.type, data + offset + sizeof(tlv));
++
++ ret = nvmem_add_one_cell(nvmem, &cell);
++ if (ret) {
++ of_node_put(layout);
++ return ret;
++ }
++
++ offset += sizeof(tlv) + tlv.len;
++ }
++
++ of_node_put(layout);
++
++ return 0;
++}
++
++static bool onie_tlv_hdr_is_valid(struct device *dev, struct onie_tlv_hdr *hdr)
++{
++ if (memcmp(hdr->id, ONIE_TLV_HDR_ID, sizeof(hdr->id))) {
++ dev_err(dev, "Invalid header\n");
++ return false;
++ }
++
++ if (hdr->version != 0x1) {
++ dev_err(dev, "Invalid version number\n");
++ return false;
++ }
++
++ return true;
++}
++
++static bool onie_tlv_crc_is_valid(struct device *dev, size_t table_len, u8 *table)
++{
++ struct onie_tlv crc_hdr;
++ u32 read_crc, calc_crc;
++ __be32 crc_be;
++
++ memcpy(&crc_hdr, table + table_len - ONIE_TLV_CRC_FIELD_SZ, sizeof(crc_hdr));
++ if (crc_hdr.type != 0xfe || crc_hdr.len != ONIE_TLV_CRC_SZ) {
++ dev_err(dev, "Invalid CRC field\n");
++ return false;
++ }
++
++ /* The table contains a JAMCRC, which is XOR'ed compared to the original
++ * CRC32 implementation as known in the Ethernet world.
++ */
++ memcpy(&crc_be, table + table_len - ONIE_TLV_CRC_SZ, ONIE_TLV_CRC_SZ);
++ read_crc = be32_to_cpu(crc_be);
++ calc_crc = crc32(~0, table, table_len - ONIE_TLV_CRC_SZ) ^ 0xFFFFFFFF;
++ if (read_crc != calc_crc) {
++ dev_err(dev, "Invalid CRC read: 0x%08x, expected: 0x%08x\n",
++ read_crc, calc_crc);
++ return false;
++ }
++
++ return true;
++}
++
++static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem,
++ struct nvmem_layout *layout)
++{
++ struct onie_tlv_hdr hdr;
++ size_t table_len, data_len, hdr_len;
++ u8 *table, *data;
++ int ret;
++
++ ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
++ if (ret < 0)
++ return ret;
++
++ if (!onie_tlv_hdr_is_valid(dev, &hdr)) {
++ dev_err(dev, "Invalid ONIE TLV header\n");
++ return -EINVAL;
++ }
++
++ hdr_len = sizeof(hdr.id) + sizeof(hdr.version) + sizeof(hdr.data_len);
++ data_len = be16_to_cpu(hdr.data_len);
++ table_len = hdr_len + data_len;
++ if (table_len > ONIE_TLV_MAX_LEN) {
++ dev_err(dev, "Invalid ONIE TLV data length\n");
++ return -EINVAL;
++ }
++
++ table = devm_kmalloc(dev, table_len, GFP_KERNEL);
++ if (!table)
++ return -ENOMEM;
++
++ ret = nvmem_device_read(nvmem, 0, table_len, table);
++ if (ret != table_len)
++ return ret;
++
++ if (!onie_tlv_crc_is_valid(dev, table_len, table))
++ return -EINVAL;
++
++ data = table + hdr_len;
++ ret = onie_tlv_add_cells(dev, nvmem, data_len, data);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static const struct of_device_id onie_tlv_of_match_table[] = {
++ { .compatible = "onie,tlv-layout", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table);
++
++static struct nvmem_layout onie_tlv_layout = {
++ .name = "ONIE tlv layout",
++ .of_match_table = onie_tlv_of_match_table,
++ .add_cells = onie_tlv_parse_table,
++};
++
++static int __init onie_tlv_init(void)
++{
++ return nvmem_layout_register(&onie_tlv_layout);
++}
++
++static void __exit onie_tlv_exit(void)
++{
++ nvmem_layout_unregister(&onie_tlv_layout);
++}
++
++module_init(onie_tlv_init);
++module_exit(onie_tlv_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
++MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing");
++MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing");
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0012-nvmem-stm32-romem-mark-OF-related-data-as-maybe-unus.patch b/target/linux/generic/backport-6.1/811-v6.4-0012-nvmem-stm32-romem-mark-OF-related-data-as-maybe-unus.patch
new file mode 100644
index 0000000000..94a0911d73
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0012-nvmem-stm32-romem-mark-OF-related-data-as-maybe-unus.patch
@@ -0,0 +1,32 @@
+From a4fb434ef96ace5af758ca2c52c3a3f8f3abc87c Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Date: Tue, 4 Apr 2023 18:21:34 +0100
+Subject: [PATCH] nvmem: stm32-romem: mark OF related data as maybe unused
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The driver can be compile tested with !CONFIG_OF making certain data
+unused:
+
+ drivers/nvmem/stm32-romem.c:271:34: error: ‘stm32_romem_of_match’ defined but not used [-Werror=unused-const-variable=]
+
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-27-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/stm32-romem.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -268,7 +268,7 @@ static const struct stm32_romem_cfg stm3
+ .ta = true,
+ };
+
+-static const struct of_device_id stm32_romem_of_match[] = {
++static const struct of_device_id stm32_romem_of_match[] __maybe_unused = {
+ { .compatible = "st,stm32f4-otp", }, {
+ .compatible = "st,stm32mp15-bsec",
+ .data = (void *)&stm32mp15_bsec_cfg,
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0013-nvmem-mtk-efuse-Support-postprocessing-for-GPU-speed.patch b/target/linux/generic/backport-6.1/811-v6.4-0013-nvmem-mtk-efuse-Support-postprocessing-for-GPU-speed.patch
new file mode 100644
index 0000000000..abda402bdd
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0013-nvmem-mtk-efuse-Support-postprocessing-for-GPU-speed.patch
@@ -0,0 +1,120 @@
+From de6e05097f7db066afb0ad4c88b730949f7b7749 Mon Sep 17 00:00:00 2001
+From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Date: Tue, 4 Apr 2023 18:21:35 +0100
+Subject: [PATCH] nvmem: mtk-efuse: Support postprocessing for GPU speed
+ binning data
+
+On some MediaTek SoCs GPU speed binning data is available for read
+in the SoC's eFuse array but it has a format that is incompatible
+with what the OPP API expects, as we read a number from 0 to 7 but
+opp-supported-hw is expecting a bitmask to enable an OPP entry:
+being what we read limited to 0-7, it's straightforward to simply
+convert the value to BIT(value) as a post-processing action.
+
+So, introduce post-processing support and enable it by evaluating
+the newly introduced platform data's `uses_post_processing` member,
+currently enabled only for MT8186.
+
+Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-28-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 53 +++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 51 insertions(+), 2 deletions(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -10,6 +10,11 @@
+ #include <linux/io.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/platform_device.h>
++#include <linux/property.h>
++
++struct mtk_efuse_pdata {
++ bool uses_post_processing;
++};
+
+ struct mtk_efuse_priv {
+ void __iomem *base;
+@@ -29,6 +34,37 @@ static int mtk_reg_read(void *context,
+ return 0;
+ }
+
++static int mtk_efuse_gpu_speedbin_pp(void *context, const char *id, int index,
++ unsigned int offset, void *data, size_t bytes)
++{
++ u8 *val = data;
++
++ if (val[0] < 8)
++ val[0] = BIT(val[0]);
++
++ return 0;
++}
++
++static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem,
++ struct nvmem_layout *layout,
++ struct nvmem_cell_info *cell)
++{
++ size_t sz = strlen(cell->name);
++
++ /*
++ * On some SoCs, the GPU speedbin is not read as bitmask but as
++ * a number with range [0-7] (max 3 bits): post process to use
++ * it in OPP tables to describe supported-hw.
++ */
++ if (cell->nbits <= 3 &&
++ strncmp(cell->name, "gpu-speedbin", min(sz, strlen("gpu-speedbin"))) == 0)
++ cell->read_post_process = mtk_efuse_gpu_speedbin_pp;
++}
++
++static struct nvmem_layout mtk_efuse_layout = {
++ .fixup_cell_info = mtk_efuse_fixup_cell_info,
++};
++
+ static int mtk_efuse_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -36,6 +72,7 @@ static int mtk_efuse_probe(struct platfo
+ struct nvmem_device *nvmem;
+ struct nvmem_config econfig = {};
+ struct mtk_efuse_priv *priv;
++ const struct mtk_efuse_pdata *pdata;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -45,20 +82,32 @@ static int mtk_efuse_probe(struct platfo
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
++ pdata = device_get_match_data(dev);
+ econfig.stride = 1;
+ econfig.word_size = 1;
+ econfig.reg_read = mtk_reg_read;
+ econfig.size = resource_size(res);
+ econfig.priv = priv;
+ econfig.dev = dev;
++ if (pdata->uses_post_processing)
++ econfig.layout = &mtk_efuse_layout;
+ nvmem = devm_nvmem_register(dev, &econfig);
+
+ return PTR_ERR_OR_ZERO(nvmem);
+ }
+
++static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = {
++ .uses_post_processing = true,
++};
++
++static const struct mtk_efuse_pdata mtk_efuse_pdata = {
++ .uses_post_processing = false,
++};
++
+ static const struct of_device_id mtk_efuse_of_match[] = {
+- { .compatible = "mediatek,mt8173-efuse",},
+- { .compatible = "mediatek,efuse",},
++ { .compatible = "mediatek,mt8173-efuse", .data = &mtk_efuse_pdata },
++ { .compatible = "mediatek,mt8186-efuse", .data = &mtk_mt8186_efuse_pdata },
++ { .compatible = "mediatek,efuse", .data = &mtk_efuse_pdata },
+ {/* sentinel */},
+ };
+ MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0014-nvmem-bcm-ocotp-Use-devm_platform_ioremap_resource.patch b/target/linux/generic/backport-6.1/811-v6.4-0014-nvmem-bcm-ocotp-Use-devm_platform_ioremap_resource.patch
new file mode 100644
index 0000000000..a0874f73d1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0014-nvmem-bcm-ocotp-Use-devm_platform_ioremap_resource.patch
@@ -0,0 +1,39 @@
+From 1dc552fa33cf98af3e784dbc0500da93cae3b24a Mon Sep 17 00:00:00 2001
+From: Yang Li <yang.lee@linux.alibaba.com>
+Date: Tue, 4 Apr 2023 18:21:38 +0100
+Subject: [PATCH] nvmem: bcm-ocotp: Use devm_platform_ioremap_resource()
+
+According to commit 7945f929f1a7 ("drivers: provide
+devm_platform_ioremap_resource()"), convert platform_get_resource(),
+devm_ioremap_resource() to a single call to use
+devm_platform_ioremap_resource(), as this is exactly what this function
+does.
+
+Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-31-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/bcm-ocotp.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/nvmem/bcm-ocotp.c
++++ b/drivers/nvmem/bcm-ocotp.c
+@@ -254,7 +254,6 @@ MODULE_DEVICE_TABLE(acpi, bcm_otpc_acpi_
+ static int bcm_otpc_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+- struct resource *res;
+ struct otpc_priv *priv;
+ struct nvmem_device *nvmem;
+ int err;
+@@ -269,8 +268,7 @@ static int bcm_otpc_probe(struct platfor
+ return -ENODEV;
+
+ /* Get OTP base address register. */
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- priv->base = devm_ioremap_resource(dev, res);
++ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base)) {
+ dev_err(dev, "unable to map I/O memory\n");
+ return PTR_ERR(priv->base);
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0015-nvmem-nintendo-otp-Use-devm_platform_ioremap_resourc.patch b/target/linux/generic/backport-6.1/811-v6.4-0015-nvmem-nintendo-otp-Use-devm_platform_ioremap_resourc.patch
new file mode 100644
index 0000000000..890dacd08d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0015-nvmem-nintendo-otp-Use-devm_platform_ioremap_resourc.patch
@@ -0,0 +1,39 @@
+From 649409990d2e93fac657be7c6960c28a2c601d65 Mon Sep 17 00:00:00 2001
+From: Yang Li <yang.lee@linux.alibaba.com>
+Date: Tue, 4 Apr 2023 18:21:39 +0100
+Subject: [PATCH] nvmem: nintendo-otp: Use devm_platform_ioremap_resource()
+
+According to commit 7945f929f1a7 ("drivers: provide
+devm_platform_ioremap_resource()"), convert platform_get_resource(),
+devm_ioremap_resource() to a single call to use
+devm_platform_ioremap_resource(), as this is exactly what this function
+does.
+
+Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-32-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/nintendo-otp.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/nvmem/nintendo-otp.c
++++ b/drivers/nvmem/nintendo-otp.c
+@@ -76,7 +76,6 @@ static int nintendo_otp_probe(struct pla
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id =
+ of_match_device(nintendo_otp_of_table, dev);
+- struct resource *res;
+ struct nvmem_device *nvmem;
+ struct nintendo_otp_priv *priv;
+
+@@ -92,8 +91,7 @@ static int nintendo_otp_probe(struct pla
+ if (!priv)
+ return -ENOMEM;
+
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- priv->regs = devm_ioremap_resource(dev, res);
++ priv->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0016-nvmem-vf610-ocotp-Use-devm_platform_get_and_ioremap_.patch b/target/linux/generic/backport-6.1/811-v6.4-0016-nvmem-vf610-ocotp-Use-devm_platform_get_and_ioremap_.patch
new file mode 100644
index 0000000000..3f5d3c1ad4
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0016-nvmem-vf610-ocotp-Use-devm_platform_get_and_ioremap_.patch
@@ -0,0 +1,32 @@
+From c2367aa60d5e34d48582362c6de34b4131d92be7 Mon Sep 17 00:00:00 2001
+From: Yang Li <yang.lee@linux.alibaba.com>
+Date: Tue, 4 Apr 2023 18:21:40 +0100
+Subject: [PATCH] nvmem: vf610-ocotp: Use
+ devm_platform_get_and_ioremap_resource()
+
+According to commit 890cc39a8799 ("drivers: provide
+devm_platform_get_and_ioremap_resource()"), convert
+platform_get_resource(), devm_ioremap_resource() to a single
+call to devm_platform_get_and_ioremap_resource(), as this is exactly
+what this function does.
+
+Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-33-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/vf610-ocotp.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/nvmem/vf610-ocotp.c
++++ b/drivers/nvmem/vf610-ocotp.c
+@@ -219,8 +219,7 @@ static int vf610_ocotp_probe(struct plat
+ if (!ocotp_dev)
+ return -ENOMEM;
+
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- ocotp_dev->base = devm_ioremap_resource(dev, res);
++ ocotp_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(ocotp_dev->base))
+ return PTR_ERR(ocotp_dev->base);
+
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0017-nvmem-core-support-specifying-both-cell-raw-data-pos.patch b/target/linux/generic/backport-6.1/811-v6.4-0017-nvmem-core-support-specifying-both-cell-raw-data-pos.patch
new file mode 100644
index 0000000000..eeb407e9bb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0017-nvmem-core-support-specifying-both-cell-raw-data-pos.patch
@@ -0,0 +1,115 @@
+From 55d4980ce55b6bb4be66877de4dbec513911b988 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 4 Apr 2023 18:21:42 +0100
+Subject: [PATCH] nvmem: core: support specifying both: cell raw data & post
+ read lengths
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Callback .read_post_process() is designed to modify raw cell content
+before providing it to the consumer. So far we were dealing with
+modifications that didn't affect cell size (length). In some cases
+however cell content needs to be reformatted and resized.
+
+It's required e.g. to provide properly formatted MAC address in case
+it's stored in a non-binary format (e.g. using ASCII).
+
+There were few discussions how to optimally handle that. Following
+possible solutions were considered:
+1. Allow .read_post_process() to realloc (resize) content buffer
+2. Allow .read_post_process() to adjust (decrease) just buffer length
+3. Register NVMEM cells using post-read sizes
+
+The preferred solution was the last one. The problem is that simply
+adjusting "bytes" in NVMEM providers would result in core code NOT
+passing whole raw data to .read_post_process() callbacks. It means
+callback functions couldn't do their job without somehow manually
+reading original cell content on their own.
+
+This patch deals with that by registering NVMEM cells with both lengths:
+raw content one and post read one. It allows:
+1. Core code to read whole raw cell content
+2. Callbacks to return content they want
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-35-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 11 +++++++----
+ include/linux/nvmem-provider.h | 2 ++
+ 2 files changed, 9 insertions(+), 4 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -50,6 +50,7 @@ struct nvmem_device {
+ struct nvmem_cell_entry {
+ const char *name;
+ int offset;
++ size_t raw_len;
+ int bytes;
+ int bit_offset;
+ int nbits;
+@@ -469,6 +470,7 @@ static int nvmem_cell_info_to_nvmem_cell
+ {
+ cell->nvmem = nvmem;
+ cell->offset = info->offset;
++ cell->raw_len = info->raw_len ?: info->bytes;
+ cell->bytes = info->bytes;
+ cell->name = info->name;
+ cell->read_post_process = info->read_post_process;
+@@ -1560,7 +1562,7 @@ static int __nvmem_cell_read(struct nvme
+ {
+ int rc;
+
+- rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes);
++ rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->raw_len);
+
+ if (rc)
+ return rc;
+@@ -1571,7 +1573,7 @@ static int __nvmem_cell_read(struct nvme
+
+ if (cell->read_post_process) {
+ rc = cell->read_post_process(cell->priv, id, index,
+- cell->offset, buf, cell->bytes);
++ cell->offset, buf, cell->raw_len);
+ if (rc)
+ return rc;
+ }
+@@ -1594,14 +1596,15 @@ static int __nvmem_cell_read(struct nvme
+ */
+ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
+ {
+- struct nvmem_device *nvmem = cell->entry->nvmem;
++ struct nvmem_cell_entry *entry = cell->entry;
++ struct nvmem_device *nvmem = entry->nvmem;
+ u8 *buf;
+ int rc;
+
+ if (!nvmem)
+ return ERR_PTR(-EINVAL);
+
+- buf = kzalloc(cell->entry->bytes, GFP_KERNEL);
++ buf = kzalloc(max_t(size_t, entry->raw_len, entry->bytes), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -51,6 +51,7 @@ struct nvmem_keepout {
+ * struct nvmem_cell_info - NVMEM cell description
+ * @name: Name.
+ * @offset: Offset within the NVMEM device.
++ * @raw_len: Length of raw data (without post processing).
+ * @bytes: Length of the cell.
+ * @bit_offset: Bit offset if cell is smaller than a byte.
+ * @nbits: Number of bits.
+@@ -62,6 +63,7 @@ struct nvmem_keepout {
+ struct nvmem_cell_info {
+ const char *name;
+ unsigned int offset;
++ size_t raw_len;
+ unsigned int bytes;
+ unsigned int bit_offset;
+ unsigned int nbits;
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0018-nvmem-u-boot-env-post-process-ethaddr-env-variable.patch b/target/linux/generic/backport-6.1/811-v6.4-0018-nvmem-u-boot-env-post-process-ethaddr-env-variable.patch
new file mode 100644
index 0000000000..adde0ffc4b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0018-nvmem-u-boot-env-post-process-ethaddr-env-variable.patch
@@ -0,0 +1,81 @@
+From c49f1a8af6bcf6d18576bca898f8083ca4b129e1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 4 Apr 2023 18:21:43 +0100
+Subject: [PATCH] nvmem: u-boot-env: post-process "ethaddr" env variable
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+U-Boot environment variables are stored in ASCII format so "ethaddr"
+requires parsing into binary to make it work with Ethernet interfaces.
+
+This includes support for indexes to support #nvmem-cell-cells = <1>.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-36-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig | 1 +
+ drivers/nvmem/u-boot-env.c | 26 ++++++++++++++++++++++++++
+ 2 files changed, 27 insertions(+)
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -340,6 +340,7 @@ config NVMEM_U_BOOT_ENV
+ tristate "U-Boot environment variables support"
+ depends on OF && MTD
+ select CRC32
++ select GENERIC_NET_UTILS
+ help
+ U-Boot stores its setup as environment variables. This driver adds
+ support for verifying & exporting such data. It also exposes variables
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -4,6 +4,8 @@
+ */
+
+ #include <linux/crc32.h>
++#include <linux/etherdevice.h>
++#include <linux/if_ether.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+@@ -70,6 +72,25 @@ static int u_boot_env_read(void *context
+ return 0;
+ }
+
++static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
++ unsigned int offset, void *buf, size_t bytes)
++{
++ u8 mac[ETH_ALEN];
++
++ if (bytes != 3 * ETH_ALEN - 1)
++ return -EINVAL;
++
++ if (!mac_pton(buf, mac))
++ return -EINVAL;
++
++ if (index)
++ eth_addr_add(mac, index);
++
++ ether_addr_copy(buf, mac);
++
++ return 0;
++}
++
+ static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf,
+ size_t data_offset, size_t data_len)
+ {
+@@ -101,6 +122,11 @@ static int u_boot_env_add_cells(struct u
+ priv->cells[idx].offset = data_offset + value - data;
+ priv->cells[idx].bytes = strlen(value);
+ priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
++ if (!strcmp(var, "ethaddr")) {
++ priv->cells[idx].raw_len = strlen(value);
++ priv->cells[idx].bytes = ETH_ALEN;
++ priv->cells[idx].read_post_process = u_boot_env_read_post_process_ethaddr;
++ }
+ }
+
+ if (WARN_ON(idx != priv->ncells))
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0019-nvmem-Add-macro-to-register-nvmem-layout-drivers.patch b/target/linux/generic/backport-6.1/811-v6.4-0019-nvmem-Add-macro-to-register-nvmem-layout-drivers.patch
new file mode 100644
index 0000000000..7c6fe22b5f
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0019-nvmem-Add-macro-to-register-nvmem-layout-drivers.patch
@@ -0,0 +1,42 @@
+From 814c978f02db17f16e6aa2efa2a929372f06da09 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:44 +0100
+Subject: [PATCH] nvmem: Add macro to register nvmem layout drivers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Provide a module_nvmem_layout_driver() macro at the end of the
+nvmem-provider.h header to reduce the boilerplate when registering nvmem
+layout drivers.
+
+Suggested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Acked-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-37-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/nvmem-provider.h | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -9,6 +9,7 @@
+ #ifndef _LINUX_NVMEM_PROVIDER_H
+ #define _LINUX_NVMEM_PROVIDER_H
+
++#include <linux/device/driver.h>
+ #include <linux/err.h>
+ #include <linux/errno.h>
+ #include <linux/gpio/consumer.h>
+@@ -242,4 +243,9 @@ nvmem_layout_get_match_data(struct nvmem
+ }
+
+ #endif /* CONFIG_NVMEM */
++
++#define module_nvmem_layout_driver(__layout_driver) \
++ module_driver(__layout_driver, nvmem_layout_register, \
++ nvmem_layout_unregister)
++
+ #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0020-nvmem-layouts-sl28vpd-Use-module_nvmem_layout_driver.patch b/target/linux/generic/backport-6.1/811-v6.4-0020-nvmem-layouts-sl28vpd-Use-module_nvmem_layout_driver.patch
new file mode 100644
index 0000000000..06646dd68b
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0020-nvmem-layouts-sl28vpd-Use-module_nvmem_layout_driver.patch
@@ -0,0 +1,39 @@
+From 0abdf99fe0c86252ba274703425f8d543d7e7f0d Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:45 +0100
+Subject: [PATCH] nvmem: layouts: sl28vpd: Use module_nvmem_layout_driver()
+
+Stop open-coding the module init/exit functions. Use the
+module_nvmem_layout_driver() instead.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-38-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts/sl28vpd.c | 14 +-------------
+ 1 file changed, 1 insertion(+), 13 deletions(-)
+
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -146,19 +146,7 @@ struct nvmem_layout sl28vpd_layout = {
+ .of_match_table = sl28vpd_of_match_table,
+ .add_cells = sl28vpd_add_cells,
+ };
+-
+-static int __init sl28vpd_init(void)
+-{
+- return nvmem_layout_register(&sl28vpd_layout);
+-}
+-
+-static void __exit sl28vpd_exit(void)
+-{
+- nvmem_layout_unregister(&sl28vpd_layout);
+-}
+-
+-module_init(sl28vpd_init);
+-module_exit(sl28vpd_exit);
++module_nvmem_layout_driver(sl28vpd_layout);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0021-nvmem-layouts-onie-tlv-Use-module_nvmem_layout_drive.patch b/target/linux/generic/backport-6.1/811-v6.4-0021-nvmem-layouts-onie-tlv-Use-module_nvmem_layout_drive.patch
new file mode 100644
index 0000000000..826f4378c2
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0021-nvmem-layouts-onie-tlv-Use-module_nvmem_layout_drive.patch
@@ -0,0 +1,39 @@
+From d119eb38faab61125aaa4f63c74eef61585cf34c Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:46 +0100
+Subject: [PATCH] nvmem: layouts: onie-tlv: Use module_nvmem_layout_driver()
+
+Stop open-coding the module init/exit functions. Use the
+module_nvmem_layout_driver() instead.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-39-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts/onie-tlv.c | 14 +-------------
+ 1 file changed, 1 insertion(+), 13 deletions(-)
+
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -237,19 +237,7 @@ static struct nvmem_layout onie_tlv_layo
+ .of_match_table = onie_tlv_of_match_table,
+ .add_cells = onie_tlv_parse_table,
+ };
+-
+-static int __init onie_tlv_init(void)
+-{
+- return nvmem_layout_register(&onie_tlv_layout);
+-}
+-
+-static void __exit onie_tlv_exit(void)
+-{
+- nvmem_layout_unregister(&onie_tlv_layout);
+-}
+-
+-module_init(onie_tlv_init);
+-module_exit(onie_tlv_exit);
++module_nvmem_layout_driver(onie_tlv_layout);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0022-nvmem-layouts-onie-tlv-Drop-wrong-module-alias.patch b/target/linux/generic/backport-6.1/811-v6.4-0022-nvmem-layouts-onie-tlv-Drop-wrong-module-alias.patch
new file mode 100644
index 0000000000..f20db85ceb
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0022-nvmem-layouts-onie-tlv-Drop-wrong-module-alias.patch
@@ -0,0 +1,24 @@
+From 6b13e4b6a9a45028ac730e550380077df1845912 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:47 +0100
+Subject: [PATCH] nvmem: layouts: onie-tlv: Drop wrong module alias
+
+The MODULE_ALIAS macro is misused here as it carries the
+description. There is currently no relevant alias to provide so let's
+just drop it.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-40-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts/onie-tlv.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -242,4 +242,3 @@ module_nvmem_layout_driver(onie_tlv_layo
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
+ MODULE_DESCRIPTION("NVMEM layout driver for Onie TLV table parsing");
+-MODULE_ALIAS("NVMEM layout driver for Onie TLV table parsing");
diff --git a/target/linux/generic/backport-6.1/811-v6.4-0023-nvmem-layouts-sl28vpd-set-varaiable-sl28vpd_layout-s.patch b/target/linux/generic/backport-6.1/811-v6.4-0023-nvmem-layouts-sl28vpd-set-varaiable-sl28vpd_layout-s.patch
new file mode 100644
index 0000000000..5cf847b57a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/811-v6.4-0023-nvmem-layouts-sl28vpd-set-varaiable-sl28vpd_layout-s.patch
@@ -0,0 +1,31 @@
+From a8642cd11635a35a5f1dc31857887900d6610778 Mon Sep 17 00:00:00 2001
+From: Tom Rix <trix@redhat.com>
+Date: Tue, 4 Apr 2023 18:21:48 +0100
+Subject: [PATCH] nvmem: layouts: sl28vpd: set varaiable sl28vpd_layout
+ storage-class-specifier to static
+
+smatch reports
+drivers/nvmem/layouts/sl28vpd.c:144:21: warning: symbol
+ 'sl28vpd_layout' was not declared. Should it be static?
+
+This variable is only used in one file so it should be static.
+
+Signed-off-by: Tom Rix <trix@redhat.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-41-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts/sl28vpd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -141,7 +141,7 @@ static const struct of_device_id sl28vpd
+ };
+ MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
+
+-struct nvmem_layout sl28vpd_layout = {
++static struct nvmem_layout sl28vpd_layout = {
+ .name = "sl28-vpd",
+ .of_match_table = sl28vpd_of_match_table,
+ .add_cells = sl28vpd_add_cells,
diff --git a/target/linux/generic/backport-6.1/812-v6.2-firmware-nvram-bcm47xx-support-init-from-IO-memory.patch b/target/linux/generic/backport-6.1/812-v6.2-firmware-nvram-bcm47xx-support-init-from-IO-memory.patch
new file mode 100644
index 0000000000..13ee2d4229
--- /dev/null
+++ b/target/linux/generic/backport-6.1/812-v6.2-firmware-nvram-bcm47xx-support-init-from-IO-memory.patch
@@ -0,0 +1,100 @@
+From a5be5ce0e25439fae3cd42e3d775979547926812 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 3 Nov 2022 09:25:29 +0100
+Subject: [PATCH] firmware/nvram: bcm47xx: support init from IO memory
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Provide NVMEM content to the NVRAM driver from a simple
+memory resource. This is necessary to use NVRAM in a memory-
+mapped flash device. Patch taken from OpenWrts development
+tree.
+
+This patch makes it possible to use memory-mapped NVRAM
+on the D-Link DWL-8610AP and the D-Link DIR-890L.
+
+Cc: Hauke Mehrtens <hauke@hauke-m.de>
+Cc: linux-mips@vger.kernel.org
+Cc: Florian Fainelli <f.fainelli@gmail.com>
+Cc: bcm-kernel-feedback-list@broadcom.com
+Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+[Added an export for modules potentially using the init symbol]
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20221103082529.359084-1-linus.walleij@linaro.org
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ drivers/firmware/broadcom/bcm47xx_nvram.c | 18 ++++++++++++++++++
+ drivers/nvmem/brcm_nvram.c | 3 +++
+ include/linux/bcm47xx_nvram.h | 6 ++++++
+ 3 files changed, 27 insertions(+)
+
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -110,6 +110,24 @@ found:
+ return 0;
+ }
+
++int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start, size_t res_size)
++{
++ if (nvram_len) {
++ pr_warn("nvram already initialized\n");
++ return -EEXIST;
++ }
++
++ if (!bcm47xx_nvram_is_valid(nvram_start)) {
++ pr_err("No valid NVRAM found\n");
++ return -ENOENT;
++ }
++
++ bcm47xx_nvram_copy(nvram_start, res_size);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(bcm47xx_nvram_init_from_iomem);
++
+ /*
+ * On bcm47xx we need access to the NVRAM very early, so we can't use mtd
+ * subsystem to access flash. We can't even use platform device / driver to
+--- a/drivers/nvmem/brcm_nvram.c
++++ b/drivers/nvmem/brcm_nvram.c
+@@ -3,6 +3,7 @@
+ * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
+ */
+
++#include <linux/bcm47xx_nvram.h>
+ #include <linux/io.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
+@@ -139,6 +140,8 @@ static int brcm_nvram_probe(struct platf
+ if (err)
+ return err;
+
++ bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res));
++
+ config.dev = dev;
+ config.cells = priv->cells;
+ config.ncells = priv->ncells;
+--- a/include/linux/bcm47xx_nvram.h
++++ b/include/linux/bcm47xx_nvram.h
+@@ -11,6 +11,7 @@
+ #include <linux/vmalloc.h>
+
+ #ifdef CONFIG_BCM47XX_NVRAM
++int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start, size_t res_size);
+ int bcm47xx_nvram_init_from_mem(u32 base, u32 lim);
+ int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len);
+ int bcm47xx_nvram_gpio_pin(const char *name);
+@@ -20,6 +21,11 @@ static inline void bcm47xx_nvram_release
+ vfree(nvram);
+ };
+ #else
++static inline int bcm47xx_nvram_init_from_iomem(void __iomem *nvram_start,
++ size_t res_size)
++{
++ return -ENOTSUPP;
++}
+ static inline int bcm47xx_nvram_init_from_mem(u32 base, u32 lim)
+ {
+ return -ENOTSUPP;
diff --git a/target/linux/generic/backport-6.1/813-v6.5-nvmem-brcm_nvram-add-.read_post_process-for-MACs.patch b/target/linux/generic/backport-6.1/813-v6.5-nvmem-brcm_nvram-add-.read_post_process-for-MACs.patch
new file mode 100644
index 0000000000..8749cad7f7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/813-v6.5-nvmem-brcm_nvram-add-.read_post_process-for-MACs.patch
@@ -0,0 +1,77 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 6 Apr 2023 12:46:16 +0200
+Subject: [PATCH] nvmem: brcm_nvram: add .read_post_process() for MACs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+1. Parse ASCII MAC format into byte based
+2. Calculate relative addresses based on index argument
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+ drivers/nvmem/Kconfig | 1 +
+ drivers/nvmem/brcm_nvram.c | 28 ++++++++++++++++++++++++++++
+ 2 files changed, 29 insertions(+)
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -55,6 +55,7 @@ config NVMEM_BRCM_NVRAM
+ tristate "Broadcom's NVRAM support"
+ depends on ARCH_BCM_5301X || COMPILE_TEST
+ depends on HAS_IOMEM
++ select GENERIC_NET_UTILS
+ help
+ This driver provides support for Broadcom's NVRAM that can be accessed
+ using I/O mapping.
+--- a/drivers/nvmem/brcm_nvram.c
++++ b/drivers/nvmem/brcm_nvram.c
+@@ -4,6 +4,8 @@
+ */
+
+ #include <linux/bcm47xx_nvram.h>
++#include <linux/etherdevice.h>
++#include <linux/if_ether.h>
+ #include <linux/io.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
+@@ -42,6 +44,25 @@ static int brcm_nvram_read(void *context
+ return 0;
+ }
+
++static int brcm_nvram_read_post_process_macaddr(void *context, const char *id, int index,
++ unsigned int offset, void *buf, size_t bytes)
++{
++ u8 mac[ETH_ALEN];
++
++ if (bytes != 3 * ETH_ALEN - 1)
++ return -EINVAL;
++
++ if (!mac_pton(buf, mac))
++ return -EINVAL;
++
++ if (index)
++ eth_addr_add(mac, index);
++
++ ether_addr_copy(buf, mac);
++
++ return 0;
++}
++
+ static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data,
+ size_t len)
+ {
+@@ -75,6 +96,13 @@ static int brcm_nvram_add_cells(struct b
+ priv->cells[idx].offset = value - (char *)data;
+ priv->cells[idx].bytes = strlen(value);
+ priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
++ if (!strcmp(var, "et0macaddr") ||
++ !strcmp(var, "et1macaddr") ||
++ !strcmp(var, "et2macaddr")) {
++ priv->cells[idx].raw_len = strlen(value);
++ priv->cells[idx].bytes = ETH_ALEN;
++ priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr;
++ }
+ }
+
+ return 0;
diff --git a/target/linux/generic/backport-6.1/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch b/target/linux/generic/backport-6.1/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch
new file mode 100644
index 0000000000..9868712648
--- /dev/null
+++ b/target/linux/generic/backport-6.1/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch
@@ -0,0 +1,51 @@
+From 5cb03751455c299b1bf10cb48631bf359cfb11b5 Mon Sep 17 00:00:00 2001
+From: "mark-yw.chen" <mark-yw.chen@mediatek.com>
+Date: Wed, 1 Sep 2021 11:32:25 +0800
+Subject: [PATCH 1/5] Bluetooth: btusb: Support public address configuration
+ for MediaTek Chip.
+
+The MediaTek chip support vendor specific HCI command(0xfc1a) to
+change the public address. Add hdev->set_bdaddr handler for MediaTek
+Chip. After doing a power cycle or MediaTek Bluetooth reset, BD_ADDR
+will bring back the original one.
+
+Signed-off-by: mark-yw.chen <mark-yw.chen@mediatek.com>
+Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+---
+ drivers/bluetooth/btusb.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -2272,6 +2272,23 @@ struct btmtk_section_map {
+ };
+ } __packed;
+
++static int btusb_set_bdaddr_mtk(struct hci_dev *hdev, const bdaddr_t *bdaddr)
++{
++ struct sk_buff *skb;
++ long ret;
++
++ skb = __hci_cmd_sync(hdev, 0xfc1a, sizeof(bdaddr), bdaddr, HCI_INIT_TIMEOUT);
++ if (IS_ERR(skb)) {
++ ret = PTR_ERR(skb);
++ bt_dev_err(hdev, "changing Mediatek device address failed (%ld)",
++ ret);
++ return ret;
++ }
++ kfree_skb(skb);
++
++ return 0;
++}
++
+ static void btusb_mtk_wmt_recv(struct urb *urb)
+ {
+ struct hci_dev *hdev = urb->context;
+@@ -3923,6 +3940,7 @@ static int btusb_probe(struct usb_interf
+ hdev->shutdown = btusb_mtk_shutdown;
+ hdev->manufacturer = 70;
+ hdev->cmd_timeout = btusb_mtk_cmd_timeout;
++ hdev->set_bdaddr = btusb_set_bdaddr_mtk;
+ set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
+ data->recv_acl = btusb_recv_acl_mtk;
+ }
diff --git a/target/linux/generic/backport-6.1/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch b/target/linux/generic/backport-6.1/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch
new file mode 100644
index 0000000000..cff537a866
--- /dev/null
+++ b/target/linux/generic/backport-6.1/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch
@@ -0,0 +1,29 @@
+From af774a731f7b4c2a90a8476cd44045ba8d1263ba Mon Sep 17 00:00:00 2001
+From: David Yang <davidcomponentone@gmail.com>
+Date: Wed, 13 Oct 2021 08:56:33 +0800
+Subject: [PATCH 2/5] Bluetooth: btusb: Fix application of sizeof to pointer
+
+The coccinelle check report:
+"./drivers/bluetooth/btusb.c:2239:36-42:
+ERROR: application of sizeof to pointer".
+Using the real size to fix it.
+
+Fixes: 5a87679ffd443 ("Bluetooth: btusb: Support public address configuration for MediaTek Chip.")
+Reported-by: Zeal Robot <zealci@zte.com.cn>
+Signed-off-by: David Yang <davidcomponentone@gmail.com>
+Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+---
+ drivers/bluetooth/btusb.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -2277,7 +2277,7 @@ static int btusb_set_bdaddr_mtk(struct h
+ struct sk_buff *skb;
+ long ret;
+
+- skb = __hci_cmd_sync(hdev, 0xfc1a, sizeof(bdaddr), bdaddr, HCI_INIT_TIMEOUT);
++ skb = __hci_cmd_sync(hdev, 0xfc1a, 6, bdaddr, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ ret = PTR_ERR(skb);
+ bt_dev_err(hdev, "changing Mediatek device address failed (%ld)",
diff --git a/target/linux/generic/backport-6.1/823-v5.18-Bluetooth-btusb-Add-a-new-PID-VID-13d3-3567-for-MT79.patch b/target/linux/generic/backport-6.1/823-v5.18-Bluetooth-btusb-Add-a-new-PID-VID-13d3-3567-for-MT79.patch
new file mode 100644
index 0000000000..d670195da1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/823-v5.18-Bluetooth-btusb-Add-a-new-PID-VID-13d3-3567-for-MT79.patch
@@ -0,0 +1,70 @@
+From e57186fc02cedff191c469a26cce615371e41740 Mon Sep 17 00:00:00 2001
+From: Yake Yang <yake.yang@mediatek.com>
+Date: Wed, 23 Feb 2022 07:55:59 +0800
+Subject: [PATCH 3/5] Bluetooth: btusb: Add a new PID/VID 13d3/3567 for MT7921
+
+Add VID 13D3 & PID 3567 for MediaTek MT7921 USB Bluetooth chip.
+
+The information in /sys/kernel/debug/usb/devices about the Bluetooth
+device is listed as the below.
+
+T: Bus=05 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0
+D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1
+P: Vendor=13d3 ProdID=3567 Rev= 1.00
+S: Manufacturer=MediaTek Inc.
+S: Product=Wireless_Device
+S: SerialNumber=000000000
+C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA
+A: FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01
+I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us
+E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms
+I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms
+I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms
+I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms
+I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms
+I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms
+I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms
+I:* If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none)
+E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us
+E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us
+I: If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none)
+E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us
+E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us
+
+Co-developed-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Yake Yang <yake.yang@mediatek.com>
+Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+---
+ drivers/bluetooth/btusb.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -464,6 +464,9 @@ static const struct usb_device_id blackl
+ { USB_DEVICE(0x13d3, 0x3564), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH |
+ BTUSB_VALID_LE_STATES },
++ { USB_DEVICE(0x13d3, 0x3567), .driver_info = BTUSB_MEDIATEK |
++ BTUSB_WIDEBAND_SPEECH |
++ BTUSB_VALID_LE_STATES },
+ { USB_DEVICE(0x0489, 0xe0cd), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH |
+ BTUSB_VALID_LE_STATES },
diff --git a/target/linux/generic/backport-6.1/824-v5.19-Bluetooth-btusb-Add-a-new-PID-VID-0489-e0c8-for-MT79.patch b/target/linux/generic/backport-6.1/824-v5.19-Bluetooth-btusb-Add-a-new-PID-VID-0489-e0c8-for-MT79.patch
new file mode 100644
index 0000000000..be9dc73421
--- /dev/null
+++ b/target/linux/generic/backport-6.1/824-v5.19-Bluetooth-btusb-Add-a-new-PID-VID-0489-e0c8-for-MT79.patch
@@ -0,0 +1,68 @@
+From e507366cd1e8e1d4eebe537c08fd142cf0b617fa Mon Sep 17 00:00:00 2001
+From: Sean Wang <sean.wang@mediatek.com>
+Date: Thu, 28 Apr 2022 02:38:39 +0800
+Subject: [PATCH 4/5] Bluetooth: btusb: Add a new PID/VID 0489/e0c8 for MT7921
+
+Add VID 0489 & PID e0c8 for MediaTek MT7921 USB Bluetooth chip.
+
+The information in /sys/kernel/debug/usb/devices about the Bluetooth
+device is listed as the below.
+
+T: Bus=01 Lev=01 Prnt=01 Port=13 Cnt=03 Dev#= 4 Spd=480 MxCh= 0
+D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1
+P: Vendor=0489 ProdID=e0c8 Rev= 1.00
+S: Manufacturer=MediaTek Inc.
+S: Product=Wireless_Device
+S: SerialNumber=000000000
+C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA
+A: FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01
+I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us
+E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms
+I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms
+I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms
+I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms
+I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms
+I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms
+I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms
+I:* If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none)
+E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us
+E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us
+I: If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none)
+E: Ad=8a(I) Atr=03(Int.) MxPS= 512 Ivl=125us
+E: Ad=0a(O) Atr=03(Int.) MxPS= 512 Ivl=125us
+
+Signed-off-by: Sean Wang <sean.wang@mediatek.com>
+Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+---
+ drivers/bluetooth/btusb.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -455,6 +455,9 @@ static const struct usb_device_id blackl
+ BTUSB_VALID_LE_STATES },
+
+ /* Additional MediaTek MT7921 Bluetooth devices */
++ { USB_DEVICE(0x0489, 0xe0c8), .driver_info = BTUSB_MEDIATEK |
++ BTUSB_WIDEBAND_SPEECH |
++ BTUSB_VALID_LE_STATES },
+ { USB_DEVICE(0x04ca, 0x3802), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH |
+ BTUSB_VALID_LE_STATES },
diff --git a/target/linux/generic/backport-6.1/825-v6.1-Bluetooth-btusb-Add-a-new-VID-PID-0e8d-0608-for-MT79.patch b/target/linux/generic/backport-6.1/825-v6.1-Bluetooth-btusb-Add-a-new-VID-PID-0e8d-0608-for-MT79.patch
new file mode 100644
index 0000000000..24ec68a2ca
--- /dev/null
+++ b/target/linux/generic/backport-6.1/825-v6.1-Bluetooth-btusb-Add-a-new-VID-PID-0e8d-0608-for-MT79.patch
@@ -0,0 +1,66 @@
+From be55622ce673f9692cc15d26d77a050cda42a3d3 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Fri, 9 Sep 2022 21:00:30 +0100
+Subject: [PATCH 1/1] Bluetooth: btusb: Add a new VID/PID 0e8d/0608 for MT7921
+
+Add a new PID/VID 0e8d/0608 for MT7921K chip found on AMD RZ608 module.
+
+From /sys/kernel/debug/usb/devices:
+T: Bus=01 Lev=02 Prnt=02 Port=01 Cnt=01 Dev#= 3 Spd=480 MxCh= 0
+D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1
+P: Vendor=0e8d ProdID=0608 Rev= 1.00
+S: Manufacturer=MediaTek Inc.
+S: Product=Wireless_Device
+S: SerialNumber=000000000
+C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA
+A: FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01
+I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us
+E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms
+I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms
+I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms
+I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms
+I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms
+I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms
+I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
+E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms
+E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms
+I:* If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none)
+E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us
+E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us
+I: If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none)
+E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us
+E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+---
+ drivers/bluetooth/btusb.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -473,6 +473,9 @@ static const struct usb_device_id blackl
+ { USB_DEVICE(0x0489, 0xe0cd), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH |
+ BTUSB_VALID_LE_STATES },
++ { USB_DEVICE(0x0e8d, 0x0608), .driver_info = BTUSB_MEDIATEK |
++ BTUSB_WIDEBAND_SPEECH |
++ BTUSB_VALID_LE_STATES },
+
+ /* MediaTek MT7922A Bluetooth devices */
+ { USB_DEVICE(0x0489, 0xe0d8), .driver_info = BTUSB_MEDIATEK |
diff --git a/target/linux/generic/backport-6.1/826-v5.17-of-base-make-small-of_parse_phandle-variants-static-.patch b/target/linux/generic/backport-6.1/826-v5.17-of-base-make-small-of_parse_phandle-variants-static-.patch
new file mode 100644
index 0000000000..b00cf57419
--- /dev/null
+++ b/target/linux/generic/backport-6.1/826-v5.17-of-base-make-small-of_parse_phandle-variants-static-.patch
@@ -0,0 +1,359 @@
+From 66a8f7f04979f4ad739085f01d99c8caf620b4f5 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 18 Jan 2022 18:35:02 +0100
+Subject: [PATCH] of: base: make small of_parse_phandle() variants static
+ inline
+
+Make all the smaller variants of the of_parse_phandle() static inline.
+This also let us remove the empty function stubs if CONFIG_OF is not
+defined.
+
+Suggested-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Michael Walle <michael@walle.cc>
+[robh: move index < 0 check into __of_parse_phandle_with_args]
+Signed-off-by: Rob Herring <robh@kernel.org>
+Link: https://lore.kernel.org/r/20220118173504.2867523-2-michael@walle.cc
+---
+ drivers/of/base.c | 131 +++------------------------------------
+ include/linux/of.h | 148 ++++++++++++++++++++++++++++++++++++---------
+ 2 files changed, 129 insertions(+), 150 deletions(-)
+
+--- a/drivers/of/base.c
++++ b/drivers/of/base.c
+@@ -1371,15 +1371,18 @@ int of_phandle_iterator_args(struct of_p
+ return count;
+ }
+
+-static int __of_parse_phandle_with_args(const struct device_node *np,
+- const char *list_name,
+- const char *cells_name,
+- int cell_count, int index,
+- struct of_phandle_args *out_args)
++int __of_parse_phandle_with_args(const struct device_node *np,
++ const char *list_name,
++ const char *cells_name,
++ int cell_count, int index,
++ struct of_phandle_args *out_args)
+ {
+ struct of_phandle_iterator it;
+ int rc, cur_index = 0;
+
++ if (index < 0)
++ return -EINVAL;
++
+ /* Loop over the phandles until all the requested entry is found */
+ of_for_each_phandle(&it, rc, np, list_name, cells_name, cell_count) {
+ /*
+@@ -1422,82 +1425,7 @@ static int __of_parse_phandle_with_args(
+ of_node_put(it.node);
+ return rc;
+ }
+-
+-/**
+- * of_parse_phandle - Resolve a phandle property to a device_node pointer
+- * @np: Pointer to device node holding phandle property
+- * @phandle_name: Name of property holding a phandle value
+- * @index: For properties holding a table of phandles, this is the index into
+- * the table
+- *
+- * Return: The device_node pointer with refcount incremented. Use
+- * of_node_put() on it when done.
+- */
+-struct device_node *of_parse_phandle(const struct device_node *np,
+- const char *phandle_name, int index)
+-{
+- struct of_phandle_args args;
+-
+- if (index < 0)
+- return NULL;
+-
+- if (__of_parse_phandle_with_args(np, phandle_name, NULL, 0,
+- index, &args))
+- return NULL;
+-
+- return args.np;
+-}
+-EXPORT_SYMBOL(of_parse_phandle);
+-
+-/**
+- * of_parse_phandle_with_args() - Find a node pointed by phandle in a list
+- * @np: pointer to a device tree node containing a list
+- * @list_name: property name that contains a list
+- * @cells_name: property name that specifies phandles' arguments count
+- * @index: index of a phandle to parse out
+- * @out_args: optional pointer to output arguments structure (will be filled)
+- *
+- * This function is useful to parse lists of phandles and their arguments.
+- * Returns 0 on success and fills out_args, on error returns appropriate
+- * errno value.
+- *
+- * Caller is responsible to call of_node_put() on the returned out_args->np
+- * pointer.
+- *
+- * Example::
+- *
+- * phandle1: node1 {
+- * #list-cells = <2>;
+- * };
+- *
+- * phandle2: node2 {
+- * #list-cells = <1>;
+- * };
+- *
+- * node3 {
+- * list = <&phandle1 1 2 &phandle2 3>;
+- * };
+- *
+- * To get a device_node of the ``node2`` node you may call this:
+- * of_parse_phandle_with_args(node3, "list", "#list-cells", 1, &args);
+- */
+-int of_parse_phandle_with_args(const struct device_node *np, const char *list_name,
+- const char *cells_name, int index,
+- struct of_phandle_args *out_args)
+-{
+- int cell_count = -1;
+-
+- if (index < 0)
+- return -EINVAL;
+-
+- /* If cells_name is NULL we assume a cell count of 0 */
+- if (!cells_name)
+- cell_count = 0;
+-
+- return __of_parse_phandle_with_args(np, list_name, cells_name,
+- cell_count, index, out_args);
+-}
+-EXPORT_SYMBOL(of_parse_phandle_with_args);
++EXPORT_SYMBOL(__of_parse_phandle_with_args);
+
+ /**
+ * of_parse_phandle_with_args_map() - Find a node pointed by phandle in a list and remap it
+@@ -1684,47 +1612,6 @@ free:
+ EXPORT_SYMBOL(of_parse_phandle_with_args_map);
+
+ /**
+- * of_parse_phandle_with_fixed_args() - Find a node pointed by phandle in a list
+- * @np: pointer to a device tree node containing a list
+- * @list_name: property name that contains a list
+- * @cell_count: number of argument cells following the phandle
+- * @index: index of a phandle to parse out
+- * @out_args: optional pointer to output arguments structure (will be filled)
+- *
+- * This function is useful to parse lists of phandles and their arguments.
+- * Returns 0 on success and fills out_args, on error returns appropriate
+- * errno value.
+- *
+- * Caller is responsible to call of_node_put() on the returned out_args->np
+- * pointer.
+- *
+- * Example::
+- *
+- * phandle1: node1 {
+- * };
+- *
+- * phandle2: node2 {
+- * };
+- *
+- * node3 {
+- * list = <&phandle1 0 2 &phandle2 2 3>;
+- * };
+- *
+- * To get a device_node of the ``node2`` node you may call this:
+- * of_parse_phandle_with_fixed_args(node3, "list", 2, 1, &args);
+- */
+-int of_parse_phandle_with_fixed_args(const struct device_node *np,
+- const char *list_name, int cell_count,
+- int index, struct of_phandle_args *out_args)
+-{
+- if (index < 0)
+- return -EINVAL;
+- return __of_parse_phandle_with_args(np, list_name, NULL, cell_count,
+- index, out_args);
+-}
+-EXPORT_SYMBOL(of_parse_phandle_with_fixed_args);
+-
+-/**
+ * of_count_phandle_with_args() - Find the number of phandles references in a property
+ * @np: pointer to a device tree node containing a list
+ * @list_name: property name that contains a list
+--- a/include/linux/of.h
++++ b/include/linux/of.h
+@@ -363,18 +363,12 @@ extern const struct of_device_id *of_mat
+ const struct of_device_id *matches, const struct device_node *node);
+ extern int of_modalias_node(struct device_node *node, char *modalias, int len);
+ extern void of_print_phandle_args(const char *msg, const struct of_phandle_args *args);
+-extern struct device_node *of_parse_phandle(const struct device_node *np,
+- const char *phandle_name,
+- int index);
+-extern int of_parse_phandle_with_args(const struct device_node *np,
+- const char *list_name, const char *cells_name, int index,
+- struct of_phandle_args *out_args);
++extern int __of_parse_phandle_with_args(const struct device_node *np,
++ const char *list_name, const char *cells_name, int cell_count,
++ int index, struct of_phandle_args *out_args);
+ extern int of_parse_phandle_with_args_map(const struct device_node *np,
+ const char *list_name, const char *stem_name, int index,
+ struct of_phandle_args *out_args);
+-extern int of_parse_phandle_with_fixed_args(const struct device_node *np,
+- const char *list_name, int cells_count, int index,
+- struct of_phandle_args *out_args);
+ extern int of_count_phandle_with_args(const struct device_node *np,
+ const char *list_name, const char *cells_name);
+
+@@ -864,18 +858,12 @@ static inline int of_property_read_strin
+ return -ENOSYS;
+ }
+
+-static inline struct device_node *of_parse_phandle(const struct device_node *np,
+- const char *phandle_name,
+- int index)
+-{
+- return NULL;
+-}
+-
+-static inline int of_parse_phandle_with_args(const struct device_node *np,
+- const char *list_name,
+- const char *cells_name,
+- int index,
+- struct of_phandle_args *out_args)
++static inline int __of_parse_phandle_with_args(const struct device_node *np,
++ const char *list_name,
++ const char *cells_name,
++ int cell_count,
++ int index,
++ struct of_phandle_args *out_args)
+ {
+ return -ENOSYS;
+ }
+@@ -889,13 +877,6 @@ static inline int of_parse_phandle_with_
+ return -ENOSYS;
+ }
+
+-static inline int of_parse_phandle_with_fixed_args(const struct device_node *np,
+- const char *list_name, int cells_count, int index,
+- struct of_phandle_args *out_args)
+-{
+- return -ENOSYS;
+-}
+-
+ static inline int of_count_phandle_with_args(const struct device_node *np,
+ const char *list_name,
+ const char *cells_name)
+@@ -1077,6 +1058,117 @@ static inline bool of_node_is_type(const
+ }
+
+ /**
++ * of_parse_phandle - Resolve a phandle property to a device_node pointer
++ * @np: Pointer to device node holding phandle property
++ * @phandle_name: Name of property holding a phandle value
++ * @index: For properties holding a table of phandles, this is the index into
++ * the table
++ *
++ * Return: The device_node pointer with refcount incremented. Use
++ * of_node_put() on it when done.
++ */
++static inline struct device_node *of_parse_phandle(const struct device_node *np,
++ const char *phandle_name,
++ int index)
++{
++ struct of_phandle_args args;
++
++ if (__of_parse_phandle_with_args(np, phandle_name, NULL, 0,
++ index, &args))
++ return NULL;
++
++ return args.np;
++}
++
++/**
++ * of_parse_phandle_with_args() - Find a node pointed by phandle in a list
++ * @np: pointer to a device tree node containing a list
++ * @list_name: property name that contains a list
++ * @cells_name: property name that specifies phandles' arguments count
++ * @index: index of a phandle to parse out
++ * @out_args: optional pointer to output arguments structure (will be filled)
++ *
++ * This function is useful to parse lists of phandles and their arguments.
++ * Returns 0 on success and fills out_args, on error returns appropriate
++ * errno value.
++ *
++ * Caller is responsible to call of_node_put() on the returned out_args->np
++ * pointer.
++ *
++ * Example::
++ *
++ * phandle1: node1 {
++ * #list-cells = <2>;
++ * };
++ *
++ * phandle2: node2 {
++ * #list-cells = <1>;
++ * };
++ *
++ * node3 {
++ * list = <&phandle1 1 2 &phandle2 3>;
++ * };
++ *
++ * To get a device_node of the ``node2`` node you may call this:
++ * of_parse_phandle_with_args(node3, "list", "#list-cells", 1, &args);
++ */
++static inline int of_parse_phandle_with_args(const struct device_node *np,
++ const char *list_name,
++ const char *cells_name,
++ int index,
++ struct of_phandle_args *out_args)
++{
++ int cell_count = -1;
++
++ /* If cells_name is NULL we assume a cell count of 0 */
++ if (!cells_name)
++ cell_count = 0;
++
++ return __of_parse_phandle_with_args(np, list_name, cells_name,
++ cell_count, index, out_args);
++}
++
++/**
++ * of_parse_phandle_with_fixed_args() - Find a node pointed by phandle in a list
++ * @np: pointer to a device tree node containing a list
++ * @list_name: property name that contains a list
++ * @cell_count: number of argument cells following the phandle
++ * @index: index of a phandle to parse out
++ * @out_args: optional pointer to output arguments structure (will be filled)
++ *
++ * This function is useful to parse lists of phandles and their arguments.
++ * Returns 0 on success and fills out_args, on error returns appropriate
++ * errno value.
++ *
++ * Caller is responsible to call of_node_put() on the returned out_args->np
++ * pointer.
++ *
++ * Example::
++ *
++ * phandle1: node1 {
++ * };
++ *
++ * phandle2: node2 {
++ * };
++ *
++ * node3 {
++ * list = <&phandle1 0 2 &phandle2 2 3>;
++ * };
++ *
++ * To get a device_node of the ``node2`` node you may call this:
++ * of_parse_phandle_with_fixed_args(node3, "list", 2, 1, &args);
++ */
++static inline int of_parse_phandle_with_fixed_args(const struct device_node *np,
++ const char *list_name,
++ int cell_count,
++ int index,
++ struct of_phandle_args *out_args)
++{
++ return __of_parse_phandle_with_args(np, list_name, NULL, cell_count,
++ index, out_args);
++}
++
++/**
+ * of_property_count_u8_elems - Count the number of u8 elements in a property
+ *
+ * @np: device node from which the property value is to be read.
diff --git a/target/linux/generic/backport-6.1/827-v6.3-0001-of-base-add-of_parse_phandle_with_optional_args.patch b/target/linux/generic/backport-6.1/827-v6.3-0001-of-base-add-of_parse_phandle_with_optional_args.patch
new file mode 100644
index 0000000000..2b2a60e096
--- /dev/null
+++ b/target/linux/generic/backport-6.1/827-v6.3-0001-of-base-add-of_parse_phandle_with_optional_args.patch
@@ -0,0 +1,58 @@
+From c5d264d4b527c96ae8903376a4b195df47b05203 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 6 Feb 2023 13:43:43 +0000
+Subject: [PATCH] of: base: add of_parse_phandle_with_optional_args()
+
+Add a new variant of the of_parse_phandle_with_args() which treats the
+cells name as optional. If it's missing, it is assumed that the phandle
+has no arguments.
+
+Up until now, a nvmem node didn't have any arguments, so all the device
+trees haven't any '#*-cells' property. But there is a need for an
+additional argument for the phandle, for which we need a '#*-cells'
+property. Therefore, we need to support nvmem nodes with and without
+this property.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-10-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/of.h | 25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+--- a/include/linux/of.h
++++ b/include/linux/of.h
+@@ -1169,6 +1169,31 @@ static inline int of_parse_phandle_with_
+ }
+
+ /**
++ * of_parse_phandle_with_optional_args() - Find a node pointed by phandle in a list
++ * @np: pointer to a device tree node containing a list
++ * @list_name: property name that contains a list
++ * @cells_name: property name that specifies phandles' arguments count
++ * @index: index of a phandle to parse out
++ * @out_args: optional pointer to output arguments structure (will be filled)
++ *
++ * Same as of_parse_phandle_with_args() except that if the cells_name property
++ * is not found, cell_count of 0 is assumed.
++ *
++ * This is used to useful, if you have a phandle which didn't have arguments
++ * before and thus doesn't have a '#*-cells' property but is now migrated to
++ * having arguments while retaining backwards compatibility.
++ */
++static inline int of_parse_phandle_with_optional_args(const struct device_node *np,
++ const char *list_name,
++ const char *cells_name,
++ int index,
++ struct of_phandle_args *out_args)
++{
++ return __of_parse_phandle_with_args(np, list_name, cells_name,
++ 0, index, out_args);
++}
++
++/**
+ * of_property_count_u8_elems - Count the number of u8 elements in a property
+ *
+ * @np: device node from which the property value is to be read.
diff --git a/target/linux/generic/backport-6.1/827-v6.3-0002-of-property-make-.-cells-optional-for-simple-props.patch b/target/linux/generic/backport-6.1/827-v6.3-0002-of-property-make-.-cells-optional-for-simple-props.patch
new file mode 100644
index 0000000000..39d9fae723
--- /dev/null
+++ b/target/linux/generic/backport-6.1/827-v6.3-0002-of-property-make-.-cells-optional-for-simple-props.patch
@@ -0,0 +1,34 @@
+From ff24fed10ba414d19579e26e60b126fad2f2bb07 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 6 Feb 2023 13:43:44 +0000
+Subject: [PATCH] of: property: make #.*-cells optional for simple props
+
+Sometimes, future bindings for phandles will get additional arguments.
+Thus the target node of the phandle will need a new #.*-cells property.
+To be backwards compatible, this needs to be optional.
+
+Prepare the DEFINE_SIMPLE_PROPS() to handle the cells name as optional.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Tested-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-11-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/property.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/of/property.c
++++ b/drivers/of/property.c
+@@ -1173,8 +1173,8 @@ static struct device_node *parse_prop_ce
+ if (strcmp(prop_name, list_name))
+ return NULL;
+
+- if (of_parse_phandle_with_args(np, list_name, cells_name, index,
+- &sup_args))
++ if (__of_parse_phandle_with_args(np, list_name, cells_name, 0, index,
++ &sup_args))
+ return NULL;
+
+ return sup_args.np;
diff --git a/target/linux/generic/backport-6.1/827-v6.3-0003-of-property-add-nvmem-cell-cells-property.patch b/target/linux/generic/backport-6.1/827-v6.3-0003-of-property-add-nvmem-cell-cells-property.patch
new file mode 100644
index 0000000000..774e793ca5
--- /dev/null
+++ b/target/linux/generic/backport-6.1/827-v6.3-0003-of-property-add-nvmem-cell-cells-property.patch
@@ -0,0 +1,30 @@
+From e2d8172043d2e50df19fcd59c11e5593de8188d7 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 6 Feb 2023 13:43:45 +0000
+Subject: [PATCH] of: property: add #nvmem-cell-cells property
+
+Bindings describe the new '#nvmem-cell-cells' property. Now that the
+arguments count property is optional, we just add this property to the
+nvmem-cells.
+
+Signed-off-by: Michael Walle <michael@walle.cc>
+Tested-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230206134356.839737-12-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/property.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/of/property.c
++++ b/drivers/of/property.c
+@@ -1276,7 +1276,7 @@ DEFINE_SIMPLE_PROP(dmas, "dmas", "#dma-c
+ DEFINE_SIMPLE_PROP(power_domains, "power-domains", "#power-domain-cells")
+ DEFINE_SIMPLE_PROP(hwlocks, "hwlocks", "#hwlock-cells")
+ DEFINE_SIMPLE_PROP(extcon, "extcon", NULL)
+-DEFINE_SIMPLE_PROP(nvmem_cells, "nvmem-cells", NULL)
++DEFINE_SIMPLE_PROP(nvmem_cells, "nvmem-cells", "#nvmem-cell-cells")
+ DEFINE_SIMPLE_PROP(phys, "phys", "#phy-cells")
+ DEFINE_SIMPLE_PROP(wakeup_parent, "wakeup-parent", NULL)
+ DEFINE_SIMPLE_PROP(pinctrl0, "pinctrl-0", NULL)
diff --git a/target/linux/generic/backport-6.1/827-v6.3-0004-of-device-Ignore-modalias-of-reused-nodes.patch b/target/linux/generic/backport-6.1/827-v6.3-0004-of-device-Ignore-modalias-of-reused-nodes.patch
new file mode 100644
index 0000000000..37eefc4570
--- /dev/null
+++ b/target/linux/generic/backport-6.1/827-v6.3-0004-of-device-Ignore-modalias-of-reused-nodes.patch
@@ -0,0 +1,37 @@
+From 553bd29700145e1849698985e9800f14e967da49 Mon Sep 17 00:00:00 2001
+From: Alexander Stein <alexander.stein@ew.tq-group.com>
+Date: Tue, 7 Feb 2023 12:05:29 +0100
+Subject: [PATCH] of: device: Ignore modalias of reused nodes
+
+If of_node is reused, do not use that node's modalias. This will hide
+the name of the actual device. This is rather prominent in USB glue
+drivers creating a platform device for the host controller.
+
+Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Link: https://lore.kernel.org/r/20230207110531.1060252-2-alexander.stein@ew.tq-group.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/device.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -249,7 +249,7 @@ static ssize_t of_device_get_modalias(st
+ ssize_t csize;
+ ssize_t tsize;
+
+- if ((!dev) || (!dev->of_node))
++ if ((!dev) || (!dev->of_node) || dev->of_node_reused)
+ return -ENODEV;
+
+ /* Name & Type */
+@@ -372,7 +372,7 @@ int of_device_uevent_modalias(struct dev
+ {
+ int sl;
+
+- if ((!dev) || (!dev->of_node))
++ if ((!dev) || (!dev->of_node) || dev->of_node_reused)
+ return -ENODEV;
+
+ /* Devicetree modalias is tricky, we add it in 2 steps */
diff --git a/target/linux/generic/backport-6.1/827-v6.3-0005-of-device-Do-not-ignore-error-code-in-of_device_ueve.patch b/target/linux/generic/backport-6.1/827-v6.3-0005-of-device-Do-not-ignore-error-code-in-of_device_ueve.patch
new file mode 100644
index 0000000000..dd5820e94a
--- /dev/null
+++ b/target/linux/generic/backport-6.1/827-v6.3-0005-of-device-Do-not-ignore-error-code-in-of_device_ueve.patch
@@ -0,0 +1,29 @@
+From 2295bed9bebe8d1eef276194fed5b5fbe89c5363 Mon Sep 17 00:00:00 2001
+From: Alexander Stein <alexander.stein@ew.tq-group.com>
+Date: Tue, 7 Feb 2023 12:05:30 +0100
+Subject: [PATCH] of: device: Do not ignore error code in
+ of_device_uevent_modalias
+
+of_device_get_modalias might return an error code, propagate that one.
+Otherwise the negative, signed integer is propagated to unsigned integer
+for the comparison resulting in a huge 'sl' size.
+
+Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Link: https://lore.kernel.org/r/20230207110531.1060252-3-alexander.stein@ew.tq-group.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/device.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -381,6 +381,8 @@ int of_device_uevent_modalias(struct dev
+
+ sl = of_device_get_modalias(dev, &env->buf[env->buflen-1],
+ sizeof(env->buf) - env->buflen);
++ if (sl < 0)
++ return sl;
+ if (sl >= (sizeof(env->buf) - env->buflen))
+ return -ENOMEM;
+ env->buflen += sl;
diff --git a/target/linux/generic/backport-6.1/828-v6.4-0002-of-Update-of_device_get_modalias.patch b/target/linux/generic/backport-6.1/828-v6.4-0002-of-Update-of_device_get_modalias.patch
new file mode 100644
index 0000000000..4713cc71b1
--- /dev/null
+++ b/target/linux/generic/backport-6.1/828-v6.4-0002-of-Update-of_device_get_modalias.patch
@@ -0,0 +1,103 @@
+From 5c3d15e127ebfc0754cd18def7124633b6d46672 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:15 +0100
+Subject: [PATCH] of: Update of_device_get_modalias()
+
+This function only needs a "struct device_node" to work, but for
+convenience the author (and only user) of this helper did use a "struct
+device" and put it in device.c.
+
+Let's convert this helper to take a "struct device node" instead. This
+change asks for two additional changes: renaming it "of_modalias()"
+to fit the current naming, and moving it outside of device.c which will
+be done in a follow-up commit.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/device.c | 29 +++++++++++++++++------------
+ 1 file changed, 17 insertions(+), 12 deletions(-)
+
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -241,7 +241,7 @@ const void *of_device_get_match_data(con
+ }
+ EXPORT_SYMBOL(of_device_get_match_data);
+
+-static ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len)
++static ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
+ {
+ const char *compat;
+ char *c;
+@@ -249,19 +249,16 @@ static ssize_t of_device_get_modalias(st
+ ssize_t csize;
+ ssize_t tsize;
+
+- if ((!dev) || (!dev->of_node) || dev->of_node_reused)
+- return -ENODEV;
+-
+ /* Name & Type */
+ /* %p eats all alphanum characters, so %c must be used here */
+- csize = snprintf(str, len, "of:N%pOFn%c%s", dev->of_node, 'T',
+- of_node_get_device_type(dev->of_node));
++ csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
++ of_node_get_device_type(np));
+ tsize = csize;
+ len -= csize;
+ if (str)
+ str += csize;
+
+- of_property_for_each_string(dev->of_node, "compatible", p, compat) {
++ of_property_for_each_string(np, "compatible", p, compat) {
+ csize = strlen(compat) + 1;
+ tsize += csize;
+ if (csize > len)
+@@ -286,7 +283,10 @@ int of_device_request_module(struct devi
+ ssize_t size;
+ int ret;
+
+- size = of_device_get_modalias(dev, NULL, 0);
++ if (!dev || !dev->of_node)
++ return -ENODEV;
++
++ size = of_modalias(dev->of_node, NULL, 0);
+ if (size < 0)
+ return size;
+
+@@ -297,7 +297,7 @@ int of_device_request_module(struct devi
+ if (!str)
+ return -ENOMEM;
+
+- of_device_get_modalias(dev, str, size);
++ of_modalias(dev->of_node, str, size);
+ str[size - 1] = '\0';
+ ret = request_module(str);
+ kfree(str);
+@@ -314,7 +314,12 @@ EXPORT_SYMBOL_GPL(of_device_request_modu
+ */
+ ssize_t of_device_modalias(struct device *dev, char *str, ssize_t len)
+ {
+- ssize_t sl = of_device_get_modalias(dev, str, len - 2);
++ ssize_t sl;
++
++ if (!dev || !dev->of_node || dev->of_node_reused)
++ return -ENODEV;
++
++ sl = of_modalias(dev->of_node, str, len - 2);
+ if (sl < 0)
+ return sl;
+ if (sl > len - 2)
+@@ -379,8 +384,8 @@ int of_device_uevent_modalias(struct dev
+ if (add_uevent_var(env, "MODALIAS="))
+ return -ENOMEM;
+
+- sl = of_device_get_modalias(dev, &env->buf[env->buflen-1],
+- sizeof(env->buf) - env->buflen);
++ sl = of_modalias(dev->of_node, &env->buf[env->buflen-1],
++ sizeof(env->buf) - env->buflen);
+ if (sl < 0)
+ return sl;
+ if (sl >= (sizeof(env->buf) - env->buflen))
diff --git a/target/linux/generic/backport-6.1/828-v6.4-0003-of-Rename-of_modalias_node.patch b/target/linux/generic/backport-6.1/828-v6.4-0003-of-Rename-of_modalias_node.patch
new file mode 100644
index 0000000000..6c20521701
--- /dev/null
+++ b/target/linux/generic/backport-6.1/828-v6.4-0003-of-Rename-of_modalias_node.patch
@@ -0,0 +1,173 @@
+From 673aa1ed1c9b6710bf24e3f0957d85e2f46c77db Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:16 +0100
+Subject: [PATCH] of: Rename of_modalias_node()
+
+This helper does not produce a real modalias, but tries to get the
+"product" compatible part of the "vendor,product" compatibles only. It
+is far from creating a purely useful modalias string and does not seem
+to be used like that directly anyway, so let's try to give this helper a
+more meaningful name before moving there a real modalias helper (already
+existing under of/device.c).
+
+Also update the various documentations to refer to the strings as
+"aliases" rather than "modaliases" which has a real meaning in the Linux
+kernel.
+
+There is no functional change.
+
+Cc: Rafael J. Wysocki <rafael@kernel.org>
+Cc: Len Brown <lenb@kernel.org>
+Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
+Cc: Maxime Ripard <mripard@kernel.org>
+Cc: Thomas Zimmermann <tzimmermann@suse.de>
+Cc: Sebastian Reichel <sre@kernel.org>
+Cc: Wolfram Sang <wsa@kernel.org>
+Cc: Mark Brown <broonie@kernel.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Acked-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Acked-by: Sebastian Reichel <sre@kernel.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-9-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/bus.c | 7 ++++---
+ drivers/gpu/drm/drm_mipi_dsi.c | 2 +-
+ drivers/hsi/hsi_core.c | 2 +-
+ drivers/i2c/busses/i2c-powermac.c | 2 +-
+ drivers/i2c/i2c-core-of.c | 2 +-
+ drivers/of/base.c | 18 +++++++++++-------
+ drivers/spi/spi.c | 4 ++--
+ include/linux/of.h | 3 ++-
+ 8 files changed, 23 insertions(+), 17 deletions(-)
+
+--- a/drivers/acpi/bus.c
++++ b/drivers/acpi/bus.c
+@@ -785,9 +785,10 @@ static bool acpi_of_modalias(struct acpi
+ * @modalias: Pointer to buffer that modalias value will be copied into
+ * @len: Length of modalias buffer
+ *
+- * This is a counterpart of of_modalias_node() for struct acpi_device objects.
+- * If there is a compatible string for @adev, it will be copied to @modalias
+- * with the vendor prefix stripped; otherwise, @default_id will be used.
++ * This is a counterpart of of_alias_from_compatible() for struct acpi_device
++ * objects. If there is a compatible string for @adev, it will be copied to
++ * @modalias with the vendor prefix stripped; otherwise, @default_id will be
++ * used.
+ */
+ void acpi_set_modalias(struct acpi_device *adev, const char *default_id,
+ char *modalias, size_t len)
+--- a/drivers/gpu/drm/drm_mipi_dsi.c
++++ b/drivers/gpu/drm/drm_mipi_dsi.c
+@@ -160,7 +160,7 @@ of_mipi_dsi_device_add(struct mipi_dsi_h
+ int ret;
+ u32 reg;
+
+- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
++ if (of_alias_from_compatible(node, info.type, sizeof(info.type)) < 0) {
+ drm_err(host, "modalias failure on %pOF\n", node);
+ return ERR_PTR(-EINVAL);
+ }
+--- a/drivers/hsi/hsi_core.c
++++ b/drivers/hsi/hsi_core.c
+@@ -207,7 +207,7 @@ static void hsi_add_client_from_dt(struc
+ if (!cl)
+ return;
+
+- err = of_modalias_node(client, name, sizeof(name));
++ err = of_alias_from_compatible(client, name, sizeof(name));
+ if (err)
+ goto err;
+
+--- a/drivers/i2c/busses/i2c-powermac.c
++++ b/drivers/i2c/busses/i2c-powermac.c
+@@ -284,7 +284,7 @@ static bool i2c_powermac_get_type(struct
+ */
+
+ /* First try proper modalias */
+- if (of_modalias_node(node, tmp, sizeof(tmp)) >= 0) {
++ if (of_alias_from_compatible(node, tmp, sizeof(tmp)) >= 0) {
+ snprintf(type, type_size, "MAC,%s", tmp);
+ return true;
+ }
+--- a/drivers/i2c/i2c-core-of.c
++++ b/drivers/i2c/i2c-core-of.c
+@@ -27,7 +27,7 @@ int of_i2c_get_board_info(struct device
+
+ memset(info, 0, sizeof(*info));
+
+- if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) {
++ if (of_alias_from_compatible(node, info->type, sizeof(info->type)) < 0) {
+ dev_err(dev, "of_i2c: modalias failure on %pOF\n", node);
+ return -EINVAL;
+ }
+--- a/drivers/of/base.c
++++ b/drivers/of/base.c
+@@ -1159,19 +1159,23 @@ struct device_node *of_find_matching_nod
+ EXPORT_SYMBOL(of_find_matching_node_and_match);
+
+ /**
+- * of_modalias_node - Lookup appropriate modalias for a device node
++ * of_alias_from_compatible - Lookup appropriate alias for a device node
++ * depending on compatible
+ * @node: pointer to a device tree node
+- * @modalias: Pointer to buffer that modalias value will be copied into
+- * @len: Length of modalias value
++ * @alias: Pointer to buffer that alias value will be copied into
++ * @len: Length of alias value
+ *
+ * Based on the value of the compatible property, this routine will attempt
+- * to choose an appropriate modalias value for a particular device tree node.
++ * to choose an appropriate alias value for a particular device tree node.
+ * It does this by stripping the manufacturer prefix (as delimited by a ',')
+ * from the first entry in the compatible list property.
+ *
++ * Note: The matching on just the "product" side of the compatible is a relic
++ * from I2C and SPI. Please do not add any new user.
++ *
+ * Return: This routine returns 0 on success, <0 on failure.
+ */
+-int of_modalias_node(struct device_node *node, char *modalias, int len)
++int of_alias_from_compatible(const struct device_node *node, char *alias, int len)
+ {
+ const char *compatible, *p;
+ int cplen;
+@@ -1180,10 +1184,10 @@ int of_modalias_node(struct device_node
+ if (!compatible || strlen(compatible) > cplen)
+ return -ENODEV;
+ p = strchr(compatible, ',');
+- strlcpy(modalias, p ? p + 1 : compatible, len);
++ strlcpy(alias, p ? p + 1 : compatible, len);
+ return 0;
+ }
+-EXPORT_SYMBOL_GPL(of_modalias_node);
++EXPORT_SYMBOL_GPL(of_alias_from_compatible);
+
+ /**
+ * of_find_node_by_phandle - Find a node given a phandle
+--- a/drivers/spi/spi.c
++++ b/drivers/spi/spi.c
+@@ -2128,8 +2128,8 @@ of_register_spi_device(struct spi_contro
+ }
+
+ /* Select device driver */
+- rc = of_modalias_node(nc, spi->modalias,
+- sizeof(spi->modalias));
++ rc = of_alias_from_compatible(nc, spi->modalias,
++ sizeof(spi->modalias));
+ if (rc < 0) {
+ dev_err(&ctlr->dev, "cannot find modalias for %pOF\n", nc);
+ goto err_out;
+--- a/include/linux/of.h
++++ b/include/linux/of.h
+@@ -361,7 +361,8 @@ extern int of_n_addr_cells(struct device
+ extern int of_n_size_cells(struct device_node *np);
+ extern const struct of_device_id *of_match_node(
+ const struct of_device_id *matches, const struct device_node *node);
+-extern int of_modalias_node(struct device_node *node, char *modalias, int len);
++extern int of_alias_from_compatible(const struct device_node *node, char *alias,
++ int len);
+ extern void of_print_phandle_args(const char *msg, const struct of_phandle_args *args);
+ extern int __of_parse_phandle_with_args(const struct device_node *np,
+ const char *list_name, const char *cells_name, int cell_count,
diff --git a/target/linux/generic/backport-6.1/828-v6.4-0004-of-Move-of_modalias-to-module.c.patch b/target/linux/generic/backport-6.1/828-v6.4-0004-of-Move-of_modalias-to-module.c.patch
new file mode 100644
index 0000000000..a70c6f2eec
--- /dev/null
+++ b/target/linux/generic/backport-6.1/828-v6.4-0004-of-Move-of_modalias-to-module.c.patch
@@ -0,0 +1,160 @@
+From bd7a7ed774afd1a4174df34227626c95573be517 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:17 +0100
+Subject: [PATCH] of: Move of_modalias() to module.c
+
+Create a specific .c file for OF related module handling.
+Move of_modalias() inside as a first step.
+
+The helper is exposed through of.h even though it is only used by core
+files because the users from device.c will soon be split into an OF-only
+helper in module.c as well as a device-oriented inline helper in
+of_device.h. Putting this helper in of_private.h would require to
+include of_private.h from of_device.h, which is not acceptable.
+
+Suggested-by: Rob Herring <robh+dt@kernel.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-10-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/Makefile | 2 +-
+ drivers/of/device.c | 37 -------------------------------------
+ drivers/of/module.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/of.h | 9 +++++++++
+ 4 files changed, 54 insertions(+), 38 deletions(-)
+ create mode 100644 drivers/of/module.c
+
+--- a/drivers/of/Makefile
++++ b/drivers/of/Makefile
+@@ -1,5 +1,5 @@
+ # SPDX-License-Identifier: GPL-2.0
+-obj-y = base.o device.o platform.o property.o
++obj-y = base.o device.o module.o platform.o property.o
+ obj-$(CONFIG_OF_KOBJ) += kobj.o
+ obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
+ obj-$(CONFIG_OF_FLATTREE) += fdt.o
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -1,5 +1,4 @@
+ // SPDX-License-Identifier: GPL-2.0
+-#include <linux/string.h>
+ #include <linux/kernel.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+@@ -241,42 +240,6 @@ const void *of_device_get_match_data(con
+ }
+ EXPORT_SYMBOL(of_device_get_match_data);
+
+-static ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
+-{
+- const char *compat;
+- char *c;
+- struct property *p;
+- ssize_t csize;
+- ssize_t tsize;
+-
+- /* Name & Type */
+- /* %p eats all alphanum characters, so %c must be used here */
+- csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
+- of_node_get_device_type(np));
+- tsize = csize;
+- len -= csize;
+- if (str)
+- str += csize;
+-
+- of_property_for_each_string(np, "compatible", p, compat) {
+- csize = strlen(compat) + 1;
+- tsize += csize;
+- if (csize > len)
+- continue;
+-
+- csize = snprintf(str, len, "C%s", compat);
+- for (c = str; c; ) {
+- c = strchr(c, ' ');
+- if (c)
+- *c++ = '_';
+- }
+- len -= csize;
+- str += csize;
+- }
+-
+- return tsize;
+-}
+-
+ int of_device_request_module(struct device *dev)
+ {
+ char *str;
+--- /dev/null
++++ b/drivers/of/module.c
+@@ -0,0 +1,44 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Linux kernel module helpers.
++ */
++
++#include <linux/of.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++
++ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
++{
++ const char *compat;
++ char *c;
++ struct property *p;
++ ssize_t csize;
++ ssize_t tsize;
++
++ /* Name & Type */
++ /* %p eats all alphanum characters, so %c must be used here */
++ csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
++ of_node_get_device_type(np));
++ tsize = csize;
++ len -= csize;
++ if (str)
++ str += csize;
++
++ of_property_for_each_string(np, "compatible", p, compat) {
++ csize = strlen(compat) + 1;
++ tsize += csize;
++ if (csize > len)
++ continue;
++
++ csize = snprintf(str, len, "C%s", compat);
++ for (c = str; c; ) {
++ c = strchr(c, ' ');
++ if (c)
++ *c++ = '_';
++ }
++ len -= csize;
++ str += csize;
++ }
++
++ return tsize;
++}
+--- a/include/linux/of.h
++++ b/include/linux/of.h
+@@ -373,6 +373,9 @@ extern int of_parse_phandle_with_args_ma
+ extern int of_count_phandle_with_args(const struct device_node *np,
+ const char *list_name, const char *cells_name);
+
++/* module functions */
++extern ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len);
++
+ /* phandle iterator functions */
+ extern int of_phandle_iterator_init(struct of_phandle_iterator *it,
+ const struct device_node *np,
+@@ -885,6 +888,12 @@ static inline int of_count_phandle_with_
+ return -ENOSYS;
+ }
+
++static inline ssize_t of_modalias(const struct device_node *np, char *str,
++ ssize_t len)
++{
++ return -ENODEV;
++}
++
+ static inline int of_phandle_iterator_init(struct of_phandle_iterator *it,
+ const struct device_node *np,
+ const char *list_name,
diff --git a/target/linux/generic/backport-6.1/828-v6.4-0005-of-Move-the-request-module-helper-logic-to-module.c.patch b/target/linux/generic/backport-6.1/828-v6.4-0005-of-Move-the-request-module-helper-logic-to-module.c.patch
new file mode 100644
index 0000000000..06bc24ca6e
--- /dev/null
+++ b/target/linux/generic/backport-6.1/828-v6.4-0005-of-Move-the-request-module-helper-logic-to-module.c.patch
@@ -0,0 +1,131 @@
+From e6506f06d5e82765666902ccf9e9162f3e31d518 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Tue, 4 Apr 2023 18:21:18 +0100
+Subject: [PATCH] of: Move the request module helper logic to module.c
+
+Depending on device.c for pure OF handling is considered
+backwards. Let's extract the content of of_device_request_module() to
+have the real logic under module.c.
+
+The next step will be to convert users of of_device_request_module() to
+use the new helper.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-11-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/device.c | 25 ++-----------------------
+ drivers/of/module.c | 30 ++++++++++++++++++++++++++++++
+ include/linux/of.h | 6 ++++++
+ 3 files changed, 38 insertions(+), 23 deletions(-)
+
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -8,7 +8,6 @@
+ #include <linux/dma-direct.h> /* for bus_dma_region */
+ #include <linux/dma-map-ops.h>
+ #include <linux/init.h>
+-#include <linux/module.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/slab.h>
+ #include <linux/platform_device.h>
+@@ -242,30 +241,10 @@ EXPORT_SYMBOL(of_device_get_match_data);
+
+ int of_device_request_module(struct device *dev)
+ {
+- char *str;
+- ssize_t size;
+- int ret;
+-
+- if (!dev || !dev->of_node)
++ if (!dev)
+ return -ENODEV;
+
+- size = of_modalias(dev->of_node, NULL, 0);
+- if (size < 0)
+- return size;
+-
+- /* Reserve an additional byte for the trailing '\0' */
+- size++;
+-
+- str = kmalloc(size, GFP_KERNEL);
+- if (!str)
+- return -ENOMEM;
+-
+- of_modalias(dev->of_node, str, size);
+- str[size - 1] = '\0';
+- ret = request_module(str);
+- kfree(str);
+-
+- return ret;
++ return of_request_module(dev->of_node);
+ }
+ EXPORT_SYMBOL_GPL(of_device_request_module);
+
+--- a/drivers/of/module.c
++++ b/drivers/of/module.c
+@@ -4,6 +4,7 @@
+ */
+
+ #include <linux/of.h>
++#include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
+
+@@ -42,3 +43,32 @@ ssize_t of_modalias(const struct device_
+
+ return tsize;
+ }
++
++int of_request_module(const struct device_node *np)
++{
++ char *str;
++ ssize_t size;
++ int ret;
++
++ if (!np)
++ return -ENODEV;
++
++ size = of_modalias(np, NULL, 0);
++ if (size < 0)
++ return size;
++
++ /* Reserve an additional byte for the trailing '\0' */
++ size++;
++
++ str = kmalloc(size, GFP_KERNEL);
++ if (!str)
++ return -ENOMEM;
++
++ of_modalias(np, str, size);
++ str[size - 1] = '\0';
++ ret = request_module(str);
++ kfree(str);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(of_request_module);
+--- a/include/linux/of.h
++++ b/include/linux/of.h
+@@ -375,6 +375,7 @@ extern int of_count_phandle_with_args(co
+
+ /* module functions */
+ extern ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len);
++extern int of_request_module(const struct device_node *np);
+
+ /* phandle iterator functions */
+ extern int of_phandle_iterator_init(struct of_phandle_iterator *it,
+@@ -893,6 +894,11 @@ static inline ssize_t of_modalias(const
+ {
+ return -ENODEV;
+ }
++
++static inline int of_request_module(const struct device_node *np)
++{
++ return -ENODEV;
++}
+
+ static inline int of_phandle_iterator_init(struct of_phandle_iterator *it,
+ const struct device_node *np,
diff --git a/target/linux/generic/backport-6.1/830-v6.2-ata-ahci-fix-enum-constants-for-gcc-13.patch b/target/linux/generic/backport-6.1/830-v6.2-ata-ahci-fix-enum-constants-for-gcc-13.patch
new file mode 100644
index 0000000000..0d81d24e09
--- /dev/null
+++ b/target/linux/generic/backport-6.1/830-v6.2-ata-ahci-fix-enum-constants-for-gcc-13.patch
@@ -0,0 +1,348 @@
+From f07788079f515ca4a681c5f595bdad19cfbd7b1d Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Sat, 3 Dec 2022 11:54:25 +0100
+Subject: [PATCH] ata: ahci: fix enum constants for gcc-13
+
+gcc-13 slightly changes the type of constant expressions that are defined
+in an enum, which triggers a compile time sanity check in libata:
+
+linux/drivers/ata/libahci.c: In function 'ahci_led_store':
+linux/include/linux/compiler_types.h:357:45: error: call to '__compiletime_assert_302' declared with attribute error: BUILD_BUG_ON failed: sizeof(_s) > sizeof(long)
+357 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
+
+The new behavior is that sizeof() returns the same value for the
+constant as it does for the enum type, which is generally more sensible
+and consistent.
+
+The problem in libata is that it contains a single enum definition for
+lots of unrelated constants, some of which are large positive (unsigned)
+integers like 0xffffffff, while others like (1<<31) are interpreted as
+negative integers, and this forces the enum type to become 64 bit wide
+even though most constants would still fit into a signed 32-bit 'int'.
+
+Fix this by changing the entire enum definition to use BIT(x) in place
+of (1<<x), which results in all values being seen as 'unsigned' and
+fitting into an unsigned 32-bit type.
+
+Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107917
+Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107405
+Reported-by: Luis Machado <luis.machado@arm.com>
+Cc: linux-ide@vger.kernel.org
+Cc: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+Cc: stable@vger.kernel.org
+Cc: Randy Dunlap <rdunlap@infradead.org>
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+Tested-by: Luis Machado <luis.machado@arm.com>
+Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+---
+ drivers/ata/ahci.h | 245 +++++++++++++++++++++++----------------------
+ 1 file changed, 123 insertions(+), 122 deletions(-)
+
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -24,6 +24,7 @@
+ #include <linux/libata.h>
+ #include <linux/phy/phy.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/bits.h>
+
+ /* Enclosure Management Control */
+ #define EM_CTRL_MSG_TYPE 0x000f0000
+@@ -54,12 +55,12 @@ enum {
+ AHCI_PORT_PRIV_FBS_DMA_SZ = AHCI_CMD_SLOT_SZ +
+ AHCI_CMD_TBL_AR_SZ +
+ (AHCI_RX_FIS_SZ * 16),
+- AHCI_IRQ_ON_SG = (1 << 31),
+- AHCI_CMD_ATAPI = (1 << 5),
+- AHCI_CMD_WRITE = (1 << 6),
+- AHCI_CMD_PREFETCH = (1 << 7),
+- AHCI_CMD_RESET = (1 << 8),
+- AHCI_CMD_CLR_BUSY = (1 << 10),
++ AHCI_IRQ_ON_SG = BIT(31),
++ AHCI_CMD_ATAPI = BIT(5),
++ AHCI_CMD_WRITE = BIT(6),
++ AHCI_CMD_PREFETCH = BIT(7),
++ AHCI_CMD_RESET = BIT(8),
++ AHCI_CMD_CLR_BUSY = BIT(10),
+
+ RX_FIS_PIO_SETUP = 0x20, /* offset of PIO Setup FIS data */
+ RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */
+@@ -77,37 +78,37 @@ enum {
+ HOST_CAP2 = 0x24, /* host capabilities, extended */
+
+ /* HOST_CTL bits */
+- HOST_RESET = (1 << 0), /* reset controller; self-clear */
+- HOST_IRQ_EN = (1 << 1), /* global IRQ enable */
+- HOST_MRSM = (1 << 2), /* MSI Revert to Single Message */
+- HOST_AHCI_EN = (1 << 31), /* AHCI enabled */
++ HOST_RESET = BIT(0), /* reset controller; self-clear */
++ HOST_IRQ_EN = BIT(1), /* global IRQ enable */
++ HOST_MRSM = BIT(2), /* MSI Revert to Single Message */
++ HOST_AHCI_EN = BIT(31), /* AHCI enabled */
+
+ /* HOST_CAP bits */
+- HOST_CAP_SXS = (1 << 5), /* Supports External SATA */
+- HOST_CAP_EMS = (1 << 6), /* Enclosure Management support */
+- HOST_CAP_CCC = (1 << 7), /* Command Completion Coalescing */
+- HOST_CAP_PART = (1 << 13), /* Partial state capable */
+- HOST_CAP_SSC = (1 << 14), /* Slumber state capable */
+- HOST_CAP_PIO_MULTI = (1 << 15), /* PIO multiple DRQ support */
+- HOST_CAP_FBS = (1 << 16), /* FIS-based switching support */
+- HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */
+- HOST_CAP_ONLY = (1 << 18), /* Supports AHCI mode only */
+- HOST_CAP_CLO = (1 << 24), /* Command List Override support */
+- HOST_CAP_LED = (1 << 25), /* Supports activity LED */
+- HOST_CAP_ALPM = (1 << 26), /* Aggressive Link PM support */
+- HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */
+- HOST_CAP_MPS = (1 << 28), /* Mechanical presence switch */
+- HOST_CAP_SNTF = (1 << 29), /* SNotification register */
+- HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */
+- HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */
++ HOST_CAP_SXS = BIT(5), /* Supports External SATA */
++ HOST_CAP_EMS = BIT(6), /* Enclosure Management support */
++ HOST_CAP_CCC = BIT(7), /* Command Completion Coalescing */
++ HOST_CAP_PART = BIT(13), /* Partial state capable */
++ HOST_CAP_SSC = BIT(14), /* Slumber state capable */
++ HOST_CAP_PIO_MULTI = BIT(15), /* PIO multiple DRQ support */
++ HOST_CAP_FBS = BIT(16), /* FIS-based switching support */
++ HOST_CAP_PMP = BIT(17), /* Port Multiplier support */
++ HOST_CAP_ONLY = BIT(18), /* Supports AHCI mode only */
++ HOST_CAP_CLO = BIT(24), /* Command List Override support */
++ HOST_CAP_LED = BIT(25), /* Supports activity LED */
++ HOST_CAP_ALPM = BIT(26), /* Aggressive Link PM support */
++ HOST_CAP_SSS = BIT(27), /* Staggered Spin-up */
++ HOST_CAP_MPS = BIT(28), /* Mechanical presence switch */
++ HOST_CAP_SNTF = BIT(29), /* SNotification register */
++ HOST_CAP_NCQ = BIT(30), /* Native Command Queueing */
++ HOST_CAP_64 = BIT(31), /* PCI DAC (64-bit DMA) support */
+
+ /* HOST_CAP2 bits */
+- HOST_CAP2_BOH = (1 << 0), /* BIOS/OS handoff supported */
+- HOST_CAP2_NVMHCI = (1 << 1), /* NVMHCI supported */
+- HOST_CAP2_APST = (1 << 2), /* Automatic partial to slumber */
+- HOST_CAP2_SDS = (1 << 3), /* Support device sleep */
+- HOST_CAP2_SADM = (1 << 4), /* Support aggressive DevSlp */
+- HOST_CAP2_DESO = (1 << 5), /* DevSlp from slumber only */
++ HOST_CAP2_BOH = BIT(0), /* BIOS/OS handoff supported */
++ HOST_CAP2_NVMHCI = BIT(1), /* NVMHCI supported */
++ HOST_CAP2_APST = BIT(2), /* Automatic partial to slumber */
++ HOST_CAP2_SDS = BIT(3), /* Support device sleep */
++ HOST_CAP2_SADM = BIT(4), /* Support aggressive DevSlp */
++ HOST_CAP2_DESO = BIT(5), /* DevSlp from slumber only */
+
+ /* registers for each SATA port */
+ PORT_LST_ADDR = 0x00, /* command list DMA addr */
+@@ -129,24 +130,24 @@ enum {
+ PORT_DEVSLP = 0x44, /* device sleep */
+
+ /* PORT_IRQ_{STAT,MASK} bits */
+- PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */
+- PORT_IRQ_TF_ERR = (1 << 30), /* task file error */
+- PORT_IRQ_HBUS_ERR = (1 << 29), /* host bus fatal error */
+- PORT_IRQ_HBUS_DATA_ERR = (1 << 28), /* host bus data error */
+- PORT_IRQ_IF_ERR = (1 << 27), /* interface fatal error */
+- PORT_IRQ_IF_NONFATAL = (1 << 26), /* interface non-fatal error */
+- PORT_IRQ_OVERFLOW = (1 << 24), /* xfer exhausted available S/G */
+- PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */
+-
+- PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */
+- PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */
+- PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */
+- PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */
+- PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */
+- PORT_IRQ_SDB_FIS = (1 << 3), /* Set Device Bits FIS rx'd */
+- PORT_IRQ_DMAS_FIS = (1 << 2), /* DMA Setup FIS rx'd */
+- PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */
+- PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */
++ PORT_IRQ_COLD_PRES = BIT(31), /* cold presence detect */
++ PORT_IRQ_TF_ERR = BIT(30), /* task file error */
++ PORT_IRQ_HBUS_ERR = BIT(29), /* host bus fatal error */
++ PORT_IRQ_HBUS_DATA_ERR = BIT(28), /* host bus data error */
++ PORT_IRQ_IF_ERR = BIT(27), /* interface fatal error */
++ PORT_IRQ_IF_NONFATAL = BIT(26), /* interface non-fatal error */
++ PORT_IRQ_OVERFLOW = BIT(24), /* xfer exhausted available S/G */
++ PORT_IRQ_BAD_PMP = BIT(23), /* incorrect port multiplier */
++
++ PORT_IRQ_PHYRDY = BIT(22), /* PhyRdy changed */
++ PORT_IRQ_DEV_ILCK = BIT(7), /* device interlock */
++ PORT_IRQ_CONNECT = BIT(6), /* port connect change status */
++ PORT_IRQ_SG_DONE = BIT(5), /* descriptor processed */
++ PORT_IRQ_UNK_FIS = BIT(4), /* unknown FIS rx'd */
++ PORT_IRQ_SDB_FIS = BIT(3), /* Set Device Bits FIS rx'd */
++ PORT_IRQ_DMAS_FIS = BIT(2), /* DMA Setup FIS rx'd */
++ PORT_IRQ_PIOS_FIS = BIT(1), /* PIO Setup FIS rx'd */
++ PORT_IRQ_D2H_REG_FIS = BIT(0), /* D2H Register FIS rx'd */
+
+ PORT_IRQ_FREEZE = PORT_IRQ_HBUS_ERR |
+ PORT_IRQ_IF_ERR |
+@@ -162,34 +163,34 @@ enum {
+ PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS,
+
+ /* PORT_CMD bits */
+- PORT_CMD_ASP = (1 << 27), /* Aggressive Slumber/Partial */
+- PORT_CMD_ALPE = (1 << 26), /* Aggressive Link PM enable */
+- PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */
+- PORT_CMD_FBSCP = (1 << 22), /* FBS Capable Port */
+- PORT_CMD_ESP = (1 << 21), /* External Sata Port */
+- PORT_CMD_HPCP = (1 << 18), /* HotPlug Capable Port */
+- PORT_CMD_PMP = (1 << 17), /* PMP attached */
+- PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */
+- PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */
+- PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */
+- PORT_CMD_CLO = (1 << 3), /* Command list override */
+- PORT_CMD_POWER_ON = (1 << 2), /* Power up device */
+- PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */
+- PORT_CMD_START = (1 << 0), /* Enable port DMA engine */
+-
+- PORT_CMD_ICC_MASK = (0xf << 28), /* i/f ICC state mask */
+- PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */
+- PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */
+- PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */
++ PORT_CMD_ASP = BIT(27), /* Aggressive Slumber/Partial */
++ PORT_CMD_ALPE = BIT(26), /* Aggressive Link PM enable */
++ PORT_CMD_ATAPI = BIT(24), /* Device is ATAPI */
++ PORT_CMD_FBSCP = BIT(22), /* FBS Capable Port */
++ PORT_CMD_ESP = BIT(21), /* External Sata Port */
++ PORT_CMD_HPCP = BIT(18), /* HotPlug Capable Port */
++ PORT_CMD_PMP = BIT(17), /* PMP attached */
++ PORT_CMD_LIST_ON = BIT(15), /* cmd list DMA engine running */
++ PORT_CMD_FIS_ON = BIT(14), /* FIS DMA engine running */
++ PORT_CMD_FIS_RX = BIT(4), /* Enable FIS receive DMA engine */
++ PORT_CMD_CLO = BIT(3), /* Command list override */
++ PORT_CMD_POWER_ON = BIT(2), /* Power up device */
++ PORT_CMD_SPIN_UP = BIT(1), /* Spin up device */
++ PORT_CMD_START = BIT(0), /* Enable port DMA engine */
++
++ PORT_CMD_ICC_MASK = (0xfu << 28), /* i/f ICC state mask */
++ PORT_CMD_ICC_ACTIVE = (0x1u << 28), /* Put i/f in active state */
++ PORT_CMD_ICC_PARTIAL = (0x2u << 28), /* Put i/f in partial state */
++ PORT_CMD_ICC_SLUMBER = (0x6u << 28), /* Put i/f in slumber state */
+
+ /* PORT_FBS bits */
+ PORT_FBS_DWE_OFFSET = 16, /* FBS device with error offset */
+ PORT_FBS_ADO_OFFSET = 12, /* FBS active dev optimization offset */
+ PORT_FBS_DEV_OFFSET = 8, /* FBS device to issue offset */
+ PORT_FBS_DEV_MASK = (0xf << PORT_FBS_DEV_OFFSET), /* FBS.DEV */
+- PORT_FBS_SDE = (1 << 2), /* FBS single device error */
+- PORT_FBS_DEC = (1 << 1), /* FBS device error clear */
+- PORT_FBS_EN = (1 << 0), /* Enable FBS */
++ PORT_FBS_SDE = BIT(2), /* FBS single device error */
++ PORT_FBS_DEC = BIT(1), /* FBS device error clear */
++ PORT_FBS_EN = BIT(0), /* Enable FBS */
+
+ /* PORT_DEVSLP bits */
+ PORT_DEVSLP_DM_OFFSET = 25, /* DITO multiplier offset */
+@@ -197,50 +198,50 @@ enum {
+ PORT_DEVSLP_DITO_OFFSET = 15, /* DITO offset */
+ PORT_DEVSLP_MDAT_OFFSET = 10, /* Minimum assertion time */
+ PORT_DEVSLP_DETO_OFFSET = 2, /* DevSlp exit timeout */
+- PORT_DEVSLP_DSP = (1 << 1), /* DevSlp present */
+- PORT_DEVSLP_ADSE = (1 << 0), /* Aggressive DevSlp enable */
++ PORT_DEVSLP_DSP = BIT(1), /* DevSlp present */
++ PORT_DEVSLP_ADSE = BIT(0), /* Aggressive DevSlp enable */
+
+ /* hpriv->flags bits */
+
+ #define AHCI_HFLAGS(flags) .private_data = (void *)(flags)
+
+- AHCI_HFLAG_NO_NCQ = (1 << 0),
+- AHCI_HFLAG_IGN_IRQ_IF_ERR = (1 << 1), /* ignore IRQ_IF_ERR */
+- AHCI_HFLAG_IGN_SERR_INTERNAL = (1 << 2), /* ignore SERR_INTERNAL */
+- AHCI_HFLAG_32BIT_ONLY = (1 << 3), /* force 32bit */
+- AHCI_HFLAG_MV_PATA = (1 << 4), /* PATA port */
+- AHCI_HFLAG_NO_MSI = (1 << 5), /* no PCI MSI */
+- AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */
+- AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */
+- AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */
+- AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */
+- AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = (1 << 11), /* treat SRST timeout as
++ AHCI_HFLAG_NO_NCQ = BIT(0),
++ AHCI_HFLAG_IGN_IRQ_IF_ERR = BIT(1), /* ignore IRQ_IF_ERR */
++ AHCI_HFLAG_IGN_SERR_INTERNAL = BIT(2), /* ignore SERR_INTERNAL */
++ AHCI_HFLAG_32BIT_ONLY = BIT(3), /* force 32bit */
++ AHCI_HFLAG_MV_PATA = BIT(4), /* PATA port */
++ AHCI_HFLAG_NO_MSI = BIT(5), /* no PCI MSI */
++ AHCI_HFLAG_NO_PMP = BIT(6), /* no PMP */
++ AHCI_HFLAG_SECT255 = BIT(8), /* max 255 sectors */
++ AHCI_HFLAG_YES_NCQ = BIT(9), /* force NCQ cap on */
++ AHCI_HFLAG_NO_SUSPEND = BIT(10), /* don't suspend */
++ AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = BIT(11), /* treat SRST timeout as
+ link offline */
+- AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */
+- AHCI_HFLAG_NO_FPDMA_AA = (1 << 13), /* no FPDMA AA */
+- AHCI_HFLAG_YES_FBS = (1 << 14), /* force FBS cap on */
+- AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on
++ AHCI_HFLAG_NO_SNTF = BIT(12), /* no sntf */
++ AHCI_HFLAG_NO_FPDMA_AA = BIT(13), /* no FPDMA AA */
++ AHCI_HFLAG_YES_FBS = BIT(14), /* force FBS cap on */
++ AHCI_HFLAG_DELAY_ENGINE = BIT(15), /* do not start engine on
+ port start (wait until
+ error-handling stage) */
+- AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */
+- AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */
++ AHCI_HFLAG_NO_DEVSLP = BIT(17), /* no device sleep */
++ AHCI_HFLAG_NO_FBS = BIT(18), /* no FBS */
+
+ #ifdef CONFIG_PCI_MSI
+- AHCI_HFLAG_MULTI_MSI = (1 << 20), /* per-port MSI(-X) */
++ AHCI_HFLAG_MULTI_MSI = BIT(20), /* per-port MSI(-X) */
+ #else
+ /* compile out MSI infrastructure */
+ AHCI_HFLAG_MULTI_MSI = 0,
+ #endif
+- AHCI_HFLAG_WAKE_BEFORE_STOP = (1 << 22), /* wake before DMA stop */
+- AHCI_HFLAG_YES_ALPM = (1 << 23), /* force ALPM cap on */
+- AHCI_HFLAG_NO_WRITE_TO_RO = (1 << 24), /* don't write to read
++ AHCI_HFLAG_WAKE_BEFORE_STOP = BIT(22), /* wake before DMA stop */
++ AHCI_HFLAG_YES_ALPM = BIT(23), /* force ALPM cap on */
++ AHCI_HFLAG_NO_WRITE_TO_RO = BIT(24), /* don't write to read
+ only registers */
+- AHCI_HFLAG_IS_MOBILE = (1 << 25), /* mobile chipset, use
++ AHCI_HFLAG_IS_MOBILE = BIT(25), /* mobile chipset, use
+ SATA_MOBILE_LPM_POLICY
+ as default lpm_policy */
+- AHCI_HFLAG_SUSPEND_PHYS = (1 << 26), /* handle PHYs during
++ AHCI_HFLAG_SUSPEND_PHYS = BIT(26), /* handle PHYs during
+ suspend/resume */
+- AHCI_HFLAG_NO_SXS = (1 << 28), /* SXS not supported */
++ AHCI_HFLAG_NO_SXS = BIT(28), /* SXS not supported */
+
+ /* ap->flags bits */
+
+@@ -256,22 +257,22 @@ enum {
+ EM_MAX_RETRY = 5,
+
+ /* em_ctl bits */
+- EM_CTL_RST = (1 << 9), /* Reset */
+- EM_CTL_TM = (1 << 8), /* Transmit Message */
+- EM_CTL_MR = (1 << 0), /* Message Received */
+- EM_CTL_ALHD = (1 << 26), /* Activity LED */
+- EM_CTL_XMT = (1 << 25), /* Transmit Only */
+- EM_CTL_SMB = (1 << 24), /* Single Message Buffer */
+- EM_CTL_SGPIO = (1 << 19), /* SGPIO messages supported */
+- EM_CTL_SES = (1 << 18), /* SES-2 messages supported */
+- EM_CTL_SAFTE = (1 << 17), /* SAF-TE messages supported */
+- EM_CTL_LED = (1 << 16), /* LED messages supported */
++ EM_CTL_RST = BIT(9), /* Reset */
++ EM_CTL_TM = BIT(8), /* Transmit Message */
++ EM_CTL_MR = BIT(0), /* Message Received */
++ EM_CTL_ALHD = BIT(26), /* Activity LED */
++ EM_CTL_XMT = BIT(25), /* Transmit Only */
++ EM_CTL_SMB = BIT(24), /* Single Message Buffer */
++ EM_CTL_SGPIO = BIT(19), /* SGPIO messages supported */
++ EM_CTL_SES = BIT(18), /* SES-2 messages supported */
++ EM_CTL_SAFTE = BIT(17), /* SAF-TE messages supported */
++ EM_CTL_LED = BIT(16), /* LED messages supported */
+
+ /* em message type */
+- EM_MSG_TYPE_LED = (1 << 0), /* LED */
+- EM_MSG_TYPE_SAFTE = (1 << 1), /* SAF-TE */
+- EM_MSG_TYPE_SES2 = (1 << 2), /* SES-2 */
+- EM_MSG_TYPE_SGPIO = (1 << 3), /* SGPIO */
++ EM_MSG_TYPE_LED = BIT(0), /* LED */
++ EM_MSG_TYPE_SAFTE = BIT(1), /* SAF-TE */
++ EM_MSG_TYPE_SES2 = BIT(2), /* SES-2 */
++ EM_MSG_TYPE_SGPIO = BIT(3), /* SGPIO */
+ };
+
+ struct ahci_cmd_hdr {
diff --git a/target/linux/generic/backport-6.1/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch b/target/linux/generic/backport-6.1/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch
new file mode 100644
index 0000000000..e9d692b651
--- /dev/null
+++ b/target/linux/generic/backport-6.1/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch
@@ -0,0 +1,49 @@
+From d3115128bdafb62628ab41861a4f06f6d02ac320 Mon Sep 17 00:00:00 2001
+From: Lech Perczak <lech.perczak@gmail.com>
+Date: Mon, 10 Jan 2022 23:48:44 +0100
+Subject: MIPS: ath79: drop _machine_restart again
+
+Commit 81424d0ad0d4 ("MIPS: ath79: Use the reset controller to restart
+OF machines") removed setup of _machine_restart on OF machines to use
+reset handler in reset controller driver.
+While removing remnants of non-OF machines in commit 3a77e0d75eed
+("MIPS: ath79: drop machfiles"), this was introduced again, making it
+impossible to use additional restart handlers registered through device
+tree. Drop setting _machine_restart altogether, and ath79_restart
+function, which is no longer used after this.
+
+Fixes: 3a77e0d75eed ("MIPS: ath79: drop machfiles")
+Cc: John Crispin <john@phrozen.org>
+Cc: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Lech Perczak <lech.perczak@gmail.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ arch/mips/ath79/setup.c | 10 ----------
+ 1 file changed, 10 deletions(-)
+
+--- a/arch/mips/ath79/setup.c
++++ b/arch/mips/ath79/setup.c
+@@ -34,15 +34,6 @@
+
+ static char ath79_sys_type[ATH79_SYS_TYPE_LEN];
+
+-static void ath79_restart(char *command)
+-{
+- local_irq_disable();
+- ath79_device_reset_set(AR71XX_RESET_FULL_CHIP);
+- for (;;)
+- if (cpu_wait)
+- cpu_wait();
+-}
+-
+ static void ath79_halt(void)
+ {
+ while (1)
+@@ -234,7 +225,6 @@ void __init plat_mem_setup(void)
+
+ detect_memory_region(0, ATH79_MEM_SIZE_MIN, ATH79_MEM_SIZE_MAX);
+
+- _machine_restart = ath79_restart;
+ _machine_halt = ath79_halt;
+ pm_power_off = ath79_halt;
+ }
diff --git a/target/linux/generic/backport-6.1/870-v5.18-hwmon-lm70-Add-ti-tmp125-support.patch b/target/linux/generic/backport-6.1/870-v5.18-hwmon-lm70-Add-ti-tmp125-support.patch
new file mode 100644
index 0000000000..fabf177628
--- /dev/null
+++ b/target/linux/generic/backport-6.1/870-v5.18-hwmon-lm70-Add-ti-tmp125-support.patch
@@ -0,0 +1,71 @@
+From 31d8f414e1596ba54a4315418e4c0086fda9e428 Mon Sep 17 00:00:00 2001
+From: Christian Lamparter <chunkeey@gmail.com>
+Date: Fri, 18 Feb 2022 10:06:43 +0100
+Subject: hwmon: (lm70) Add ti,tmp125 support
+
+The TMP125 is a 2 degree Celsius accurate Digital
+Temperature Sensor with a SPI interface.
+
+The temperature register is a 16-bit, read-only register.
+The MSB (Bit 15) is a leading zero and never set. Bits 14
+to 5 are the 1+9 temperature data bits in a two's
+complement format. Bits 4 to 0 are useless copies of
+Bit 5 value and therefore ignored.
+
+Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
+Link: https://lore.kernel.org/r/43b19cbd4e7f51e9509e561b02b5d8d0e7079fac.1645175187.git.chunkeey@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+--- a/drivers/hwmon/lm70.c
++++ b/drivers/hwmon/lm70.c
+@@ -34,6 +34,7 @@
+ #define LM70_CHIP_LM71 2 /* NS LM71 */
+ #define LM70_CHIP_LM74 3 /* NS LM74 */
+ #define LM70_CHIP_TMP122 4 /* TI TMP122/TMP124 */
++#define LM70_CHIP_TMP125 5 /* TI TMP125 */
+
+ struct lm70 {
+ struct spi_device *spi;
+@@ -87,6 +88,12 @@ static ssize_t temp1_input_show(struct d
+ * LM71:
+ * 14 bits of 2's complement data, discard LSB 2 bits,
+ * resolution 0.0312 degrees celsius.
++ *
++ * TMP125:
++ * MSB/D15 is a leading zero. D14 is the sign-bit. This is
++ * followed by 9 temperature bits (D13..D5) in 2's complement
++ * data format with a resolution of 0.25 degrees celsius per unit.
++ * LSB 5 bits (D4..D0) share the same value as D5 and get discarded.
+ */
+ switch (p_lm70->chip) {
+ case LM70_CHIP_LM70:
+@@ -102,6 +109,10 @@ static ssize_t temp1_input_show(struct d
+ case LM70_CHIP_LM71:
+ val = ((int)raw / 4) * 3125 / 100;
+ break;
++
++ case LM70_CHIP_TMP125:
++ val = (sign_extend32(raw, 14) / 32) * 250;
++ break;
+ }
+
+ status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */
+@@ -136,6 +147,10 @@ static const struct of_device_id lm70_of
+ .data = (void *) LM70_CHIP_TMP122,
+ },
+ {
++ .compatible = "ti,tmp125",
++ .data = (void *) LM70_CHIP_TMP125,
++ },
++ {
+ .compatible = "ti,lm71",
+ .data = (void *) LM70_CHIP_LM71,
+ },
+@@ -184,6 +199,7 @@ static const struct spi_device_id lm70_i
+ { "lm70", LM70_CHIP_LM70 },
+ { "tmp121", LM70_CHIP_TMP121 },
+ { "tmp122", LM70_CHIP_TMP122 },
++ { "tmp125", LM70_CHIP_TMP125 },
+ { "lm71", LM70_CHIP_LM71 },
+ { "lm74", LM70_CHIP_LM74 },
+ { },
diff --git a/target/linux/generic/backport-6.1/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch b/target/linux/generic/backport-6.1/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch
new file mode 100644
index 0000000000..39fdb32773
--- /dev/null
+++ b/target/linux/generic/backport-6.1/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch
@@ -0,0 +1,58 @@
+From a79a5613e1907e1bf09bb6ba6fd5ff43b66c1afe Mon Sep 17 00:00:00 2001
+From: Lech Perczak <lech.perczak@gmail.com>
+Date: Fri, 1 Apr 2022 22:03:55 +0200
+Subject: [PATCH 1/3] cdc_ether: export usbnet_cdc_zte_rx_fixup
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Commit bfe9b9d2df66 ("cdc_ether: Improve ZTE MF823/831/910 handling")
+introduces a workaround for certain ZTE modems reporting invalid MAC
+addresses over CDC-ECM.
+The same issue was present on their RNDIS interface,which was fixed in
+commit a5a18bdf7453 ("rndis_host: Set valid random MAC on buggy devices").
+
+However, internal modem of ZTE MF286R router, on its RNDIS interface, also
+exhibits a second issue fixed already in CDC-ECM, of the device not
+respecting configured random MAC address. In order to share the fixup for
+this with rndis_host driver, export the workaround function, which will
+be re-used in the following commit in rndis_host.
+
+Cc: Kristian Evensen <kristian.evensen@gmail.com>
+Cc: Bjørn Mork <bjorn@mork.no>
+Cc: Oliver Neukum <oliver@neukum.org>
+Signed-off-by: Lech Perczak <lech.perczak@gmail.com>
+---
+ drivers/net/usb/cdc_ether.c | 3 ++-
+ include/linux/usb/usbnet.h | 1 +
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/usb/cdc_ether.c
++++ b/drivers/net/usb/cdc_ether.c
+@@ -479,7 +479,7 @@ static int usbnet_cdc_zte_bind(struct us
+ * device MAC address has been updated). Always set MAC address to that of the
+ * device.
+ */
+-static int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
++int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+ {
+ if (skb->len < ETH_HLEN || !(skb->data[0] & 0x02))
+ return 1;
+@@ -489,6 +489,7 @@ static int usbnet_cdc_zte_rx_fixup(struc
+
+ return 1;
+ }
++EXPORT_SYMBOL_GPL(usbnet_cdc_zte_rx_fixup);
+
+ /* Ensure correct link state
+ *
+--- a/include/linux/usb/usbnet.h
++++ b/include/linux/usb/usbnet.h
+@@ -214,6 +214,7 @@ extern int usbnet_ether_cdc_bind(struct
+ extern int usbnet_cdc_bind(struct usbnet *, struct usb_interface *);
+ extern void usbnet_cdc_unbind(struct usbnet *, struct usb_interface *);
+ extern void usbnet_cdc_status(struct usbnet *, struct urb *);
++extern int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb);
+
+ /* CDC and RNDIS support the same host-chosen packet filters for IN transfers */
+ #define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
diff --git a/target/linux/generic/backport-6.1/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch b/target/linux/generic/backport-6.1/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch
new file mode 100644
index 0000000000..71a6df8102
--- /dev/null
+++ b/target/linux/generic/backport-6.1/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch
@@ -0,0 +1,118 @@
+From aa8aff10e969aca0cb64f5e54ff7489355582667 Mon Sep 17 00:00:00 2001
+From: Lech Perczak <lech.perczak@gmail.com>
+Date: Fri, 1 Apr 2022 22:04:01 +0200
+Subject: [PATCH 2/3] rndis_host: enable the bogus MAC fixup for ZTE devices
+ from cdc_ether
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Certain ZTE modems, namely: MF823. MF831, MF910, built-in modem from
+MF286R, expose both CDC-ECM and RNDIS network interfaces.
+They have a trait of ignoring the locally-administered MAC address
+configured on the interface both in CDC-ECM and RNDIS part,
+and this leads to dropping of incoming traffic by the host.
+However, the workaround was only present in CDC-ECM, and MF286R
+explicitly requires it in RNDIS mode.
+
+Re-use the workaround in rndis_host as well, to fix operation of MF286R
+module, some versions of which expose only the RNDIS interface. Do so by
+introducing new flag, RNDIS_DRIVER_DATA_DST_MAC_FIXUP, and testing for it
+in rndis_rx_fixup. This is required, as RNDIS uses frame batching, and all
+of the packets inside the batch need the fixup. This might introduce a
+performance penalty, because test is done for every returned Ethernet
+frame.
+
+Apply the workaround to both "flavors" of RNDIS interfaces, as older ZTE
+modems, like MF823 found in the wild, report the USB_CLASS_COMM class
+interfaces, while MF286R reports USB_CLASS_WIRELESS_CONTROLLER.
+
+Suggested-by: Bjørn Mork <bjorn@mork.no>
+Cc: Kristian Evensen <kristian.evensen@gmail.com>
+Cc: Oliver Neukum <oliver@neukum.org>
+Signed-off-by: Lech Perczak <lech.perczak@gmail.com>
+---
+ drivers/net/usb/rndis_host.c | 32 ++++++++++++++++++++++++++++++++
+ include/linux/usb/rndis_host.h | 1 +
+ 2 files changed, 33 insertions(+)
+
+--- a/drivers/net/usb/rndis_host.c
++++ b/drivers/net/usb/rndis_host.c
+@@ -486,10 +486,14 @@ EXPORT_SYMBOL_GPL(rndis_unbind);
+ */
+ int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+ {
++ bool dst_mac_fixup;
++
+ /* This check is no longer done by usbnet */
+ if (skb->len < dev->net->hard_header_len)
+ return 0;
+
++ dst_mac_fixup = !!(dev->driver_info->data & RNDIS_DRIVER_DATA_DST_MAC_FIXUP);
++
+ /* peripheral may have batched packets to us... */
+ while (likely(skb->len)) {
+ struct rndis_data_hdr *hdr = (void *)skb->data;
+@@ -524,10 +528,17 @@ int rndis_rx_fixup(struct usbnet *dev, s
+ break;
+ skb_pull(skb, msg_len - sizeof *hdr);
+ skb_trim(skb2, data_len);
++
++ if (unlikely(dst_mac_fixup))
++ usbnet_cdc_zte_rx_fixup(dev, skb2);
++
+ usbnet_skb_return(dev, skb2);
+ }
+
+ /* caller will usbnet_skb_return the remaining packet */
++ if (unlikely(dst_mac_fixup))
++ usbnet_cdc_zte_rx_fixup(dev, skb);
++
+ return 1;
+ }
+ EXPORT_SYMBOL_GPL(rndis_rx_fixup);
+@@ -601,6 +612,17 @@ static const struct driver_info rndis_po
+ .tx_fixup = rndis_tx_fixup,
+ };
+
++static const struct driver_info zte_rndis_info = {
++ .description = "ZTE RNDIS device",
++ .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
++ .data = RNDIS_DRIVER_DATA_DST_MAC_FIXUP,
++ .bind = rndis_bind,
++ .unbind = rndis_unbind,
++ .status = rndis_status,
++ .rx_fixup = rndis_rx_fixup,
++ .tx_fixup = rndis_tx_fixup,
++};
++
+ /*-------------------------------------------------------------------------*/
+
+ static const struct usb_device_id products [] = {
+@@ -615,6 +637,16 @@ static const struct usb_device_id produc
+ USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+ .driver_info = (unsigned long)&rndis_info,
+ }, {
++ /* ZTE WWAN modules */
++ USB_VENDOR_AND_INTERFACE_INFO(0x19d2,
++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
++ .driver_info = (unsigned long)&zte_rndis_info,
++}, {
++ /* ZTE WWAN modules, ACM flavour */
++ USB_VENDOR_AND_INTERFACE_INFO(0x19d2,
++ USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
++ .driver_info = (unsigned long)&zte_rndis_info,
++}, {
+ /* RNDIS is MSFT's un-official variant of CDC ACM */
+ USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+ .driver_info = (unsigned long) &rndis_info,
+--- a/include/linux/usb/rndis_host.h
++++ b/include/linux/usb/rndis_host.h
+@@ -197,6 +197,7 @@ struct rndis_keepalive_c { /* IN (option
+
+ /* Flags for driver_info::data */
+ #define RNDIS_DRIVER_DATA_POLL_STATUS 1 /* poll status before control */
++#define RNDIS_DRIVER_DATA_DST_MAC_FIXUP 2 /* device ignores configured MAC address */
+
+ extern void rndis_status(struct usbnet *dev, struct urb *urb);
+ extern int
diff --git a/target/linux/generic/backport-6.1/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch b/target/linux/generic/backport-6.1/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch
new file mode 100644
index 0000000000..bdd812cc90
--- /dev/null
+++ b/target/linux/generic/backport-6.1/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch
@@ -0,0 +1,63 @@
+From 9bfb4bcda7ba32d73ea322ea56a8ebe32e9247f6 Mon Sep 17 00:00:00 2001
+From: Lech Perczak <lech.perczak@gmail.com>
+Date: Sat, 2 Apr 2022 02:19:57 +0200
+Subject: [PATCH 3/3] rndis_host: limit scope of bogus MAC address detection to
+ ZTE devices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reporting of bogus MAC addresses and ignoring configuration of new
+destination address wasn't observed outside of a range of ZTE devices,
+among which this seems to be the common bug. Align rndis_host driver
+with implementation found in cdc_ether, which also limits this workaround
+to ZTE devices.
+
+Suggested-by: Bjørn Mork <bjorn@mork.no>
+Cc: Kristian Evensen <kristian.evensen@gmail.com>
+Cc: Oliver Neukum <oliver@neukum.org>
+Signed-off-by: Lech Perczak <lech.perczak@gmail.com>
+---
+ drivers/net/usb/rndis_host.c | 17 ++++++++++++-----
+ 1 file changed, 12 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/usb/rndis_host.c
++++ b/drivers/net/usb/rndis_host.c
+@@ -419,10 +419,7 @@ generic_rndis_bind(struct usbnet *dev, s
+ goto halt_fail_and_release;
+ }
+
+- if (bp[0] & 0x02)
+- eth_hw_addr_random(net);
+- else
+- ether_addr_copy(net->dev_addr, bp);
++ ether_addr_copy(net->dev_addr, bp);
+
+ /* set a nonzero filter to enable data transfers */
+ memset(u.set, 0, sizeof *u.set);
+@@ -464,6 +461,16 @@ static int rndis_bind(struct usbnet *dev
+ return generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_NOT_WIRELESS);
+ }
+
++static int zte_rndis_bind(struct usbnet *dev, struct usb_interface *intf)
++{
++ int status = rndis_bind(dev, intf);
++
++ if (!status && (dev->net->dev_addr[0] & 0x02))
++ eth_hw_addr_random(dev->net);
++
++ return status;
++}
++
+ void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
+ {
+ struct rndis_halt *halt;
+@@ -616,7 +623,7 @@ static const struct driver_info zte_rndi
+ .description = "ZTE RNDIS device",
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .data = RNDIS_DRIVER_DATA_DST_MAC_FIXUP,
+- .bind = rndis_bind,
++ .bind = zte_rndis_bind,
+ .unbind = rndis_unbind,
+ .status = rndis_status,
+ .rx_fixup = rndis_rx_fixup,
diff --git a/target/linux/generic/backport-6.1/890-v6.1-mtd-spinand-winbond-fix-flash-detection.patch b/target/linux/generic/backport-6.1/890-v6.1-mtd-spinand-winbond-fix-flash-detection.patch
new file mode 100644
index 0000000000..38fbc3a3d7
--- /dev/null
+++ b/target/linux/generic/backport-6.1/890-v6.1-mtd-spinand-winbond-fix-flash-detection.patch
@@ -0,0 +1,40 @@
+From dbf70fc204d2fbb0d8ad8f42038a60846502efda Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Mon, 10 Oct 2022 13:51:09 +0300
+Subject: [PATCH] mtd: spinand: winbond: fix flash identification
+
+Winbond uses 3 bytes to identify flash: vendor_id, dev_id_0, dev_id_1,
+but current driver uses only first 2 bytes of it for devices
+identification. As result Winbond W25N02KV flash (id_bytes: EF, AA, 22)
+is identified as W25N01GV (id_bytes: EF, AA, 21).
+
+Fix this by adding missed identification bytes.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20221010105110.446674-1-mikhail.kshevetskiy@iopsys.eu
+---
+ drivers/mtd/nand/spi/winbond.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/nand/spi/winbond.c
++++ b/drivers/mtd/nand/spi/winbond.c
+@@ -76,7 +76,7 @@ static int w25m02gv_select_target(struct
+
+ static const struct spinand_info winbond_spinand_table[] = {
+ SPINAND_INFO("W25M02GV",
+- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab),
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -86,7 +86,7 @@ static const struct spinand_info winbond
+ SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
+ SPINAND_SELECT_TARGET(w25m02gv_select_target)),
+ SPINAND_INFO("W25N01GV",
+- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa),
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
diff --git a/target/linux/generic/backport-6.1/891-v6.1-mtd-spinand-winbond-add-W25N02KV.patch b/target/linux/generic/backport-6.1/891-v6.1-mtd-spinand-winbond-add-W25N02KV.patch
new file mode 100644
index 0000000000..d75a1acc57
--- /dev/null
+++ b/target/linux/generic/backport-6.1/891-v6.1-mtd-spinand-winbond-add-W25N02KV.patch
@@ -0,0 +1,106 @@
+From 6154c7a583483d7b69f53bea868efdc369edd563 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Mon, 10 Oct 2022 13:51:10 +0300
+Subject: [PATCH] mtd: spinand: winbond: add Winbond W25N02KV flash support
+
+Add support of Winbond W25N02KV flash
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20221010105110.446674-2-mikhail.kshevetskiy@iopsys.eu
+---
+ drivers/mtd/nand/spi/winbond.c | 75 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 75 insertions(+)
+
+--- a/drivers/mtd/nand/spi/winbond.c
++++ b/drivers/mtd/nand/spi/winbond.c
+@@ -74,6 +74,72 @@ static int w25m02gv_select_target(struct
+ return spi_mem_exec_op(spinand->spimem, &op);
+ }
+
++static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = 64 + (16 * section);
++ region->length = 13;
++
++ return 0;
++}
++
++static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = (16 * section) + 2;
++ region->length = 14;
++
++ return 0;
++}
++
++static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
++ .ecc = w25n02kv_ooblayout_ecc,
++ .free = w25n02kv_ooblayout_free,
++};
++
++static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
++ u8 status)
++{
++ struct nand_device *nand = spinand_to_nand(spinand);
++ u8 mbf = 0;
++ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
++
++ switch (status & STATUS_ECC_MASK) {
++ case STATUS_ECC_NO_BITFLIPS:
++ return 0;
++
++ case STATUS_ECC_UNCOR_ERROR:
++ return -EBADMSG;
++
++ case STATUS_ECC_HAS_BITFLIPS:
++ /*
++ * Let's try to retrieve the real maximum number of bitflips
++ * in order to avoid forcing the wear-leveling layer to move
++ * data around if it's not necessary.
++ */
++ if (spi_mem_exec_op(spinand->spimem, &op))
++ return nanddev_get_ecc_conf(nand)->strength;
++
++ mbf >>= 4;
++
++ if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf))
++ return nanddev_get_ecc_conf(nand)->strength;
++
++ return mbf;
++
++ default:
++ break;
++ }
++
++ return -EINVAL;
++}
++
+ static const struct spinand_info winbond_spinand_table[] = {
+ SPINAND_INFO("W25M02GV",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
+@@ -94,6 +160,15 @@ static const struct spinand_info winbond
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
++ SPINAND_INFO("W25N02KV",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
++ 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),
++ 0,
++ SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
+ };
+
+ static int winbond_spinand_init(struct spinand_device *spinand)
diff --git a/target/linux/generic/config-6.1 b/target/linux/generic/config-6.1
new file mode 100644
index 0000000000..f13af8ba26
--- /dev/null
+++ b/target/linux/generic/config-6.1
@@ -0,0 +1,7591 @@
+# CONFIG_104_QUAD_8 is not set
+CONFIG_32BIT=y
+CONFIG_64BIT_TIME=y
+# CONFIG_6LOWPAN is not set
+# CONFIG_6LOWPAN_DEBUGFS is not set
+# CONFIG_6PACK is not set
+# CONFIG_8139CP is not set
+# CONFIG_8139TOO is not set
+# CONFIG_9P_FS is not set
+# CONFIG_AB3100_CORE is not set
+# CONFIG_AB8500_CORE is not set
+# CONFIG_ABP060MG is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_ACENIC is not set
+# CONFIG_ACERHDF is not set
+# CONFIG_ACER_WIRELESS is not set
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_ACPI_ALS is not set
+# CONFIG_ACPI_APEI is not set
+# CONFIG_ACPI_APEI_PCIEAER is not set
+# CONFIG_ACPI_BUTTON is not set
+# CONFIG_ACPI_CONFIGFS is not set
+# CONFIG_ACPI_CUSTOM_METHOD is not set
+# CONFIG_ACPI_EXTLOG is not set
+# CONFIG_ACPI_HED is not set
+# CONFIG_ACPI_NFIT is not set
+# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set
+# CONFIG_ACPI_TABLE_UPGRADE is not set
+# CONFIG_ACPI_VIDEO is not set
+# CONFIG_AD2S1200 is not set
+# CONFIG_AD2S1210 is not set
+# CONFIG_AD2S90 is not set
+# CONFIG_AD5064 is not set
+# CONFIG_AD5110 is not set
+# CONFIG_AD525X_DPOT is not set
+# CONFIG_AD5272 is not set
+# CONFIG_AD5360 is not set
+# CONFIG_AD5380 is not set
+# CONFIG_AD5421 is not set
+# CONFIG_AD5446 is not set
+# CONFIG_AD5449 is not set
+# CONFIG_AD5504 is not set
+# CONFIG_AD5592R is not set
+# CONFIG_AD5593R is not set
+# CONFIG_AD5624R_SPI is not set
+# CONFIG_AD5686 is not set
+# CONFIG_AD5686_SPI is not set
+# CONFIG_AD5696_I2C is not set
+# CONFIG_AD5755 is not set
+# CONFIG_AD5758 is not set
+# CONFIG_AD5761 is not set
+# CONFIG_AD5764 is not set
+# CONFIG_AD5766 is not set
+# CONFIG_AD5770R is not set
+# CONFIG_AD5791 is not set
+# CONFIG_AD5933 is not set
+# CONFIG_AD7091R5 is not set
+# CONFIG_AD7124 is not set
+# CONFIG_AD7150 is not set
+# CONFIG_AD7152 is not set
+# CONFIG_AD7192 is not set
+# CONFIG_AD7266 is not set
+# CONFIG_AD7280 is not set
+# CONFIG_AD7291 is not set
+# CONFIG_AD7292 is not set
+# CONFIG_AD7298 is not set
+# CONFIG_AD7303 is not set
+# CONFIG_AD7476 is not set
+# CONFIG_AD7606 is not set
+# CONFIG_AD7606_IFACE_PARALLEL is not set
+# CONFIG_AD7606_IFACE_SPI is not set
+# CONFIG_AD7746 is not set
+# CONFIG_AD7766 is not set
+# CONFIG_AD7768_1 is not set
+# CONFIG_AD7780 is not set
+# CONFIG_AD7791 is not set
+# CONFIG_AD7793 is not set
+# CONFIG_AD7816 is not set
+# CONFIG_AD7887 is not set
+# CONFIG_AD7923 is not set
+# CONFIG_AD7949 is not set
+# CONFIG_AD799X is not set
+# CONFIG_AD8366 is not set
+# CONFIG_AD8801 is not set
+# CONFIG_AD9467 is not set
+# CONFIG_AD9523 is not set
+# CONFIG_AD9832 is not set
+# CONFIG_AD9834 is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_ADE7854 is not set
+# CONFIG_ADF4350 is not set
+# CONFIG_ADF4371 is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADIN_PHY is not set
+# CONFIG_ADIS16080 is not set
+# CONFIG_ADIS16130 is not set
+# CONFIG_ADIS16136 is not set
+# CONFIG_ADIS16201 is not set
+# CONFIG_ADIS16203 is not set
+# CONFIG_ADIS16209 is not set
+# CONFIG_ADIS16240 is not set
+# CONFIG_ADIS16260 is not set
+# CONFIG_ADIS16400 is not set
+# CONFIG_ADIS16460 is not set
+# CONFIG_ADIS16475 is not set
+# CONFIG_ADIS16480 is not set
+# CONFIG_ADI_AXI_ADC is not set
+# CONFIG_ADJD_S311 is not set
+# CONFIG_ADM6996_PHY is not set
+# CONFIG_ADM8211 is not set
+# CONFIG_ADT7316 is not set
+# CONFIG_ADUX1020 is not set
+CONFIG_ADVISE_SYSCALLS=y
+# CONFIG_ADXL345_I2C is not set
+# CONFIG_ADXL345_SPI is not set
+# CONFIG_ADXL372_I2C is not set
+# CONFIG_ADXL372_SPI is not set
+# CONFIG_ADXRS290 is not set
+# CONFIG_ADXRS450 is not set
+CONFIG_AEABI=y
+# CONFIG_AFE4403 is not set
+# CONFIG_AFE4404 is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_AFS_DEBUG_CURSOR is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_AF_KCM is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_AF_RXRPC_INJECT_LOSS is not set
+# CONFIG_AF_RXRPC_IPV6 is not set
+CONFIG_AF_UNIX_OOB=y
+# CONFIG_AGP is not set
+# CONFIG_AHCI_BRCM is not set
+# CONFIG_AHCI_CEVA is not set
+# CONFIG_AHCI_IMX is not set
+# CONFIG_AHCI_MVEBU is not set
+# CONFIG_AHCI_QORIQ is not set
+# CONFIG_AHCI_XGENE is not set
+CONFIG_AIO=y
+# CONFIG_AIRO is not set
+# CONFIG_AIRO_CS is not set
+# CONFIG_AIX_PARTITION is not set
+# CONFIG_AK09911 is not set
+# CONFIG_AK8974 is not set
+# CONFIG_AK8975 is not set
+# CONFIG_AL3010 is not set
+# CONFIG_AL3320A is not set
+# CONFIG_ALIM7101_WDT is not set
+CONFIG_ALLOW_DEV_COREDUMP=y
+# CONFIG_ALTERA_MBOX is not set
+# CONFIG_ALTERA_MSGDMA is not set
+# CONFIG_ALTERA_STAPL is not set
+# CONFIG_ALTERA_TSE is not set
+# CONFIG_ALX is not set
+# CONFIG_AL_FIC is not set
+# CONFIG_AM2315 is not set
+# CONFIG_AM335X_PHY_USB is not set
+# CONFIG_AMBA_PL08X is not set
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_AMD_MEM_ENCRYPT is not set
+# CONFIG_AMD_PHY is not set
+# CONFIG_AMD_XGBE is not set
+# CONFIG_AMD_XGBE_HAVE_ECC is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_AMILO_RFKILL is not set
+# CONFIG_ANDROID is not set
+CONFIG_ANON_INODES=y
+# CONFIG_APDS9300 is not set
+# CONFIG_APDS9802ALS is not set
+# CONFIG_APDS9960 is not set
+# CONFIG_APM8018X is not set
+# CONFIG_APM_EMULATION is not set
+# CONFIG_APPLE_GMUX is not set
+# CONFIG_APPLE_MFI_FASTCHARGE is not set
+# CONFIG_APPLE_PROPERTIES is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_AQTION is not set
+# CONFIG_AQUANTIA_PHY is not set
+# CONFIG_AR5523 is not set
+# CONFIG_AR7 is not set
+# CONFIG_AR8216_PHY is not set
+# CONFIG_AR8216_PHY_LEDS is not set
+# CONFIG_ARCH_ACTIONS is not set
+# CONFIG_ARCH_AGILEX is not set
+# CONFIG_ARCH_ALPINE is not set
+# CONFIG_ARCH_APPLE is not set
+# CONFIG_ARCH_ARTPEC is not set
+# CONFIG_ARCH_ASPEED is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_AXXIA is not set
+# CONFIG_ARCH_BCM is not set
+# CONFIG_ARCH_BCM2835 is not set
+# CONFIG_ARCH_BCM4908 is not set
+# CONFIG_ARCH_BCM_21664 is not set
+# CONFIG_ARCH_BCM_23550 is not set
+# CONFIG_ARCH_BCM_281XX is not set
+# CONFIG_ARCH_BCM_5301X is not set
+# CONFIG_ARCH_BCM_53573 is not set
+# CONFIG_ARCH_BCM_63XX is not set
+# CONFIG_ARCH_BCM_CYGNUS is not set
+# CONFIG_ARCH_BCM_IPROC is not set
+# CONFIG_ARCH_BCM_NSP is not set
+# CONFIG_ARCH_BERLIN is not set
+CONFIG_ARCH_BINFMT_ELF_STATE=y
+# CONFIG_ARCH_BITMAIN is not set
+# CONFIG_ARCH_BRCMSTB is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CNS3XXX is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_DIGICOLOR is not set
+# CONFIG_ARCH_DMA_ADDR_T_64BIT is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_EXYNOS is not set
+CONFIG_ARCH_FLATMEM_ENABLE=y
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_HI3xxx is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_HISI is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_INTEL_SOCFPGA is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_K3 is not set
+# CONFIG_ARCH_KEEMBAY is not set
+# CONFIG_ARCH_KEYSTONE is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_LAYERSCAPE is not set
+# CONFIG_ARCH_LG1K is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_MEDIATEK is not set
+# CONFIG_ARCH_MESON is not set
+# CONFIG_ARCH_MILBEAUT is not set
+CONFIG_ARCH_MMAP_RND_BITS=8
+CONFIG_ARCH_MMAP_RND_BITS_MAX=16
+CONFIG_ARCH_MMAP_RND_BITS_MIN=8
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_MSTARV7 is not set
+# CONFIG_ARCH_MULTIPLATFORM is not set
+# CONFIG_ARCH_MULTI_V6 is not set
+# CONFIG_ARCH_MULTI_V7 is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MVEBU is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_MXS is not set
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_NPCM is not set
+# CONFIG_ARCH_NSPIRE is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_OMAP1 is not set
+# CONFIG_ARCH_OMAP2 is not set
+# CONFIG_ARCH_OMAP2PLUS is not set
+# CONFIG_ARCH_OMAP3 is not set
+# CONFIG_ARCH_OMAP4 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_OXNAS is not set
+# CONFIG_ARCH_PICOXCELL is not set
+# CONFIG_ARCH_PRIMA2 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_QCOM is not set
+# CONFIG_ARCH_RANDOM is not set
+# CONFIG_ARCH_RDA is not set
+# CONFIG_ARCH_REALTEK is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_RENESAS is not set
+# CONFIG_ARCH_ROCKCHIP is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_S32 is not set
+# CONFIG_ARCH_S3C24XX is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_SEATTLE is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_SIRF is not set
+# CONFIG_ARCH_SOCFPGA is not set
+# CONFIG_ARCH_SPARX5 is not set
+# CONFIG_ARCH_SPRD is not set
+# CONFIG_ARCH_STI is not set
+# CONFIG_ARCH_STM32 is not set
+# CONFIG_ARCH_STRATIX10 is not set
+# CONFIG_ARCH_SUNXI is not set
+# CONFIG_ARCH_SYNQUACER is not set
+# CONFIG_ARCH_TANGO is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_THUNDER is not set
+# CONFIG_ARCH_THUNDER2 is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_UNIPHIER is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_VIRT is not set
+# CONFIG_ARCH_VISCONTI is not set
+# CONFIG_ARCH_VT8500 is not set
+# CONFIG_ARCH_VULCAN is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_WANTS_THP_SWAP is not set
+# CONFIG_ARCH_WM8505 is not set
+# CONFIG_ARCH_WM8750 is not set
+# CONFIG_ARCH_WM8850 is not set
+# CONFIG_ARCH_XGENE is not set
+# CONFIG_ARCH_ZX is not set
+# CONFIG_ARCH_ZYNQ is not set
+# CONFIG_ARCH_ZYNQMP is not set
+# CONFIG_ARCNET is not set
+# CONFIG_ARC_EMAC is not set
+# CONFIG_ARC_IRQ_NO_AUTOSAVE is not set
+# CONFIG_ARM64_16K_PAGES is not set
+# CONFIG_ARM64_64K_PAGES is not set
+# CONFIG_ARM64_AMU_EXTN is not set
+# CONFIG_ARM64_BTI is not set
+# CONFIG_ARM64_CRYPTO is not set
+# CONFIG_ARM64_E0PD is not set
+# CONFIG_ARM64_ERRATUM_1024718 is not set
+# CONFIG_ARM64_ERRATUM_1165522 is not set
+# CONFIG_ARM64_ERRATUM_1286807 is not set
+# CONFIG_ARM64_ERRATUM_1319367 is not set
+# CONFIG_ARM64_ERRATUM_1418040 is not set
+# CONFIG_ARM64_ERRATUM_1463225 is not set
+# CONFIG_ARM64_ERRATUM_1508412 is not set
+# CONFIG_ARM64_ERRATUM_1530923 is not set
+# CONFIG_ARM64_ERRATUM_1542419 is not set
+# CONFIG_ARM64_ERRATUM_1742098 is not set
+# CONFIG_ARM64_ERRATUM_2441007 is not set
+# CONFIG_ARM64_ERRATUM_2441009 is not set
+# CONFIG_ARM64_ERRATUM_819472 is not set
+# CONFIG_ARM64_ERRATUM_824069 is not set
+# CONFIG_ARM64_ERRATUM_826319 is not set
+# CONFIG_ARM64_ERRATUM_827319 is not set
+# CONFIG_ARM64_ERRATUM_832075 is not set
+# CONFIG_ARM64_ERRATUM_834220 is not set
+# CONFIG_ARM64_ERRATUM_843419 is not set
+# CONFIG_ARM64_ERRATUM_845719 is not set
+# CONFIG_ARM64_ERRATUM_858921 is not set
+# CONFIG_ARM64_HW_AFDBM is not set
+# CONFIG_ARM64_LSE_ATOMICS is not set
+# CONFIG_ARM64_MODULE_PLTS is not set
+# CONFIG_ARM64_MTE is not set
+# CONFIG_ARM64_PAN is not set
+# CONFIG_ARM64_PMEM is not set
+# CONFIG_ARM64_PSEUDO_NMI is not set
+# CONFIG_ARM64_PTDUMP_DEBUGFS is not set
+# CONFIG_ARM64_PTR_AUTH is not set
+# CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET is not set
+# CONFIG_ARM64_RAS_EXTN is not set
+# CONFIG_ARM64_RELOC_TEST is not set
+# CONFIG_ARM64_SVE is not set
+CONFIG_ARM64_SW_TTBR0_PAN=y
+# CONFIG_ARM64_TLB_RANGE is not set
+# CONFIG_ARM64_UAO is not set
+# CONFIG_ARM64_USE_LSE_ATOMICS is not set
+# CONFIG_ARM64_VA_BITS_48 is not set
+# CONFIG_ARM64_VHE is not set
+# CONFIG_ARM_APPENDED_DTB is not set
+# CONFIG_ARM_ARCH_TIMER is not set
+# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set
+# CONFIG_ARM_BIG_LITTLE_CPUFREQ is not set
+# CONFIG_ARM_CCI is not set
+# CONFIG_ARM_CCI400_PMU is not set
+# CONFIG_ARM_CCI5xx_PMU is not set
+# CONFIG_ARM_CCI_PMU is not set
+# CONFIG_ARM_CCN is not set
+# CONFIG_ARM_CMN is not set
+# CONFIG_ARM_CPUIDLE is not set
+CONFIG_ARM_CPU_TOPOLOGY=y
+# CONFIG_ARM_CRYPTO is not set
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+# CONFIG_ARM_DSU_PMU is not set
+# CONFIG_ARM_ERRATA_326103 is not set
+# CONFIG_ARM_ERRATA_364296 is not set
+# CONFIG_ARM_ERRATA_411920 is not set
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+# CONFIG_ARM_ERRATA_643719 is not set
+# CONFIG_ARM_ERRATA_720789 is not set
+# CONFIG_ARM_ERRATA_742230 is not set
+# CONFIG_ARM_ERRATA_742231 is not set
+# CONFIG_ARM_ERRATA_743622 is not set
+# CONFIG_ARM_ERRATA_751472 is not set
+# CONFIG_ARM_ERRATA_754322 is not set
+# CONFIG_ARM_ERRATA_754327 is not set
+# CONFIG_ARM_ERRATA_764369 is not set
+# CONFIG_ARM_ERRATA_773022 is not set
+# CONFIG_ARM_ERRATA_775420 is not set
+# CONFIG_ARM_ERRATA_798181 is not set
+# CONFIG_ARM_ERRATA_814220 is not set
+# CONFIG_ARM_ERRATA_818325_852422 is not set
+# CONFIG_ARM_ERRATA_821420 is not set
+# CONFIG_ARM_ERRATA_825619 is not set
+# CONFIG_ARM_ERRATA_852421 is not set
+# CONFIG_ARM_ERRATA_852423 is not set
+# CONFIG_ARM_ERRATA_857271 is not set
+# CONFIG_ARM_ERRATA_857272 is not set
+# CONFIG_ARM_FFA_TRANSPORT is not set
+CONFIG_ARM_GIC_MAX_NR=1
+# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
+# CONFIG_ARM_KPROBES_TEST is not set
+# CONFIG_ARM_LPAE is not set
+# CONFIG_ARM_MEDIATEK_CPUFREQ_HW is not set
+# CONFIG_ARM_MHU is not set
+# CONFIG_ARM_MODULE_PLTS is not set
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+# CONFIG_ARM_PSCI is not set
+# CONFIG_ARM_PSCI_CHECKER is not set
+# CONFIG_ARM_PSCI_CPUIDLE is not set
+# CONFIG_ARM_PTDUMP_DEBUGFS is not set
+# CONFIG_ARM_SBSA_WATCHDOG is not set
+# CONFIG_ARM_SCMI_PROTOCOL is not set
+# CONFIG_ARM_SCPI_PROTOCOL is not set
+# CONFIG_ARM_SDE_INTERFACE is not set
+# CONFIG_ARM_SMCCC_SOC_ID is not set
+# CONFIG_ARM_SMC_WATCHDOG is not set
+# CONFIG_ARM_SP805_WATCHDOG is not set
+# CONFIG_ARM_SPE_PMU is not set
+# CONFIG_ARM_THUMBEE is not set
+# CONFIG_ARM_TIMER_SP804 is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_ARM_VIRT_EXT is not set
+# CONFIG_AS3935 is not set
+# CONFIG_AS73211 is not set
+# CONFIG_ASM9260_TIMER is not set
+# CONFIG_ASN1 is not set
+# CONFIG_ASUS_LAPTOP is not set
+# CONFIG_ASUS_WIRELESS is not set
+# CONFIG_ASYMMETRIC_KEY_TYPE is not set
+# CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE is not set
+# CONFIG_ASYNC_RAID6_TEST is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_AT76C50X_USB is not set
+# CONFIG_AT803X_PHY is not set
+# CONFIG_AT91_SAMA5D2_ADC is not set
+# CONFIG_ATA is not set
+# CONFIG_ATAGS is not set
+CONFIG_ATAGS_PROC=y
+# CONFIG_ATALK is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_ATA_ACPI is not set
+CONFIG_ATA_BMDMA=y
+# CONFIG_ATA_FORCE is not set
+# CONFIG_ATA_GENERIC is not set
+# CONFIG_ATA_LEDS is not set
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_ATA_PIIX is not set
+CONFIG_ATA_SFF=y
+# CONFIG_ATA_VERBOSE_ERROR is not set
+# CONFIG_ATH10K is not set
+# CONFIG_ATH25 is not set
+# CONFIG_ATH5K is not set
+# CONFIG_ATH6KL is not set
+# CONFIG_ATH79 is not set
+# CONFIG_ATH9K is not set
+# CONFIG_ATH9K_HTC is not set
+# CONFIG_ATH_DEBUG is not set
+# CONFIG_ATL1 is not set
+# CONFIG_ATL1C is not set
+# CONFIG_ATL1E is not set
+# CONFIG_ATL2 is not set
+# CONFIG_ATLAS_EZO_SENSOR is not set
+# CONFIG_ATLAS_PH_SENSOR is not set
+# CONFIG_ATM is not set
+# CONFIG_ATMEL is not set
+# CONFIG_ATMEL_PIT is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ATM_AMBASSADOR is not set
+# CONFIG_ATM_BR2684 is not set
+CONFIG_ATM_BR2684_IPFILTER=y
+# CONFIG_ATM_CLIP is not set
+CONFIG_ATM_CLIP_NO_ICMP=y
+# CONFIG_ATM_DRIVERS is not set
+# CONFIG_ATM_DUMMY is not set
+# CONFIG_ATM_ENI is not set
+# CONFIG_ATM_FIRESTREAM is not set
+# CONFIG_ATM_FORE200E is not set
+# CONFIG_ATM_HE is not set
+# CONFIG_ATM_HORIZON is not set
+# CONFIG_ATM_IA is not set
+# CONFIG_ATM_IDT77252 is not set
+# CONFIG_ATM_LANAI is not set
+# CONFIG_ATM_LANE is not set
+# CONFIG_ATM_MPOA is not set
+# CONFIG_ATM_NICSTAR is not set
+# CONFIG_ATM_SOLOS is not set
+# CONFIG_ATM_TCP is not set
+# CONFIG_ATM_ZATM is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_ATP is not set
+# CONFIG_AUDIT is not set
+# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set
+# CONFIG_AURORA_NB8800 is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTO_ZRELADDR is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_AX25 is not set
+# CONFIG_AX25_DAMA_SLAVE is not set
+# CONFIG_AX88796 is not set
+# CONFIG_AX88796B_PHY is not set
+# CONFIG_AXP20X_ADC is not set
+# CONFIG_AXP20X_POWER is not set
+# CONFIG_AXP288_ADC is not set
+# CONFIG_AXP288_FUEL_GAUGE is not set
+# CONFIG_B43 is not set
+# CONFIG_B43LEGACY is not set
+# CONFIG_B44 is not set
+# CONFIG_B53 is not set
+# CONFIG_B53_MDIO_DRIVER is not set
+# CONFIG_B53_MMAP_DRIVER is not set
+# CONFIG_B53_SERDES is not set
+# CONFIG_B53_SPI_DRIVER is not set
+# CONFIG_B53_SRAB_DRIVER is not set
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+# CONFIG_BACKLIGHT_APPLE is not set
+# CONFIG_BACKLIGHT_ARCXCNN is not set
+# CONFIG_BACKLIGHT_BD6107 is not set
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
+# CONFIG_BACKLIGHT_GENERIC is not set
+# CONFIG_BACKLIGHT_GPIO is not set
+# CONFIG_BACKLIGHT_KTD253 is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_BACKLIGHT_LED is not set
+# CONFIG_BACKLIGHT_LM3630A is not set
+# CONFIG_BACKLIGHT_LM3639 is not set
+# CONFIG_BACKLIGHT_LP855X is not set
+# CONFIG_BACKLIGHT_LV5207LP is not set
+# CONFIG_BACKLIGHT_PANDORA is not set
+# CONFIG_BACKLIGHT_PM8941_WLED is not set
+# CONFIG_BACKLIGHT_PWM is not set
+# CONFIG_BACKLIGHT_QCOM_WLED is not set
+# CONFIG_BACKLIGHT_RPI is not set
+# CONFIG_BACKLIGHT_SAHARA is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_BAREUDP is not set
+CONFIG_BASE_FULL=y
+CONFIG_BASE_SMALL=0
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_BATTERY_BQ27XXX is not set
+# CONFIG_BATTERY_BQ27XXX_HDQ is not set
+# CONFIG_BATTERY_CW2015 is not set
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_BATTERY_DS2780 is not set
+# CONFIG_BATTERY_DS2781 is not set
+# CONFIG_BATTERY_DS2782 is not set
+# CONFIG_BATTERY_GAUGE_LTC2941 is not set
+# CONFIG_BATTERY_GOLDFISH is not set
+# CONFIG_BATTERY_LEGO_EV3 is not set
+# CONFIG_BATTERY_MAX17040 is not set
+# CONFIG_BATTERY_MAX17042 is not set
+# CONFIG_BATTERY_MAX1721X is not set
+# CONFIG_BATTERY_RT5033 is not set
+# CONFIG_BATTERY_SBS is not set
+# CONFIG_BAYCOM_EPP is not set
+# CONFIG_BAYCOM_PAR is not set
+# CONFIG_BAYCOM_SER_FDX is not set
+# CONFIG_BAYCOM_SER_HDX is not set
+# CONFIG_BCACHE is not set
+# CONFIG_BCM47XX is not set
+# CONFIG_BCM54140_PHY is not set
+# CONFIG_BCM63XX is not set
+# CONFIG_BCM63XX_PHY is not set
+# CONFIG_BCM7038_WDT is not set
+# CONFIG_BCM7XXX_PHY is not set
+# CONFIG_BCM84881_PHY is not set
+# CONFIG_BCM87XX_PHY is not set
+# CONFIG_BCMA is not set
+# CONFIG_BCMA_DRIVER_GPIO is not set
+CONFIG_BCMA_POSSIBLE=y
+# CONFIG_BCMGENET is not set
+# CONFIG_BCM_IPROC_ADC is not set
+# CONFIG_BCM_KONA_USB2_PHY is not set
+# CONFIG_BCM_SBA_RAID is not set
+# CONFIG_BCM_VK is not set
+# CONFIG_BDI_SWITCH is not set
+# CONFIG_BE2ISCSI is not set
+# CONFIG_BE2NET is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_BGMAC is not set
+# CONFIG_BH1750 is not set
+# CONFIG_BH1780 is not set
+# CONFIG_BIG_KEYS is not set
+# CONFIG_BIG_LITTLE is not set
+CONFIG_BINARY_PRINTF=y
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_ELF_FDPIC is not set
+# CONFIG_BINFMT_FLAT is not set
+# CONFIG_BINFMT_MISC is not set
+CONFIG_BINFMT_SCRIPT=y
+CONFIG_BITREVERSE=y
+# CONFIG_BLK_CGROUP_IOCOST is not set
+# CONFIG_BLK_CGROUP_IOLATENCY is not set
+# CONFIG_BLK_CGROUP_IOPRIO is not set
+# CONFIG_BLK_CMDLINE_PARSER is not set
+# CONFIG_BLK_DEBUG_FS is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_BLK_DEV_4DRIVES is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI14XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_BLK_DEV_ATIIXP is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_CS5520 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_CS5535 is not set
+# CONFIG_BLK_DEV_CS5536 is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_DELKIN is not set
+# CONFIG_BLK_DEV_DM is not set
+# CONFIG_BLK_DEV_DRBD is not set
+# CONFIG_BLK_DEV_DTC2278 is not set
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_GENERIC is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_HT6560B is not set
+# CONFIG_BLK_DEV_IDEACPI is not set
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDECS is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_IDEPNP is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_INTEGRITY is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_BLK_DEV_IT8172 is not set
+# CONFIG_BLK_DEV_IT8213 is not set
+# CONFIG_BLK_DEV_IT821X is not set
+# CONFIG_BLK_DEV_JMICRON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_NULL_BLK is not set
+# CONFIG_BLK_DEV_NVME is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_BLK_DEV_PMEM is not set
+# CONFIG_BLK_DEV_QD65XX is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_RBD is not set
+# CONFIG_BLK_DEV_RSXX is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_SD is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SKD is not set
+# CONFIG_BLK_DEV_SL82C105 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_TC86C001 is not set
+# CONFIG_BLK_DEV_THROTTLING is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_UMC8672 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_ZONED is not set
+# CONFIG_BLK_INLINE_ENCRYPTION is not set
+# CONFIG_BLK_SED_OPAL is not set
+# CONFIG_BLK_WBT is not set
+CONFIG_BLOCK=y
+# CONFIG_BMA180 is not set
+# CONFIG_BMA220 is not set
+# CONFIG_BMA400 is not set
+# CONFIG_BMC150_ACCEL is not set
+# CONFIG_BMC150_MAGN is not set
+# CONFIG_BMC150_MAGN_I2C is not set
+# CONFIG_BMC150_MAGN_SPI is not set
+# CONFIG_BME680 is not set
+# CONFIG_BMG160 is not set
+# CONFIG_BMI088_ACCEL is not set
+# CONFIG_BMI160_I2C is not set
+# CONFIG_BMI160_SPI is not set
+# CONFIG_BMIPS_GENERIC is not set
+# CONFIG_BMP280 is not set
+# CONFIG_BNA is not set
+# CONFIG_BNX2 is not set
+# CONFIG_BNX2X is not set
+# CONFIG_BNX2X_SRIOV is not set
+# CONFIG_BNXT is not set
+# CONFIG_BONDING is not set
+# CONFIG_BOOKE_WDT is not set
+CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT=3
+# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+# CONFIG_BOOTTIME_TRACING is not set
+# CONFIG_BOOT_CONFIG is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+CONFIG_BOOT_RAW=y
+# CONFIG_BOUNCE is not set
+CONFIG_BPF=y
+# CONFIG_BPFILTER is not set
+CONFIG_BPF_JIT=y
+# CONFIG_BPF_JIT_ALWAYS_ON is not set
+CONFIG_BPF_JIT_DEFAULT_ON=y
+# CONFIG_BPF_PRELOAD is not set
+# CONFIG_BPF_STREAM_PARSER is not set
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
+# CONFIG_BPQETHER is not set
+CONFIG_BQL=y
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_BRCMFMAC is not set
+# CONFIG_BRCMSMAC is not set
+# CONFIG_BRCMSTB_GISB_ARB is not set
+CONFIG_BRIDGE=y
+# CONFIG_BRIDGE_CFM is not set
+# CONFIG_BRIDGE_EBT_802_3 is not set
+# CONFIG_BRIDGE_EBT_AMONG is not set
+# CONFIG_BRIDGE_EBT_ARP is not set
+# CONFIG_BRIDGE_EBT_ARPREPLY is not set
+# CONFIG_BRIDGE_EBT_BROUTE is not set
+# CONFIG_BRIDGE_EBT_DNAT is not set
+# CONFIG_BRIDGE_EBT_IP is not set
+# CONFIG_BRIDGE_EBT_IP6 is not set
+# CONFIG_BRIDGE_EBT_LIMIT is not set
+# CONFIG_BRIDGE_EBT_LOG is not set
+# CONFIG_BRIDGE_EBT_MARK is not set
+# CONFIG_BRIDGE_EBT_MARK_T is not set
+# CONFIG_BRIDGE_EBT_NFLOG is not set
+# CONFIG_BRIDGE_EBT_PKTTYPE is not set
+# CONFIG_BRIDGE_EBT_REDIRECT is not set
+# CONFIG_BRIDGE_EBT_SNAT is not set
+# CONFIG_BRIDGE_EBT_STP is not set
+# CONFIG_BRIDGE_EBT_T_FILTER is not set
+# CONFIG_BRIDGE_EBT_T_NAT is not set
+# CONFIG_BRIDGE_EBT_VLAN is not set
+CONFIG_BRIDGE_IGMP_SNOOPING=y
+# CONFIG_BRIDGE_MRP is not set
+# CONFIG_BRIDGE_NETFILTER is not set
+# CONFIG_BRIDGE_NF_EBTABLES is not set
+CONFIG_BRIDGE_VLAN_FILTERING=y
+# CONFIG_BROADCOM_PHY is not set
+CONFIG_BROKEN_ON_SMP=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_BT is not set
+# CONFIG_BTRFS_ASSERT is not set
+# CONFIG_BTRFS_DEBUG is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_BTRFS_FS_POSIX_ACL is not set
+# CONFIG_BTRFS_FS_REF_VERIFY is not set
+# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set
+# CONFIG_BT_AOSPEXT is not set
+# CONFIG_BT_ATH3K is not set
+# CONFIG_BT_BNEP is not set
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_CMTP is not set
+# CONFIG_BT_FEATURE_DEBUG is not set
+# CONFIG_BT_HCIBCM203X is not set
+# CONFIG_BT_HCIBFUSB is not set
+# CONFIG_BT_HCIBLUECARD is not set
+# CONFIG_BT_HCIBPA10X is not set
+# CONFIG_BT_HCIBT3C is not set
+# CONFIG_BT_HCIBTSDIO is not set
+# CONFIG_BT_HCIBTUSB is not set
+# CONFIG_BT_HCIBTUSB_AUTOSUSPEND is not set
+# CONFIG_BT_HCIBTUSB_MTK is not set
+# CONFIG_BT_HCIBTUSB_RTL is not set
+# CONFIG_BT_HCIDTL1 is not set
+# CONFIG_BT_HCIUART is not set
+# CONFIG_BT_HCIUART_3WIRE is not set
+# CONFIG_BT_HCIUART_AG6XX is not set
+# CONFIG_BT_HCIUART_ATH3K is not set
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_H4=y
+# CONFIG_BT_HCIUART_LL is not set
+# CONFIG_BT_HCIUART_MRVL is not set
+# CONFIG_BT_HCIUART_QCA is not set
+# CONFIG_BT_HCIUART_RTL is not set
+# CONFIG_BT_HCIVHCI is not set
+# CONFIG_BT_HIDP is not set
+# CONFIG_BT_HS is not set
+# CONFIG_BT_LE is not set
+# CONFIG_BT_LEDS is not set
+# CONFIG_BT_MRVL is not set
+# CONFIG_BT_MSFTEXT is not set
+# CONFIG_BT_MTKSDIO is not set
+# CONFIG_BT_MTKUART is not set
+# CONFIG_BT_RFCOMM is not set
+CONFIG_BT_RFCOMM_TTY=y
+# CONFIG_BT_SELFTEST is not set
+# CONFIG_BT_VIRTIO is not set
+CONFIG_BUG=y
+# CONFIG_BUG_ON_DATA_CORRUPTION is not set
+CONFIG_BUILDTIME_EXTABLE_SORT=y
+CONFIG_BUILDTIME_TABLE_SORT=y
+# CONFIG_BUILD_BIN2C is not set
+CONFIG_BUILD_SALT=""
+# CONFIG_C2PORT is not set
+CONFIG_CACHE_L2X0_PMU=y
+# CONFIG_CADENCE_WATCHDOG is not set
+# CONFIG_CAIF is not set
+# CONFIG_CAN is not set
+# CONFIG_CAN_BCM is not set
+# CONFIG_CAN_DEBUG_DEVICES is not set
+# CONFIG_CAN_DEV is not set
+# CONFIG_CAN_ETAS_ES58X is not set
+# CONFIG_CAN_GS_USB is not set
+# CONFIG_CAN_GW is not set
+# CONFIG_CAN_HI311X is not set
+# CONFIG_CAN_IFI_CANFD is not set
+# CONFIG_CAN_ISOTP is not set
+# CONFIG_CAN_J1939 is not set
+# CONFIG_CAN_KVASER_PCIEFD is not set
+# CONFIG_CAN_MCBA_USB is not set
+# CONFIG_CAN_MCP251XFD is not set
+# CONFIG_CAN_M_CAN is not set
+# CONFIG_CAN_PEAK_PCIEFD is not set
+# CONFIG_CAN_RAW is not set
+# CONFIG_CAN_RCAR is not set
+# CONFIG_CAN_RCAR_CANFD is not set
+# CONFIG_CAN_SLCAN is not set
+# CONFIG_CAN_SUN4I is not set
+# CONFIG_CAN_UCAN is not set
+# CONFIG_CAN_VCAN is not set
+# CONFIG_CAN_VXCAN is not set
+# CONFIG_CAPI_AVM is not set
+# CONFIG_CAPI_EICON is not set
+# CONFIG_CAPI_TRACE is not set
+CONFIG_CARDBUS=y
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_CARL9170 is not set
+# CONFIG_CASSINI is not set
+# CONFIG_CAVIUM_CPT is not set
+# CONFIG_CAVIUM_ERRATUM_22375 is not set
+# CONFIG_CAVIUM_ERRATUM_23144 is not set
+# CONFIG_CAVIUM_ERRATUM_23154 is not set
+# CONFIG_CAVIUM_ERRATUM_27456 is not set
+# CONFIG_CAVIUM_ERRATUM_30115 is not set
+# CONFIG_CAVIUM_OCTEON_SOC is not set
+# CONFIG_CAVIUM_PTP is not set
+# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set
+# CONFIG_CB710_CORE is not set
+# CONFIG_CC10001_ADC is not set
+# CONFIG_CCS811 is not set
+CONFIG_CC_CAN_LINK=y
+CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_CEPH_FS is not set
+# CONFIG_CEPH_LIB is not set
+# CONFIG_CFG80211 is not set
+# CONFIG_CFG80211_CERTIFICATION_ONUS is not set
+# CONFIG_CGROUPS is not set
+# CONFIG_CGROUP_MISC is not set
+# CONFIG_CHARGER_ADP5061 is not set
+# CONFIG_CHARGER_BD99954 is not set
+# CONFIG_CHARGER_BQ2415X is not set
+# CONFIG_CHARGER_BQ24190 is not set
+# CONFIG_CHARGER_BQ24257 is not set
+# CONFIG_CHARGER_BQ24735 is not set
+# CONFIG_CHARGER_BQ2515X is not set
+# CONFIG_CHARGER_BQ256XX is not set
+# CONFIG_CHARGER_BQ25890 is not set
+# CONFIG_CHARGER_BQ25980 is not set
+# CONFIG_CHARGER_DETECTOR_MAX14656 is not set
+# CONFIG_CHARGER_GPIO is not set
+# CONFIG_CHARGER_ISP1704 is not set
+# CONFIG_CHARGER_LP8727 is not set
+# CONFIG_CHARGER_LT3651 is not set
+# CONFIG_CHARGER_LTC3651 is not set
+# CONFIG_CHARGER_LTC4162L is not set
+# CONFIG_CHARGER_MANAGER is not set
+# CONFIG_CHARGER_MAX8903 is not set
+# CONFIG_CHARGER_QCOM_SMBB is not set
+# CONFIG_CHARGER_RT9455 is not set
+# CONFIG_CHARGER_SBS is not set
+# CONFIG_CHARGER_SMB347 is not set
+# CONFIG_CHARGER_TWL4030 is not set
+# CONFIG_CHARGER_UCS1002 is not set
+# CONFIG_CHASH_SELFTEST is not set
+# CONFIG_CHASH_STATS is not set
+# CONFIG_CHECKPOINT_RESTORE is not set
+# CONFIG_CHELSIO_T1 is not set
+# CONFIG_CHELSIO_T3 is not set
+# CONFIG_CHELSIO_T4 is not set
+# CONFIG_CHELSIO_T4VF is not set
+# CONFIG_CHROME_PLATFORMS is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_CHR_DEV_SCH is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_CIFS is not set
+# CONFIG_CIFS_ACL is not set
+CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y
+# CONFIG_CIFS_DEBUG is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_FSCACHE is not set
+# CONFIG_CIFS_NFSD_EXPORT is not set
+CONFIG_CIFS_POSIX=y
+# CONFIG_CIFS_SMB2 is not set
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_STATS2 is not set
+# CONFIG_CIFS_SWN_UPCALL is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+CONFIG_CIFS_XATTR=y
+# CONFIG_CIO_DAC is not set
+# CONFIG_CLEANCACHE is not set
+# CONFIG_CLKSRC_PISTACHIO is not set
+# CONFIG_CLKSRC_VERSATILE is not set
+# CONFIG_CLK_GFM_LPASS_SM8250 is not set
+# CONFIG_CLK_HSDK is not set
+# CONFIG_CLK_QORIQ is not set
+# CONFIG_CLK_SP810 is not set
+# CONFIG_CLOCK_THERMAL is not set
+CONFIG_CLS_U32_MARK=y
+# CONFIG_CLS_U32_PERF is not set
+# CONFIG_CM32181 is not set
+# CONFIG_CM3232 is not set
+# CONFIG_CM3323 is not set
+# CONFIG_CM3605 is not set
+# CONFIG_CM36651 is not set
+# CONFIG_CMA is not set
+CONFIG_CMDLINE=""
+# CONFIG_CMDLINE_BOOL is not set
+# CONFIG_CMDLINE_EXTEND is not set
+# CONFIG_CMDLINE_FORCE is not set
+# CONFIG_CMDLINE_FROM_BOOTLOADER is not set
+# CONFIG_CMDLINE_PARTITION is not set
+# CONFIG_CNIC is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_CODE_PATCHING_SELFTEST is not set
+# CONFIG_COMEDI is not set
+# CONFIG_COMMON_CLK_AXI_CLKGEN is not set
+# CONFIG_COMMON_CLK_BOSTON is not set
+# CONFIG_COMMON_CLK_CDCE706 is not set
+# CONFIG_COMMON_CLK_CDCE925 is not set
+# CONFIG_COMMON_CLK_CS2000_CP is not set
+# CONFIG_COMMON_CLK_FIXED_MMIO is not set
+# CONFIG_COMMON_CLK_IPROC is not set
+# CONFIG_COMMON_CLK_MAX9485 is not set
+# CONFIG_COMMON_CLK_MT6765 is not set
+# CONFIG_COMMON_CLK_MT8167 is not set
+# CONFIG_COMMON_CLK_MT8167_AUDSYS is not set
+# CONFIG_COMMON_CLK_MT8167_IMGSYS is not set
+# CONFIG_COMMON_CLK_MT8167_MFGCFG is not set
+# CONFIG_COMMON_CLK_MT8167_MMSYS is not set
+# CONFIG_COMMON_CLK_MT8167_VDECSYS is not set
+# CONFIG_COMMON_CLK_MT8192 is not set
+# CONFIG_COMMON_CLK_NXP is not set
+# CONFIG_COMMON_CLK_PIC32 is not set
+# CONFIG_COMMON_CLK_PISTACHIO is not set
+# CONFIG_COMMON_CLK_PWM is not set
+# CONFIG_COMMON_CLK_PXA is not set
+# CONFIG_COMMON_CLK_QCOM is not set
+# CONFIG_COMMON_CLK_SI514 is not set
+# CONFIG_COMMON_CLK_SI5341 is not set
+# CONFIG_COMMON_CLK_SI5351 is not set
+# CONFIG_COMMON_CLK_SI544 is not set
+# CONFIG_COMMON_CLK_SI570 is not set
+# CONFIG_COMMON_CLK_VC5 is not set
+# CONFIG_COMMON_CLK_XGENE is not set
+# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set
+CONFIG_COMPACTION=y
+# CONFIG_COMPAL_LAPTOP is not set
+# CONFIG_COMPAT is not set
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_COMPILE_TEST is not set
+# CONFIG_CONFIGFS_FS is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
+CONFIG_CONSOLE_LOGLEVEL_QUIET=4
+CONFIG_CONSTRUCTORS=y
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_COPS is not set
+# CONFIG_CORDIC is not set
+# CONFIG_COREDUMP is not set
+# CONFIG_CORESIGHT is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_CORTINA_PHY is not set
+# CONFIG_COUNTER is not set
+# CONFIG_CPA_DEBUG is not set
+# CONFIG_CPU_BIG_ENDIAN is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_GOV_SCHEDUTIL is not set
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_THERMAL is not set
+# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_ICACHE_MISMATCH_WORKAROUND is not set
+# CONFIG_CPU_IDLE is not set
+# CONFIG_CPU_IDLE_GOV_LADDER is not set
+# CONFIG_CPU_IDLE_GOV_MENU is not set
+# CONFIG_CPU_IDLE_GOV_TEO is not set
+# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set
+# CONFIG_CPU_ISOLATION is not set
+# CONFIG_CPU_LITTLE_ENDIAN is not set
+# CONFIG_CPU_NO_EFFICIENT_FFS is not set
+CONFIG_CPU_SW_DOMAIN_PAN=y
+# CONFIG_CPU_THERMAL is not set
+# CONFIG_CRAMFS is not set
+CONFIG_CRAMFS_BLOCKDEV=y
+# CONFIG_CRAMFS_MTD is not set
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=y
+# CONFIG_CRC32_BIT is not set
+CONFIG_CRC32_SARWATE=y
+# CONFIG_CRC32_SELFTEST is not set
+# CONFIG_CRC32_SLICEBY4 is not set
+# CONFIG_CRC32_SLICEBY8 is not set
+# CONFIG_CRC4 is not set
+# CONFIG_CRC64 is not set
+# CONFIG_CRC7 is not set
+# CONFIG_CRC8 is not set
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC_ITU_T is not set
+# CONFIG_CRC_T10DIF is not set
+CONFIG_CROSS_COMPILE=""
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+CONFIG_CRYPTO=y
+# CONFIG_CRYPTO_842 is not set
+CONFIG_CRYPTO_ACOMP2=y
+# CONFIG_CRYPTO_ADIANTUM is not set
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+# CONFIG_CRYPTO_AEGIS128 is not set
+# CONFIG_CRYPTO_AEGIS128L is not set
+# CONFIG_CRYPTO_AEGIS128L_AESNI_SSE2 is not set
+# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set
+# CONFIG_CRYPTO_AEGIS256 is not set
+# CONFIG_CRYPTO_AEGIS256_AESNI_SSE2 is not set
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_586 is not set
+# CONFIG_CRYPTO_AES_ARM is not set
+# CONFIG_CRYPTO_AES_ARM64 is not set
+# CONFIG_CRYPTO_AES_ARM64_BS is not set
+# CONFIG_CRYPTO_AES_ARM64_CE is not set
+# CONFIG_CRYPTO_AES_ARM64_CE_BLK is not set
+# CONFIG_CRYPTO_AES_ARM64_CE_CCM is not set
+# CONFIG_CRYPTO_AES_ARM64_NEON_BLK is not set
+# CONFIG_CRYPTO_AES_ARM_BS is not set
+# CONFIG_CRYPTO_AES_ARM_CE is not set
+# CONFIG_CRYPTO_AES_NI_INTEL is not set
+# CONFIG_CRYPTO_AES_TI is not set
+CONFIG_CRYPTO_AKCIPHER=y
+CONFIG_CRYPTO_AKCIPHER2=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_BLAKE2B is not set
+# CONFIG_CRYPTO_BLAKE2B_NEON is not set
+# CONFIG_CRYPTO_BLAKE2S is not set
+# CONFIG_CRYPTO_BLAKE2S_ARM is not set
+# CONFIG_CRYPTO_BLAKE2S_X86 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_CBC is not set
+CONFIG_CRYPTO_CCM=y
+# CONFIG_CRYPTO_CFB is not set
+# CONFIG_CRYPTO_CHACHA20 is not set
+# CONFIG_CRYPTO_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_CHACHA20_NEON is not set
+# CONFIG_CRYPTO_CHACHA20_X86_64 is not set
+# CONFIG_CRYPTO_CHACHA_MIPS is not set
+# CONFIG_CRYPTO_CMAC is not set
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CRC32C_INTEL is not set
+# CONFIG_CRYPTO_CRC32_ARM_CE is not set
+# CONFIG_CRYPTO_CRCT10DIF is not set
+# CONFIG_CRYPTO_CRCT10DIF_ARM64_CE is not set
+# CONFIG_CRYPTO_CRCT10DIF_ARM_CE is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_CTR=y
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_CURVE25519 is not set
+# CONFIG_CRYPTO_CURVE25519_NEON is not set
+# CONFIG_CRYPTO_CURVE25519_X86 is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set
+# CONFIG_CRYPTO_DEV_ATMEL_AES is not set
+# CONFIG_CRYPTO_DEV_ATMEL_AUTHENC is not set
+# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set
+# CONFIG_CRYPTO_DEV_ATMEL_SHA is not set
+# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set
+# CONFIG_CRYPTO_DEV_ATMEL_TDES is not set
+# CONFIG_CRYPTO_DEV_CAVIUM_ZIP is not set
+# CONFIG_CRYPTO_DEV_CCP is not set
+# CONFIG_CRYPTO_DEV_CCP_DEBUGFS is not set
+# CONFIG_CRYPTO_DEV_CCREE is not set
+# CONFIG_CRYPTO_DEV_FSL_CAAM is not set
+# CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC is not set
+# CONFIG_CRYPTO_DEV_HIFN_795X is not set
+# CONFIG_CRYPTO_DEV_HISI_SEC is not set
+# CONFIG_CRYPTO_DEV_HISI_ZIP is not set
+# CONFIG_CRYPTO_DEV_IMGTEC_HASH is not set
+# CONFIG_CRYPTO_DEV_MARVELL_CESA is not set
+# CONFIG_CRYPTO_DEV_MEDIATEK is not set
+# CONFIG_CRYPTO_DEV_MV_CESA is not set
+# CONFIG_CRYPTO_DEV_MXC_SCC is not set
+# CONFIG_CRYPTO_DEV_MXS_DCP is not set
+# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set
+# CONFIG_CRYPTO_DEV_OCTEONTX_CPT is not set
+# CONFIG_CRYPTO_DEV_QAT_4XXX is not set
+# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set
+# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set
+# CONFIG_CRYPTO_DEV_QAT_C62X is not set
+# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set
+# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set
+# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set
+# CONFIG_CRYPTO_DEV_QCE is not set
+# CONFIG_CRYPTO_DEV_S5P is not set
+# CONFIG_CRYPTO_DEV_SAFEXCEL is not set
+# CONFIG_CRYPTO_DEV_SAHARA is not set
+# CONFIG_CRYPTO_DEV_SP_PSP is not set
+# CONFIG_CRYPTO_DEV_TALITOS is not set
+# CONFIG_CRYPTO_DEV_VIRTIO is not set
+# CONFIG_CRYPTO_DH is not set
+# CONFIG_CRYPTO_DRBG_CTR is not set
+# CONFIG_CRYPTO_DRBG_HASH is not set
+# CONFIG_CRYPTO_DRBG_MENU is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_ECDH is not set
+# CONFIG_CRYPTO_ECDSA is not set
+# CONFIG_CRYPTO_ECHAINIV is not set
+# CONFIG_CRYPTO_ECRDSA is not set
+# CONFIG_CRYPTO_ESSIV is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_GCM=y
+CONFIG_CRYPTO_GF128MUL=y
+CONFIG_CRYPTO_GHASH=y
+# CONFIG_CRYPTO_GHASH_ARM64_CE is not set
+# CONFIG_CRYPTO_GHASH_ARM_CE is not set
+# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_CRYPTO_JITTERENTROPY is not set
+# CONFIG_CRYPTO_KEYWRAP is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+CONFIG_CRYPTO_KPP=y
+CONFIG_CRYPTO_KPP2=y
+CONFIG_CRYPTO_LIB_AES=y
+CONFIG_CRYPTO_LIB_ARC4=y
+# CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC is not set
+# CONFIG_CRYPTO_LIB_CHACHA is not set
+# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_LIB_CURVE25519 is not set
+# CONFIG_CRYPTO_LIB_POLY1305 is not set
+CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_LZ4 is not set
+# CONFIG_CRYPTO_LZ4HC is not set
+# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_MCRYPTD is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_MORUS1280 is not set
+# CONFIG_CRYPTO_MORUS1280_AVX2 is not set
+# CONFIG_CRYPTO_MORUS1280_SSE2 is not set
+# CONFIG_CRYPTO_MORUS640 is not set
+# CONFIG_CRYPTO_MORUS640_SSE2 is not set
+# CONFIG_CRYPTO_NHPOLY1305_NEON is not set
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_NULL2=y
+# CONFIG_CRYPTO_OFB is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_PCOMP is not set
+# CONFIG_CRYPTO_PCOMP2 is not set
+CONFIG_CRYPTO_PCRYPT=y
+# CONFIG_CRYPTO_POLY1305 is not set
+# CONFIG_CRYPTO_POLY1305_ARM is not set
+# CONFIG_CRYPTO_POLY1305_MIPS is not set
+# CONFIG_CRYPTO_POLY1305_NEON is not set
+# CONFIG_CRYPTO_POLY1305_X86_64 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RNG is not set
+# CONFIG_CRYPTO_RSA is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SEQIV is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA1_ARM is not set
+# CONFIG_CRYPTO_SHA1_ARM64_CE is not set
+# CONFIG_CRYPTO_SHA1_ARM_CE is not set
+# CONFIG_CRYPTO_SHA1_ARM_NEON is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA256_ARM is not set
+# CONFIG_CRYPTO_SHA256_ARM64 is not set
+# CONFIG_CRYPTO_SHA2_ARM64_CE is not set
+# CONFIG_CRYPTO_SHA2_ARM_CE is not set
+# CONFIG_CRYPTO_SHA3 is not set
+# CONFIG_CRYPTO_SHA3_ARM64 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_SHA512_ARM is not set
+# CONFIG_CRYPTO_SHA512_ARM64 is not set
+# CONFIG_CRYPTO_SHA512_ARM64_CE is not set
+# CONFIG_CRYPTO_SIMD is not set
+CONFIG_CRYPTO_SKCIPHER=y
+CONFIG_CRYPTO_SKCIPHER2=y
+# CONFIG_CRYPTO_SM2 is not set
+# CONFIG_CRYPTO_SM3 is not set
+# CONFIG_CRYPTO_SM3_ARM64_CE is not set
+# CONFIG_CRYPTO_SM4 is not set
+# CONFIG_CRYPTO_SM4_ARM64_CE is not set
+# CONFIG_CRYPTO_SPECK is not set
+# CONFIG_CRYPTO_STATS is not set
+# CONFIG_CRYPTO_STREEBOG is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_TWOFISH_586 is not set
+# CONFIG_CRYPTO_TWOFISH_COMMON is not set
+# CONFIG_CRYPTO_USER is not set
+# CONFIG_CRYPTO_USER_API_AEAD is not set
+# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_RNG is not set
+# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+# CONFIG_CRYPTO_VMAC is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_XXHASH is not set
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_ZSTD is not set
+# CONFIG_CS5535_MFGPT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_CS89x0_PLATFORM is not set
+# CONFIG_CSD_LOCK_WAIT_DEBUG is not set
+# CONFIG_CUSE is not set
+# CONFIG_CW1200 is not set
+# CONFIG_CXD2880_SPI_DRV is not set
+# CONFIG_CXL_AFU_DRIVER_OPS is not set
+# CONFIG_CXL_BASE is not set
+# CONFIG_CXL_BUS is not set
+# CONFIG_CXL_EEH is not set
+# CONFIG_CXL_KERNEL_API is not set
+# CONFIG_CXL_LIB is not set
+# CONFIG_CYPRESS_FIRMWARE is not set
+# CONFIG_DA280 is not set
+# CONFIG_DA311 is not set
+# CONFIG_DAMON is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_DAX is not set
+# CONFIG_DCB is not set
+# CONFIG_DDR is not set
+# CONFIG_DEBUG_ALIGN_RODATA is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_EFI is not set
+# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set
+# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_FS_ALLOW_ALL=y
+# CONFIG_DEBUG_FS_ALLOW_NONE is not set
+# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_DEBUG_HIGHMEM is not set
+# CONFIG_DEBUG_ICEDCC is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_INFO_BTF is not set
+# CONFIG_DEBUG_INFO_COMPRESSED is not set
+# CONFIG_DEBUG_INFO_DWARF4 is not set
+# CONFIG_DEBUG_INFO_DWARF5 is not set
+CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
+# CONFIG_DEBUG_INFO_REDUCED is not set
+# CONFIG_DEBUG_INFO_SPLIT is not set
+# CONFIG_DEBUG_IRQFLAGS is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_KMAP_LOCAL is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_KOBJECT_RELEASE is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_LL is not set
+# CONFIG_DEBUG_LL_UART_8250 is not set
+# CONFIG_DEBUG_LL_UART_PL01X is not set
+# CONFIG_DEBUG_LOCKDEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_MISC is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_NX_TEST is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_DEBUG_PAGE_REF is not set
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_DEBUG_PINCTRL is not set
+# CONFIG_DEBUG_PI_LIST is not set
+# CONFIG_DEBUG_PLIST is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RODATA_TEST is not set
+# CONFIG_DEBUG_RSEQ is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_DEBUG_RWSEMS is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+# CONFIG_DEBUG_SEMIHOSTING is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set
+# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set
+# CONFIG_DEBUG_TIMEKEEPING is not set
+# CONFIG_DEBUG_UART_8250_PALMCHIP is not set
+# CONFIG_DEBUG_UART_8250_WORD is not set
+# CONFIG_DEBUG_UART_BCM63XX is not set
+# CONFIG_DEBUG_UART_FLOW_CONTROL is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_VIRTUAL is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_VM_PGFLAGS is not set
+# CONFIG_DEBUG_VM_PGTABLE is not set
+# CONFIG_DEBUG_VM_RB is not set
+# CONFIG_DEBUG_VM_VMACACHE is not set
+# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set
+# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set
+# CONFIG_DEBUG_WX is not set
+# CONFIG_DEBUG_ZBOOT is not set
+# CONFIG_DECNET is not set
+# CONFIG_DEFAULT_CODEL is not set
+CONFIG_DEFAULT_CUBIC=y
+CONFIG_DEFAULT_DEADLINE=y
+# CONFIG_DEFAULT_FQ is not set
+CONFIG_DEFAULT_FQ_CODEL=y
+# CONFIG_DEFAULT_FQ_PIE is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+CONFIG_DEFAULT_INIT=""
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_DEFAULT_NET_SCH="fq_codel"
+# CONFIG_DEFAULT_NOOP is not set
+# CONFIG_DEFAULT_PFIFO_FAST is not set
+# CONFIG_DEFAULT_RENO is not set
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_DEFAULT_SECURITY_DAC=y
+# CONFIG_DEFAULT_SECURITY_SELINUX is not set
+# CONFIG_DEFAULT_SFQ is not set
+CONFIG_DEFAULT_TCP_CONG="cubic"
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
+# CONFIG_DELL_LAPTOP is not set
+# CONFIG_DELL_RBTN is not set
+# CONFIG_DELL_SMBIOS is not set
+# CONFIG_DELL_SMO8800 is not set
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+# CONFIG_DETECT_HUNG_TASK is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_DEVMEM is not set
+CONFIG_DEVPORT=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_DEVTMPFS is not set
+# CONFIG_DEVTMPFS_MOUNT is not set
+# CONFIG_DEV_DAX is not set
+# CONFIG_DGAP is not set
+# CONFIG_DGNC is not set
+# CONFIG_DHT11 is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_DISPLAY_CONNECTOR_ANALOG_TV is not set
+# CONFIG_DISPLAY_CONNECTOR_DVI is not set
+# CONFIG_DISPLAY_CONNECTOR_HDMI is not set
+# CONFIG_DISPLAY_ENCODER_TFP410 is not set
+# CONFIG_DISPLAY_ENCODER_TPD12S015 is not set
+# CONFIG_DISPLAY_PANEL_DPI is not set
+# CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02 is not set
+# CONFIG_DISPLAY_PANEL_TPO_TD028TTEC1 is not set
+# CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_DL2K is not set
+# CONFIG_DLHL60D is not set
+# CONFIG_DLM is not set
+# CONFIG_DM9000 is not set
+# CONFIG_DMABUF_DEBUG is not set
+# CONFIG_DMABUF_HEAPS is not set
+# CONFIG_DMABUF_MOVE_NOTIFY is not set
+# CONFIG_DMABUF_SELFTESTS is not set
+# CONFIG_DMABUF_SYSFS_STATS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_DMADEVICES_DEBUG is not set
+# CONFIG_DMARD06 is not set
+# CONFIG_DMARD09 is not set
+# CONFIG_DMARD10 is not set
+# CONFIG_DMASCC is not set
+# CONFIG_DMATEST is not set
+# CONFIG_DMA_API_DEBUG is not set
+CONFIG_DMA_COHERENT_POOL=y
+CONFIG_DMA_DECLARE_COHERENT=y
+# CONFIG_DMA_ENGINE is not set
+# CONFIG_DMA_FENCE_TRACE is not set
+# CONFIG_DMA_JZ4780 is not set
+# CONFIG_DMA_MAP_BENCHMARK is not set
+CONFIG_DMA_NONCOHERENT_MMAP=y
+# CONFIG_DMA_NOOP_OPS is not set
+# CONFIG_DMA_PERNUMA_CMA is not set
+# CONFIG_DMA_RESTRICTED_POOL is not set
+# CONFIG_DMA_SHARED_BUFFER is not set
+# CONFIG_DMA_VIRT_OPS is not set
+# CONFIG_DM_CACHE is not set
+# CONFIG_DM_CLONE is not set
+# CONFIG_DM_DEBUG is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_DUST is not set
+# CONFIG_DM_EBS is not set
+# CONFIG_DM_ERA is not set
+# CONFIG_DM_FLAKEY is not set
+# CONFIG_DM_INTEGRITY is not set
+# CONFIG_DM_LOG_USERSPACE is not set
+# CONFIG_DM_LOG_WRITES is not set
+# CONFIG_DM_MQ_DEFAULT is not set
+# CONFIG_DM_MULTIPATH is not set
+# CONFIG_DM_RAID is not set
+# CONFIG_DM_SWITCH is not set
+# CONFIG_DM_THIN_PROVISIONING is not set
+# CONFIG_DM_UEVENT is not set
+# CONFIG_DM_UNSTRIPED is not set
+# CONFIG_DM_VERITY is not set
+# CONFIG_DM_WRITECACHE is not set
+# CONFIG_DM_ZERO is not set
+# CONFIG_DNET is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_DNS_RESOLVER is not set
+CONFIG_DOUBLEFAULT=y
+# CONFIG_DP83640_PHY is not set
+# CONFIG_DP83822_PHY is not set
+# CONFIG_DP83848_PHY is not set
+# CONFIG_DP83867_PHY is not set
+# CONFIG_DP83869_PHY is not set
+# CONFIG_DP83TC811_PHY is not set
+# CONFIG_DPOT_DAC is not set
+# CONFIG_DPS310 is not set
+CONFIG_DQL=y
+# CONFIG_DRAGONRISE_FF is not set
+# CONFIG_DRM is not set
+# CONFIG_DRM_AMDGPU is not set
+# CONFIG_DRM_AMDGPU_CIK is not set
+# CONFIG_DRM_AMDGPU_GART_DEBUGFS is not set
+# CONFIG_DRM_AMDGPU_SI is not set
+# CONFIG_DRM_AMDGPU_USERPTR is not set
+# CONFIG_DRM_AMD_ACP is not set
+# CONFIG_DRM_AMD_DC_DCN2_0 is not set
+# CONFIG_DRM_AMD_DC_DCN3_0 is not set
+# CONFIG_DRM_AMD_DC_HDCP is not set
+# CONFIG_DRM_AMD_DC_SI is not set
+# CONFIG_DRM_AMD_SECURE_DISPLAY is not set
+# CONFIG_DRM_ANALOGIX_ANX6345 is not set
+# CONFIG_DRM_ANALOGIX_ANX7625 is not set
+# CONFIG_DRM_ANALOGIX_ANX78XX is not set
+# CONFIG_DRM_ARCPGU is not set
+# CONFIG_DRM_ARMADA is not set
+# CONFIG_DRM_AST is not set
+# CONFIG_DRM_ATMEL_HLCDC is not set
+# CONFIG_DRM_BOCHS is not set
+# CONFIG_DRM_CDNS_DSI is not set
+# CONFIG_DRM_CDNS_MHDP8546 is not set
+# CONFIG_DRM_CHIPONE_ICN6211 is not set
+# CONFIG_DRM_CHRONTEL_CH7033 is not set
+# CONFIG_DRM_CIRRUS_QEMU is not set
+# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set
+# CONFIG_DRM_DEBUG_MM is not set
+# CONFIG_DRM_DEBUG_SELFTEST is not set
+# CONFIG_DRM_DISPLAY_CONNECTOR is not set
+# CONFIG_DRM_DP_AUX_CHARDEV is not set
+# CONFIG_DRM_DP_CEC is not set
+# CONFIG_DRM_DUMB_VGA_DAC is not set
+# CONFIG_DRM_DW_HDMI_CEC is not set
+# CONFIG_DRM_ETNAVIV is not set
+# CONFIG_DRM_EXYNOS is not set
+# CONFIG_DRM_FBDEV_EMULATION is not set
+# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set
+# CONFIG_DRM_FSL_DCU is not set
+# CONFIG_DRM_GM12U320 is not set
+# CONFIG_DRM_GMA500 is not set
+# CONFIG_DRM_GUD is not set
+# CONFIG_DRM_HDLCD is not set
+# CONFIG_DRM_HISI_HIBMC is not set
+# CONFIG_DRM_HISI_KIRIN is not set
+# CONFIG_DRM_I2C_ADV7511 is not set
+# CONFIG_DRM_I2C_CH7006 is not set
+# CONFIG_DRM_I2C_NXP_TDA9950 is not set
+# CONFIG_DRM_I2C_NXP_TDA998X is not set
+# CONFIG_DRM_I2C_SIL164 is not set
+# CONFIG_DRM_I915 is not set
+# CONFIG_DRM_ITE_IT66121 is not set
+# CONFIG_DRM_KOMEDA is not set
+# CONFIG_DRM_LEGACY is not set
+# CONFIG_DRM_LIB_RANDOM is not set
+# CONFIG_DRM_LIMA is not set
+# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set
+# CONFIG_DRM_LONTIUM_LT8912B is not set
+# CONFIG_DRM_LONTIUM_LT9611 is not set
+# CONFIG_DRM_LONTIUM_LT9611UXC is not set
+# CONFIG_DRM_LVDS_CODEC is not set
+# CONFIG_DRM_LVDS_ENCODER is not set
+# CONFIG_DRM_MALI_DISPLAY is not set
+# CONFIG_DRM_MCDE is not set
+# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set
+# CONFIG_DRM_MGAG200 is not set
+# CONFIG_DRM_MXSFB is not set
+# CONFIG_DRM_NOUVEAU is not set
+# CONFIG_DRM_NWL_MIPI_DSI is not set
+# CONFIG_DRM_NXP_PTN3460 is not set
+# CONFIG_DRM_OMAP is not set
+# CONFIG_DRM_PANEL_ABT_Y030XX067A is not set
+# CONFIG_DRM_PANEL_ARM_VERSATILE is not set
+# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set
+# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set
+# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set
+# CONFIG_DRM_PANEL_DSI_CM is not set
+# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set
+# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set
+# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set
+# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9341 is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9806E is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set
+# CONFIG_DRM_PANEL_INNOLUX_EJ030NA is not set
+# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set
+# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set
+# CONFIG_DRM_PANEL_KHADAS_TS050 is not set
+# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set
+# CONFIG_DRM_PANEL_LG_LB035Q02 is not set
+# CONFIG_DRM_PANEL_LG_LG4573 is not set
+# CONFIG_DRM_PANEL_LVDS is not set
+# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set
+# CONFIG_DRM_PANEL_MIPI_DBI is not set
+# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT36672A is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set
+# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set
+# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set
+# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set
+# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set
+# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set
+# CONFIG_DRM_PANEL_ROCKTECH_JH057N00900 is not set
+# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_DB7430 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_SOFEF00 is not set
+# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set
+# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set
+# CONFIG_DRM_PANEL_SIMPLE is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set
+# CONFIG_DRM_PANEL_SONY_ACX424AKP is not set
+# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set
+# CONFIG_DRM_PANEL_TDO_TL070WSH30 is not set
+# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set
+# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_DRM_PANEL_TPO_TPG110 is not set
+# CONFIG_DRM_PANEL_TPO_Y17P is not set
+# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set
+# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set
+# CONFIG_DRM_PANEL_WIDECHIPS_WS2401 is not set
+# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set
+# CONFIG_DRM_PANFROST is not set
+# CONFIG_DRM_PARADE_PS8622 is not set
+# CONFIG_DRM_PARADE_PS8640 is not set
+# CONFIG_DRM_PL111 is not set
+# CONFIG_DRM_QXL is not set
+# CONFIG_DRM_RADEON is not set
+# CONFIG_DRM_RADEON_USERPTR is not set
+# CONFIG_DRM_RCAR_DW_HDMI is not set
+# CONFIG_DRM_RCAR_LVDS is not set
+# CONFIG_DRM_ROCKCHIP is not set
+# CONFIG_DRM_SII902X is not set
+# CONFIG_DRM_SII9234 is not set
+# CONFIG_DRM_SIL_SII8620 is not set
+# CONFIG_DRM_SIMPLEDRM is not set
+# CONFIG_DRM_SIMPLE_BRIDGE is not set
+# CONFIG_DRM_STI is not set
+# CONFIG_DRM_STM is not set
+# CONFIG_DRM_SUN4I is not set
+# CONFIG_DRM_THINE_THC63LVD1024 is not set
+# CONFIG_DRM_TIDSS is not set
+# CONFIG_DRM_TILCDC is not set
+# CONFIG_DRM_TINYDRM is not set
+# CONFIG_DRM_TI_SN65DSI83 is not set
+# CONFIG_DRM_TI_SN65DSI86 is not set
+# CONFIG_DRM_TI_TFP410 is not set
+# CONFIG_DRM_TI_TPD12S015 is not set
+# CONFIG_DRM_TOSHIBA_TC358762 is not set
+# CONFIG_DRM_TOSHIBA_TC358764 is not set
+# CONFIG_DRM_TOSHIBA_TC358767 is not set
+# CONFIG_DRM_TOSHIBA_TC358768 is not set
+# CONFIG_DRM_TOSHIBA_TC358775 is not set
+# CONFIG_DRM_TVE200 is not set
+# CONFIG_DRM_UDL is not set
+# CONFIG_DRM_VBOXVIDEO is not set
+# CONFIG_DRM_VC4_HDMI_CEC is not set
+# CONFIG_DRM_VGEM is not set
+# CONFIG_DRM_VIRTIO_GPU is not set
+# CONFIG_DRM_VKMS is not set
+# CONFIG_DRM_VMWGFX is not set
+# CONFIG_DRM_XEN is not set
+# CONFIG_DRM_XEN_FRONTEND is not set
+# CONFIG_DS1682 is not set
+# CONFIG_DS1803 is not set
+# CONFIG_DS4424 is not set
+# CONFIG_DST_CACHE is not set
+# CONFIG_DTLK is not set
+# CONFIG_DUMMY is not set
+CONFIG_DUMMY_CONSOLE_COLUMNS=80
+CONFIG_DUMMY_CONSOLE_ROWS=25
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_DVB_A8293 is not set
+# CONFIG_DVB_AF9013 is not set
+# CONFIG_DVB_AF9033 is not set
+# CONFIG_DVB_AS102 is not set
+# CONFIG_DVB_ASCOT2E is not set
+# CONFIG_DVB_ATBM8830 is not set
+# CONFIG_DVB_AU8522_DTV is not set
+# CONFIG_DVB_AU8522_V4L is not set
+# CONFIG_DVB_B2C2_FLEXCOP_USB is not set
+# CONFIG_DVB_BCM3510 is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_DVB_CX22700 is not set
+# CONFIG_DVB_CX22702 is not set
+# CONFIG_DVB_CX24110 is not set
+# CONFIG_DVB_CX24116 is not set
+# CONFIG_DVB_CX24117 is not set
+# CONFIG_DVB_CX24120 is not set
+# CONFIG_DVB_CX24123 is not set
+# CONFIG_DVB_CXD2099 is not set
+# CONFIG_DVB_CXD2820R is not set
+# CONFIG_DVB_CXD2841ER is not set
+# CONFIG_DVB_CXD2880 is not set
+# CONFIG_DVB_DEMUX_SECTION_LOSS_LOG is not set
+# CONFIG_DVB_DIB3000MB is not set
+# CONFIG_DVB_DIB3000MC is not set
+# CONFIG_DVB_DIB7000M is not set
+# CONFIG_DVB_DIB7000P is not set
+# CONFIG_DVB_DIB8000 is not set
+# CONFIG_DVB_DIB9000 is not set
+# CONFIG_DVB_DRX39XYJ is not set
+# CONFIG_DVB_DRXD is not set
+# CONFIG_DVB_DRXK is not set
+# CONFIG_DVB_DS3000 is not set
+# CONFIG_DVB_DUMMY_FE is not set
+# CONFIG_DVB_DYNAMIC_MINORS is not set
+# CONFIG_DVB_EC100 is not set
+# CONFIG_DVB_FIREDTV is not set
+# CONFIG_DVB_HELENE is not set
+# CONFIG_DVB_HORUS3A is not set
+# CONFIG_DVB_ISL6405 is not set
+# CONFIG_DVB_ISL6421 is not set
+# CONFIG_DVB_ISL6423 is not set
+# CONFIG_DVB_IX2505V is not set
+# CONFIG_DVB_L64781 is not set
+# CONFIG_DVB_LG2160 is not set
+# CONFIG_DVB_LGDT3305 is not set
+# CONFIG_DVB_LGDT3306A is not set
+# CONFIG_DVB_LGDT330X is not set
+# CONFIG_DVB_LGS8GL5 is not set
+# CONFIG_DVB_LGS8GXX is not set
+# CONFIG_DVB_LNBH25 is not set
+# CONFIG_DVB_LNBH29 is not set
+# CONFIG_DVB_LNBP21 is not set
+# CONFIG_DVB_LNBP22 is not set
+# CONFIG_DVB_M88DS3103 is not set
+# CONFIG_DVB_M88RS2000 is not set
+CONFIG_DVB_MAX_ADAPTERS=16
+# CONFIG_DVB_MB86A16 is not set
+# CONFIG_DVB_MB86A20S is not set
+# CONFIG_DVB_MMAP is not set
+# CONFIG_DVB_MN88443X is not set
+# CONFIG_DVB_MN88472 is not set
+# CONFIG_DVB_MN88473 is not set
+# CONFIG_DVB_MT312 is not set
+# CONFIG_DVB_MT352 is not set
+# CONFIG_DVB_MXL5XX is not set
+# CONFIG_DVB_MXL692 is not set
+# CONFIG_DVB_NET is not set
+# CONFIG_DVB_NXT200X is not set
+# CONFIG_DVB_NXT6000 is not set
+# CONFIG_DVB_OR51132 is not set
+# CONFIG_DVB_OR51211 is not set
+# CONFIG_DVB_PLATFORM_DRIVERS is not set
+# CONFIG_DVB_PLL is not set
+# CONFIG_DVB_RTL2830 is not set
+# CONFIG_DVB_RTL2832 is not set
+# CONFIG_DVB_RTL2832_SDR is not set
+# CONFIG_DVB_S5H1409 is not set
+# CONFIG_DVB_S5H1411 is not set
+# CONFIG_DVB_S5H1420 is not set
+# CONFIG_DVB_S5H1432 is not set
+# CONFIG_DVB_S921 is not set
+# CONFIG_DVB_SI2165 is not set
+# CONFIG_DVB_SI2168 is not set
+# CONFIG_DVB_SI21XX is not set
+# CONFIG_DVB_SP2 is not set
+# CONFIG_DVB_SP8870 is not set
+# CONFIG_DVB_SP887X is not set
+# CONFIG_DVB_STB0899 is not set
+# CONFIG_DVB_STB6000 is not set
+# CONFIG_DVB_STB6100 is not set
+# CONFIG_DVB_STV0288 is not set
+# CONFIG_DVB_STV0297 is not set
+# CONFIG_DVB_STV0299 is not set
+# CONFIG_DVB_STV0367 is not set
+# CONFIG_DVB_STV0900 is not set
+# CONFIG_DVB_STV090x is not set
+# CONFIG_DVB_STV0910 is not set
+# CONFIG_DVB_STV6110 is not set
+# CONFIG_DVB_STV6110x is not set
+# CONFIG_DVB_STV6111 is not set
+# CONFIG_DVB_TC90522 is not set
+# CONFIG_DVB_TDA10021 is not set
+# CONFIG_DVB_TDA10023 is not set
+# CONFIG_DVB_TDA10048 is not set
+# CONFIG_DVB_TDA1004X is not set
+# CONFIG_DVB_TDA10071 is not set
+# CONFIG_DVB_TDA10086 is not set
+# CONFIG_DVB_TDA18271C2DD is not set
+# CONFIG_DVB_TDA665x is not set
+# CONFIG_DVB_TDA8083 is not set
+# CONFIG_DVB_TDA8261 is not set
+# CONFIG_DVB_TDA826X is not set
+# CONFIG_DVB_TEST_DRIVERS is not set
+# CONFIG_DVB_TS2020 is not set
+# CONFIG_DVB_TTUSB_BUDGET is not set
+# CONFIG_DVB_TTUSB_DEC is not set
+# CONFIG_DVB_TUA6100 is not set
+# CONFIG_DVB_TUNER_CX24113 is not set
+# CONFIG_DVB_TUNER_DIB0070 is not set
+# CONFIG_DVB_TUNER_DIB0090 is not set
+# CONFIG_DVB_TUNER_ITD1000 is not set
+# CONFIG_DVB_ULE_DEBUG is not set
+# CONFIG_DVB_USB is not set
+# CONFIG_DVB_USB_V2 is not set
+# CONFIG_DVB_VES1820 is not set
+# CONFIG_DVB_VES1X93 is not set
+# CONFIG_DVB_ZD1301_DEMOD is not set
+# CONFIG_DVB_ZL10036 is not set
+# CONFIG_DVB_ZL10039 is not set
+# CONFIG_DVB_ZL10353 is not set
+# CONFIG_DWC_XLGMAC is not set
+# CONFIG_DWMAC_DWC_QOS_ETH is not set
+# CONFIG_DWMAC_INTEL_PLAT is not set
+# CONFIG_DWMAC_IPQ806X is not set
+# CONFIG_DWMAC_LOONGSON is not set
+# CONFIG_DWMAC_LPC18XX is not set
+# CONFIG_DWMAC_MESON is not set
+# CONFIG_DWMAC_ROCKCHIP is not set
+# CONFIG_DWMAC_SOCFPGA is not set
+# CONFIG_DWMAC_STI is not set
+# CONFIG_DW_AXI_DMAC is not set
+# CONFIG_DW_DMAC is not set
+# CONFIG_DW_DMAC_PCI is not set
+# CONFIG_DW_EDMA is not set
+# CONFIG_DW_EDMA_PCIE is not set
+# CONFIG_DW_WATCHDOG is not set
+# CONFIG_DW_XDATA_PCIE is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_DYNAMIC_DEBUG_CORE is not set
+# CONFIG_E100 is not set
+# CONFIG_E1000 is not set
+# CONFIG_E1000E is not set
+# CONFIG_E1000E_HWTS is not set
+# CONFIG_EARLY_PRINTK_8250 is not set
+# CONFIG_EARLY_PRINTK_USB_XDBC is not set
+# CONFIG_EBC_C384_WDT is not set
+# CONFIG_ECHO is not set
+# CONFIG_ECRYPT_FS is not set
+# CONFIG_EDAC is not set
+# CONFIG_EEEPC_LAPTOP is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_EEPROM_93XX46 is not set
+# CONFIG_EEPROM_AT24 is not set
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_DIGSY_MTC_CFG is not set
+# CONFIG_EEPROM_EE1004 is not set
+# CONFIG_EEPROM_IDT_89HPESX is not set
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EFI is not set
+CONFIG_EFI_PARTITION=y
+# CONFIG_EFI_VARS_PSTORE is not set
+# CONFIG_EFS_FS is not set
+CONFIG_ELFCORE=y
+# CONFIG_ELF_CORE is not set
+# CONFIG_EMAC_ROCKCHIP is not set
+CONFIG_EMBEDDED=y
+# CONFIG_EM_TIMER_STI is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+# CONFIG_ENA_ETHERNET is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_ENCRYPTED_KEYS is not set
+# CONFIG_ENCX24J600 is not set
+# CONFIG_ENERGY_MODEL is not set
+# CONFIG_ENIC is not set
+# CONFIG_ENVELOPE_DETECTOR is not set
+# CONFIG_EPAPR_PARAVIRT is not set
+# CONFIG_EPIC100 is not set
+CONFIG_EPOLL=y
+# CONFIG_EQUALIZER is not set
+# CONFIG_EROFS_FS is not set
+# CONFIG_ET131X is not set
+CONFIG_ETHERNET=y
+# CONFIG_ETHOC is not set
+CONFIG_ETHTOOL_NETLINK=y
+CONFIG_EVENTFD=y
+# CONFIG_EVM is not set
+# CONFIG_EXFAT_FS is not set
+CONFIG_EXPERT=y
+CONFIG_EXPORTFS=y
+# CONFIG_EXPORTFS_BLOCK_OPS is not set
+# CONFIG_EXT2_FS is not set
+CONFIG_EXT2_FS_XATTR=y
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_DEBUG is not set
+# CONFIG_EXT4_ENCRYPTION is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_EXT4_FS_POSIX_ACL is not set
+# CONFIG_EXT4_FS_SECURITY is not set
+CONFIG_EXT4_USE_FOR_EXT2=y
+# CONFIG_EXTCON is not set
+# CONFIG_EXTCON_ADC_JACK is not set
+# CONFIG_EXTCON_ARIZONA is not set
+# CONFIG_EXTCON_AXP288 is not set
+# CONFIG_EXTCON_FSA9480 is not set
+# CONFIG_EXTCON_GPIO is not set
+# CONFIG_EXTCON_INTEL_INT3496 is not set
+# CONFIG_EXTCON_MAX3355 is not set
+# CONFIG_EXTCON_PTN5150 is not set
+# CONFIG_EXTCON_QCOM_SPMI_MISC is not set
+# CONFIG_EXTCON_RT8973A is not set
+# CONFIG_EXTCON_SM5502 is not set
+# CONFIG_EXTCON_USBC_TUSB320 is not set
+# CONFIG_EXTCON_USB_GPIO is not set
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_EXTRA_TARGETS=""
+# CONFIG_EXYNOS_ADC is not set
+# CONFIG_EXYNOS_VIDEO is not set
+# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_F2FS_CHECK_FS is not set
+# CONFIG_F2FS_FAULT_INJECTION is not set
+# CONFIG_F2FS_FS is not set
+# CONFIG_F2FS_FS_COMPRESSION is not set
+# CONFIG_F2FS_FS_ENCRYPTION is not set
+# CONFIG_F2FS_FS_POSIX_ACL is not set
+# CONFIG_F2FS_FS_SECURITY is not set
+CONFIG_F2FS_FS_XATTR=y
+# CONFIG_F2FS_IOSTAT is not set
+# CONFIG_F2FS_IO_TRACE is not set
+CONFIG_F2FS_STAT_FS=y
+# CONFIG_FAILOVER is not set
+# CONFIG_FAIR_GROUP_SCHED is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_FAT_DEFAULT_UTF8 is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_FB is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_ARMCLCD is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_ATMEL is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_AUO_K190X is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_BIG_ENDIAN is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+# CONFIG_FB_BOTH_ENDIAN is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_CARMINE is not set
+# CONFIG_FB_CFB_COPYAREA is not set
+# CONFIG_FB_CFB_FILLRECT is not set
+# CONFIG_FB_CFB_IMAGEBLIT is not set
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_DA8XX is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_FLEX is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_FSL_DIU is not set
+# CONFIG_FB_GEODE is not set
+# CONFIG_FB_GOLDFISH is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_I740 is not set
+# CONFIG_FB_IBM_GXT4500 is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_IMX is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_LE80578 is not set
+# CONFIG_FB_LITTLE_ENDIAN is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_MXS is not set
+# CONFIG_FB_N411 is not set
+# CONFIG_FB_NEOMAGIC is not set
+CONFIG_FB_NOTIFY=y
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_OF is not set
+# CONFIG_FB_OMAP2 is not set
+# CONFIG_FB_OPENCORES is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_PS3 is not set
+# CONFIG_FB_PXA is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIMPLE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_SM712 is not set
+# CONFIG_FB_SM750 is not set
+# CONFIG_FB_SMSCUFX is not set
+# CONFIG_FB_SSD1307 is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_TFT is not set
+# CONFIG_FB_TFT_AGM1264K_FL is not set
+# CONFIG_FB_TFT_BD663474 is not set
+# CONFIG_FB_TFT_FBTFT_DEVICE is not set
+# CONFIG_FB_TFT_HX8340BN is not set
+# CONFIG_FB_TFT_HX8347D is not set
+# CONFIG_FB_TFT_HX8353D is not set
+# CONFIG_FB_TFT_HX8357D is not set
+# CONFIG_FB_TFT_ILI9163 is not set
+# CONFIG_FB_TFT_ILI9320 is not set
+# CONFIG_FB_TFT_ILI9325 is not set
+# CONFIG_FB_TFT_ILI9340 is not set
+# CONFIG_FB_TFT_ILI9341 is not set
+# CONFIG_FB_TFT_ILI9481 is not set
+# CONFIG_FB_TFT_ILI9486 is not set
+# CONFIG_FB_TFT_PCD8544 is not set
+# CONFIG_FB_TFT_RA8875 is not set
+# CONFIG_FB_TFT_S6D02A1 is not set
+# CONFIG_FB_TFT_S6D1121 is not set
+# CONFIG_FB_TFT_SEPS525 is not set
+# CONFIG_FB_TFT_SH1106 is not set
+# CONFIG_FB_TFT_SSD1289 is not set
+# CONFIG_FB_TFT_SSD1305 is not set
+# CONFIG_FB_TFT_SSD1306 is not set
+# CONFIG_FB_TFT_SSD1325 is not set
+# CONFIG_FB_TFT_SSD1331 is not set
+# CONFIG_FB_TFT_SSD1351 is not set
+# CONFIG_FB_TFT_ST7735R is not set
+# CONFIG_FB_TFT_ST7789V is not set
+# CONFIG_FB_TFT_TINYLCD is not set
+# CONFIG_FB_TFT_TLS8204 is not set
+# CONFIG_FB_TFT_UC1611 is not set
+# CONFIG_FB_TFT_UC1701 is not set
+# CONFIG_FB_TFT_UPD161704 is not set
+# CONFIG_FB_TFT_WATTEROTT is not set
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_TMIO is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_UDL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_VGA16 is not set
+# CONFIG_FB_VIA is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_XGI is not set
+# CONFIG_FCOE is not set
+# CONFIG_FCOE_FNIC is not set
+# CONFIG_FDDI is not set
+# CONFIG_FEALNX is not set
+# CONFIG_FENCE_TRACE is not set
+# CONFIG_FHANDLE is not set
+CONFIG_FIB_RULES=y
+# CONFIG_FIELDBUS_DEV is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_FIND_BIT_BENCHMARK is not set
+# CONFIG_FIREWIRE is not set
+# CONFIG_FIREWIRE_NOSY is not set
+# CONFIG_FIREWIRE_SERIAL is not set
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+# CONFIG_FIRMWARE_MEMMAP is not set
+# CONFIG_FIT_PARTITION is not set
+# CONFIG_FIXED_PHY is not set
+CONFIG_FLATMEM=y
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_FM10K is not set
+# CONFIG_FMC is not set
+# CONFIG_FONTS is not set
+# CONFIG_FONT_6x8 is not set
+# CONFIG_FONT_TER16x32 is not set
+# CONFIG_FORCEDETH is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_FORTIFY_SOURCE=y
+# CONFIG_FPGA is not set
+# CONFIG_FRAMEBUFFER_CONSOLE is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set
+# CONFIG_FRAME_POINTER is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_FREEZER is not set
+# CONFIG_FRONTSWAP is not set
+# CONFIG_FSCACHE is not set
+# CONFIG_FSI is not set
+# CONFIG_FSL_EDMA is not set
+# CONFIG_FSL_ENETC is not set
+# CONFIG_FSL_ENETC_IERB is not set
+# CONFIG_FSL_ENETC_MDIO is not set
+# CONFIG_FSL_ENETC_VF is not set
+# CONFIG_FSL_ERRATUM_A008585 is not set
+# CONFIG_FSL_MC_BUS is not set
+# CONFIG_FSL_PQ_MDIO is not set
+# CONFIG_FSL_QDMA is not set
+# CONFIG_FSL_RCPM is not set
+# CONFIG_FSL_XGMAC_MDIO is not set
+CONFIG_FSNOTIFY=y
+# CONFIG_FS_DAX is not set
+# CONFIG_FS_ENCRYPTION is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_FS_VERITY is not set
+# CONFIG_FTGMAC100 is not set
+# CONFIG_FTL is not set
+# CONFIG_FTMAC100 is not set
+# CONFIG_FTRACE is not set
+# CONFIG_FTRACE_RECORD_RECURSION is not set
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_FTR_FIXUP_SELFTEST is not set
+# CONFIG_FTWDT010_WATCHDOG is not set
+# CONFIG_FUJITSU_ERRATUM_010001 is not set
+# CONFIG_FUJITSU_ES is not set
+# CONFIG_FUJITSU_LAPTOP is not set
+# CONFIG_FUJITSU_TABLET is not set
+# CONFIG_FUNCTION_ERROR_INJECTION is not set
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_FUSE_FS is not set
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_FC is not set
+# CONFIG_FUSION_SAS is not set
+# CONFIG_FUSION_SPI is not set
+CONFIG_FUTEX=y
+CONFIG_FUTEX_PI=y
+# CONFIG_FW_CFG_SYSFS is not set
+CONFIG_FW_LOADER=y
+# CONFIG_FW_LOADER_COMPRESS is not set
+CONFIG_FW_LOADER_USER_HELPER=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+# CONFIG_FXAS21002C is not set
+# CONFIG_FXLS8962AF_I2C is not set
+# CONFIG_FXLS8962AF_SPI is not set
+# CONFIG_FXOS8700_I2C is not set
+# CONFIG_FXOS8700_SPI is not set
+CONFIG_GACT_PROB=y
+# CONFIG_GADGET_UAC1 is not set
+# CONFIG_GAMEPORT is not set
+# CONFIG_GATEWORKS_GW16083 is not set
+# CONFIG_GCC_PLUGINS is not set
+# CONFIG_GCOV is not set
+# CONFIG_GCOV_KERNEL is not set
+# CONFIG_GDB_SCRIPTS is not set
+# CONFIG_GEMINI_ETHERNET is not set
+# CONFIG_GENERIC_ADC_BATTERY is not set
+# CONFIG_GENERIC_ADC_THERMAL is not set
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+# CONFIG_GENERIC_CPU_DEVICES is not set
+CONFIG_GENERIC_HWEIGHT=y
+# CONFIG_GENERIC_IRQ_DEBUGFS is not set
+CONFIG_GENERIC_IRQ_IPI=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_NET_UTILS=y
+# CONFIG_GENERIC_PHY is not set
+CONFIG_GENERIC_PTDUMP=y
+CONFIG_GENERIC_VDSO_TIME_NS=y
+# CONFIG_GENEVE is not set
+# CONFIG_GENWQE is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_GIGASET_CAPI is not set
+# CONFIG_GIGASET_DEBUG is not set
+# CONFIG_GIGASET_DUMMYLL is not set
+# CONFIG_GLOB_SELFTEST is not set
+# CONFIG_GNSS is not set
+# CONFIG_GOLDFISH is not set
+# CONFIG_GOOGLE_FIRMWARE is not set
+# CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT is not set
+# CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY is not set
+# CONFIG_GOOGLE_SMI is not set
+# CONFIG_GP2AP002 is not set
+# CONFIG_GP2AP020A00F is not set
+# CONFIG_GPD_POCKET_FAN is not set
+CONFIG_GPIOLIB=y
+CONFIG_GPIOLIB_FASTPATH_LIMIT=512
+# CONFIG_GPIO_104_DIO_48E is not set
+# CONFIG_GPIO_104_IDIO_16 is not set
+# CONFIG_GPIO_104_IDI_48 is not set
+# CONFIG_GPIO_74X164 is not set
+# CONFIG_GPIO_74XX_MMIO is not set
+# CONFIG_GPIO_ADNP is not set
+# CONFIG_GPIO_ADP5588 is not set
+# CONFIG_GPIO_AGGREGATOR is not set
+# CONFIG_GPIO_ALTERA is not set
+# CONFIG_GPIO_AMD8111 is not set
+# CONFIG_GPIO_AMDPT is not set
+# CONFIG_GPIO_AMD_FCH is not set
+# CONFIG_GPIO_BCM_KONA is not set
+# CONFIG_GPIO_BT8XX is not set
+# CONFIG_GPIO_CADENCE is not set
+# CONFIG_GPIO_CASCADE is not set
+# CONFIG_GPIO_CDEV is not set
+# CONFIG_GPIO_CDEV_V1 is not set
+# CONFIG_GPIO_CS5535 is not set
+# CONFIG_GPIO_DWAPB is not set
+# CONFIG_GPIO_EM is not set
+# CONFIG_GPIO_EXAR is not set
+# CONFIG_GPIO_F7188X is not set
+# CONFIG_GPIO_FTGPIO010 is not set
+# CONFIG_GPIO_GENERIC_PLATFORM is not set
+# CONFIG_GPIO_GPIO_MM is not set
+# CONFIG_GPIO_GRGPIO is not set
+# CONFIG_GPIO_GW_PLD is not set
+# CONFIG_GPIO_HLWD is not set
+# CONFIG_GPIO_ICH is not set
+# CONFIG_GPIO_IT87 is not set
+# CONFIG_GPIO_LOGICVC is not set
+# CONFIG_GPIO_LYNXPOINT is not set
+# CONFIG_GPIO_MAX3191X is not set
+# CONFIG_GPIO_MAX7300 is not set
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_MB86S7X is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_ML_IOH is not set
+# CONFIG_GPIO_MOCKUP is not set
+# CONFIG_GPIO_MPC8XXX is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCA953X_IRQ is not set
+# CONFIG_GPIO_PCA9570 is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_GPIO_PCH is not set
+# CONFIG_GPIO_PCIE_IDIO_24 is not set
+# CONFIG_GPIO_PCI_IDIO_16 is not set
+# CONFIG_GPIO_PISOSR is not set
+# CONFIG_GPIO_PL061 is not set
+# CONFIG_GPIO_PWM is not set
+# CONFIG_GPIO_RCAR is not set
+# CONFIG_GPIO_RDC321X is not set
+# CONFIG_GPIO_SAMA5D2_PIOBU is not set
+# CONFIG_GPIO_SCH is not set
+# CONFIG_GPIO_SCH311X is not set
+# CONFIG_GPIO_SIFIVE is not set
+# CONFIG_GPIO_SX150X is not set
+# CONFIG_GPIO_SYSCON is not set
+CONFIG_GPIO_SYSFS=y
+# CONFIG_GPIO_TPIC2810 is not set
+# CONFIG_GPIO_TS4900 is not set
+# CONFIG_GPIO_TS5500 is not set
+# CONFIG_GPIO_VIRTIO is not set
+# CONFIG_GPIO_VX855 is not set
+# CONFIG_GPIO_WATCHDOG is not set
+# CONFIG_GPIO_WINBOND is not set
+# CONFIG_GPIO_WS16C48 is not set
+# CONFIG_GPIO_XGENE is not set
+# CONFIG_GPIO_XILINX is not set
+# CONFIG_GPIO_XRA1403 is not set
+# CONFIG_GPIO_ZEVIO is not set
+# CONFIG_GPIO_ZX is not set
+# CONFIG_GREENASIA_FF is not set
+# CONFIG_GREYBUS is not set
+# CONFIG_GS_FPGABOOT is not set
+# CONFIG_GTP is not set
+# CONFIG_GUP_BENCHMARK is not set
+# CONFIG_GUP_TEST is not set
+# CONFIG_GVE is not set
+# CONFIG_HABANA_AI is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_HAPPYMEAL is not set
+CONFIG_HARDENED_USERCOPY=y
+# CONFIG_HARDENED_USERCOPY_FALLBACK is not set
+# CONFIG_HARDENED_USERCOPY_PAGESPAN is not set
+CONFIG_HARDEN_BRANCH_HISTORY=y
+CONFIG_HARDEN_EL2_VECTORS=y
+# CONFIG_HARDLOCKUP_DETECTOR is not set
+# CONFIG_HAVE_ARM_ARCH_TIMER is not set
+# CONFIG_HCALL_STATS is not set
+# CONFIG_HDC100X is not set
+# CONFIG_HDC2010 is not set
+# CONFIG_HDLC is not set
+# CONFIG_HDLC_CISCO is not set
+# CONFIG_HDLC_FR is not set
+# CONFIG_HDLC_PPP is not set
+# CONFIG_HDLC_RAW is not set
+# CONFIG_HDLC_RAW_ETH is not set
+# CONFIG_HDMI_LPE_AUDIO is not set
+# CONFIG_HDQ_MASTER_OMAP is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_HEADERS_INSTALL is not set
+# CONFIG_HEADER_TEST is not set
+# CONFIG_HERMES is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_HFSPLUS_FS_POSIX_ACL is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFS_FS_POSIX_ACL is not set
+# CONFIG_HI6421V600_IRQ is not set
+# CONFIG_HI8435 is not set
+# CONFIG_HIBERNATION is not set
+# CONFIG_HID is not set
+# CONFIG_HIDRAW is not set
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACCUTOUCH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_ACRUX_FF is not set
+# CONFIG_HID_ALPS is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_APPLEIR is not set
+# CONFIG_HID_ASUS is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BATTERY_STRENGTH is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_BETOP_FF is not set
+# CONFIG_HID_BIGBEN_FF is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_CMEDIA is not set
+# CONFIG_HID_CORSAIR is not set
+# CONFIG_HID_COUGAR is not set
+# CONFIG_HID_CP2112 is not set
+# CONFIG_HID_CREATIVE_SB0540 is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_ELAN is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_ELO is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_FT260 is not set
+# CONFIG_HID_GEMBIRD is not set
+# CONFIG_HID_GENERIC is not set
+# CONFIG_HID_GFRM is not set
+# CONFIG_HID_GLORIOUS is not set
+# CONFIG_HID_GOOGLE_HAMMER is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_GT683R is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_HOLTEK is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_ITE is not set
+# CONFIG_HID_JABRA is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LED is not set
+# CONFIG_HID_LENOVO is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_LOGITECH_DJ is not set
+# CONFIG_HID_LOGITECH_HIDPP is not set
+# CONFIG_HID_MACALLY is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MALTRON is not set
+# CONFIG_HID_MAYFLASH is not set
+# CONFIG_HID_MCP2221 is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_NTI is not set
+# CONFIG_HID_NTRIG is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PENMOUNT is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PID is not set
+# CONFIG_HID_PLANTRONICS is not set
+# CONFIG_HID_PLAYSTATION is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_PRODIKEYS is not set
+# CONFIG_HID_REDRAGON is not set
+# CONFIG_HID_RETRODE is not set
+# CONFIG_HID_RMI is not set
+# CONFIG_HID_ROCCAT is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SEMITEK is not set
+# CONFIG_HID_SENSOR_HUB is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_SONY is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEAM is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_THINGM is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_U2FZERO is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_UDRAW_PS3 is not set
+# CONFIG_HID_VIEWSONIC is not set
+# CONFIG_HID_VIVALDI is not set
+# CONFIG_HID_WACOM is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_WIIMOTE is not set
+# CONFIG_HID_XINMO is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_HIGH_RES_TIMERS=y
+# CONFIG_HINIC is not set
+# CONFIG_HIP04_ETH is not set
+# CONFIG_HIPPI is not set
+# CONFIG_HISILICON_ERRATUM_161010101 is not set
+# CONFIG_HISILICON_ERRATUM_161600802 is not set
+# CONFIG_HISI_DMA is not set
+# CONFIG_HISI_FEMAC is not set
+# CONFIG_HISI_HIKEY_USB is not set
+# CONFIG_HIST_TRIGGERS_DEBUG is not set
+# CONFIG_HIX5HD2_GMAC is not set
+# CONFIG_HMC425 is not set
+# CONFIG_HMC6352 is not set
+# CONFIG_HNS is not set
+# CONFIG_HNS3 is not set
+# CONFIG_HNS_DSAF is not set
+# CONFIG_HNS_ENET is not set
+# CONFIG_HOSTAP is not set
+# CONFIG_HOSTAP_CS is not set
+# CONFIG_HOSTAP_PCI is not set
+# CONFIG_HOSTAP_PLX is not set
+# CONFIG_HOTPLUG_CPU is not set
+# CONFIG_HOTPLUG_PCI is not set
+# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set
+# CONFIG_HOTPLUG_PCI_COMPAQ is not set
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# CONFIG_HOTPLUG_PCI_IBM is not set
+# CONFIG_HOTPLUG_PCI_SHPC is not set
+# CONFIG_HP03 is not set
+# CONFIG_HP100 is not set
+# CONFIG_HP206C is not set
+CONFIG_HPET_MMAP_DEFAULT=y
+# CONFIG_HPFS_FS is not set
+# CONFIG_HP_ILO is not set
+# CONFIG_HP_WIRELESS is not set
+# CONFIG_HSA_AMD is not set
+# CONFIG_HSI is not set
+# CONFIG_HSR is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_I2CPLD is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTS221 is not set
+# CONFIG_HTU21 is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_HVC_UDBG is not set
+# CONFIG_HWLAT_TRACER is not set
+# CONFIG_HWMON is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+# CONFIG_HWMON_VID is not set
+# CONFIG_HWSPINLOCK is not set
+# CONFIG_HWSPINLOCK_OMAP is not set
+CONFIG_HW_PERF_EVENTS=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HW_RANDOM_AMD is not set
+# CONFIG_HW_RANDOM_ARM_SMCCC_TRNG is not set
+# CONFIG_HW_RANDOM_ATMEL is not set
+# CONFIG_HW_RANDOM_BA431 is not set
+# CONFIG_HW_RANDOM_BCM2835 is not set
+# CONFIG_HW_RANDOM_CAVIUM is not set
+# CONFIG_HW_RANDOM_CCTRNG is not set
+# CONFIG_HW_RANDOM_EXYNOS is not set
+# CONFIG_HW_RANDOM_GEODE is not set
+# CONFIG_HW_RANDOM_INTEL is not set
+# CONFIG_HW_RANDOM_IPROC_RNG200 is not set
+# CONFIG_HW_RANDOM_MTK is not set
+# CONFIG_HW_RANDOM_OMAP is not set
+# CONFIG_HW_RANDOM_OMAP3_ROM is not set
+# CONFIG_HW_RANDOM_PPC4XX is not set
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+CONFIG_HW_RANDOM_TPM=y
+# CONFIG_HW_RANDOM_VIA is not set
+# CONFIG_HW_RANDOM_VIRTIO is not set
+# CONFIG_HW_RANDOM_XIPHERA is not set
+# CONFIG_HX711 is not set
+# CONFIG_HYPERV is not set
+# CONFIG_HYPERV_TSCPAGE is not set
+# CONFIG_HYSDN is not set
+CONFIG_HZ=100
+CONFIG_HZ_100=y
+# CONFIG_HZ_1000 is not set
+# CONFIG_HZ_1024 is not set
+# CONFIG_HZ_128 is not set
+# CONFIG_HZ_200 is not set
+# CONFIG_HZ_24 is not set
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_256 is not set
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_48 is not set
+# CONFIG_HZ_500 is not set
+# CONFIG_HZ_PERIODIC is not set
+# CONFIG_I2C is not set
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCA is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set
+# CONFIG_I2C_AU1550 is not set
+# CONFIG_I2C_BCM2835 is not set
+# CONFIG_I2C_BCM_IPROC is not set
+# CONFIG_I2C_BRCMSTB is not set
+# CONFIG_I2C_CADENCE is not set
+# CONFIG_I2C_CBUS_GPIO is not set
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_CP2615 is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEMUX_PINCTRL is not set
+# CONFIG_I2C_DESIGNWARE_PCI is not set
+# CONFIG_I2C_DESIGNWARE_PLATFORM is not set
+# CONFIG_I2C_DESIGNWARE_SLAVE is not set
+# CONFIG_I2C_DIOLAN_U2C is not set
+# CONFIG_I2C_EG20T is not set
+# CONFIG_I2C_ELEKTOR is not set
+# CONFIG_I2C_EMEV2 is not set
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+# CONFIG_I2C_HID is not set
+# CONFIG_I2C_HID_OF is not set
+# CONFIG_I2C_HID_OF_GOODIX is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_IBM_IIC is not set
+# CONFIG_I2C_IMG is not set
+# CONFIG_I2C_ISCH is not set
+# CONFIG_I2C_ISMT is not set
+# CONFIG_I2C_JZ4780 is not set
+# CONFIG_I2C_MLXCPLD is not set
+# CONFIG_I2C_MPC is not set
+# CONFIG_I2C_MT65XX is not set
+# CONFIG_I2C_MUX is not set
+# CONFIG_I2C_MUX_GPIO is not set
+# CONFIG_I2C_MUX_GPMUX is not set
+# CONFIG_I2C_MUX_LTC4306 is not set
+# CONFIG_I2C_MUX_MLXCPLD is not set
+# CONFIG_I2C_MUX_PCA9541 is not set
+# CONFIG_I2C_MUX_PCA954x is not set
+# CONFIG_I2C_MUX_PINCTRL is not set
+# CONFIG_I2C_MUX_REG is not set
+# CONFIG_I2C_MV64XXX is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_NOMADIK is not set
+# CONFIG_I2C_NVIDIA_GPU is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_OCTEON is not set
+# CONFIG_I2C_PARPORT is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_PCA_ISA is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_PIIX4 is not set
+# CONFIG_I2C_PXA_PCI is not set
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_RCAR is not set
+# CONFIG_I2C_RK3X is not set
+# CONFIG_I2C_ROBOTFUZZ_OSIF is not set
+# CONFIG_I2C_S3C2410 is not set
+# CONFIG_I2C_SCMI is not set
+# CONFIG_I2C_SH_MOBILE is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_SLAVE is not set
+# CONFIG_I2C_SLAVE_EEPROM is not set
+# CONFIG_I2C_SMBUS is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_THUNDERX is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_VERSATILE is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_VIRTIO is not set
+# CONFIG_I2C_XILINX is not set
+# CONFIG_I3C is not set
+# CONFIG_I40E is not set
+# CONFIG_I40EVF is not set
+# CONFIG_I6300ESB_WDT is not set
+# CONFIG_I82092 is not set
+# CONFIG_I82365 is not set
+# CONFIG_IAQCORE is not set
+# CONFIG_IBM_ASM is not set
+# CONFIG_IBM_EMAC_DEBUG is not set
+# CONFIG_IBM_EMAC_EMAC4 is not set
+# CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_IBM_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_EMAC_RGMII is not set
+# CONFIG_IBM_EMAC_TAH is not set
+# CONFIG_IBM_EMAC_ZMII is not set
+# CONFIG_ICE is not set
+# CONFIG_ICP10100 is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ICST is not set
+# CONFIG_IDE is not set
+# CONFIG_IDEAPAD_LAPTOP is not set
+# CONFIG_IDE_GD is not set
+# CONFIG_IDE_PROC_FS is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+# CONFIG_IDLE_PAGE_TRACKING is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_IEEE802154_ADF7242 is not set
+# CONFIG_IEEE802154_ATUSB is not set
+# CONFIG_IEEE802154_CA8210 is not set
+# CONFIG_IEEE802154_HWSIM is not set
+# CONFIG_IEEE802154_MCR20A is not set
+# CONFIG_IFB is not set
+# CONFIG_IGB is not set
+# CONFIG_IGBVF is not set
+# CONFIG_IGC is not set
+# CONFIG_IIO is not set
+# CONFIG_IIO_BUFFER is not set
+# CONFIG_IIO_BUFFER_CB is not set
+# CONFIG_IIO_BUFFER_DMA is not set
+# CONFIG_IIO_BUFFER_DMAENGINE is not set
+# CONFIG_IIO_BUFFER_HDC2010 is not set
+# CONFIG_IIO_BUFFER_HW_CONSUMER is not set
+# CONFIG_IIO_CONFIGFS is not set
+CONFIG_IIO_CONSUMERS_PER_TRIGGER=2
+# CONFIG_IIO_CROS_EC_ACCEL_LEGACY is not set
+# CONFIG_IIO_INTERRUPT_TRIGGER is not set
+# CONFIG_IIO_MUX is not set
+# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set
+# CONFIG_IIO_RESCALE is not set
+# CONFIG_IIO_SIMPLE_DUMMY is not set
+# CONFIG_IIO_SSP_SENSORHUB is not set
+# CONFIG_IIO_ST_ACCEL_3AXIS is not set
+# CONFIG_IIO_ST_GYRO_3AXIS is not set
+# CONFIG_IIO_ST_LSM6DSX is not set
+# CONFIG_IIO_ST_LSM9DS0 is not set
+# CONFIG_IIO_ST_MAGN_3AXIS is not set
+# CONFIG_IIO_ST_PRESS is not set
+# CONFIG_IIO_SW_DEVICE is not set
+# CONFIG_IIO_SW_TRIGGER is not set
+# CONFIG_IIO_SYSFS_TRIGGER is not set
+# CONFIG_IIO_TRIGGER is not set
+# CONFIG_IIO_TRIGGERED_EVENT is not set
+# CONFIG_IKCONFIG is not set
+# CONFIG_IKCONFIG_PROC is not set
+# CONFIG_IKHEADERS is not set
+# CONFIG_IMA is not set
+# CONFIG_IMGPDC_WDT is not set
+# CONFIG_IMG_MDC_DMA is not set
+# CONFIG_IMX7D_ADC is not set
+# CONFIG_IMX_IPUV3_CORE is not set
+# CONFIG_IMX_THERMAL is not set
+# CONFIG_INA2XX_ADC is not set
+# CONFIG_INDIRECT_PIO is not set
+CONFIG_INET=y
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_ESPINTCP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_ESPINTCP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_LRO is not set
+CONFIG_INET_TABLE_PERTURB_ORDER=16
+# CONFIG_INET_TCP_DIAG is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_UDP_DIAG is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INFINIBAND is not set
+# CONFIG_INFTL is not set
+# CONFIG_INGENIC_ADC is not set
+# CONFIG_INGENIC_CGU_JZ4725B is not set
+# CONFIG_INGENIC_CGU_JZ4740 is not set
+# CONFIG_INGENIC_CGU_JZ4760 is not set
+# CONFIG_INGENIC_CGU_JZ4770 is not set
+# CONFIG_INGENIC_CGU_JZ4780 is not set
+# CONFIG_INGENIC_CGU_X1000 is not set
+# CONFIG_INGENIC_CGU_X1830 is not set
+# CONFIG_INGENIC_OST is not set
+# CONFIG_INGENIC_SYSOST is not set
+# CONFIG_INGENIC_TCU_CLK is not set
+# CONFIG_INGENIC_TCU_IRQ is not set
+# CONFIG_INGENIC_TIMER is not set
+CONFIG_INIT_ENV_ARG_LIMIT=32
+# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set
+# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set
+# CONFIG_INIT_STACK_ALL_PATTERN is not set
+# CONFIG_INIT_STACK_ALL_ZERO is not set
+CONFIG_INIT_STACK_NONE=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_INPUT is not set
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_ADXL34X is not set
+# CONFIG_INPUT_APANEL is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_ATLAS_BTNS is not set
+# CONFIG_INPUT_ATMEL_CAPTOUCH is not set
+# CONFIG_INPUT_AXP20X_PEK is not set
+# CONFIG_INPUT_BMA150 is not set
+# CONFIG_INPUT_CM109 is not set
+# CONFIG_INPUT_CMA3000 is not set
+# CONFIG_INPUT_DA7280_HAPTICS is not set
+# CONFIG_INPUT_DRV260X_HAPTICS is not set
+# CONFIG_INPUT_DRV2665_HAPTICS is not set
+# CONFIG_INPUT_DRV2667_HAPTICS is not set
+# CONFIG_INPUT_E3X0_BUTTON is not set
+# CONFIG_INPUT_EVBUG is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_GP2A is not set
+# CONFIG_INPUT_GPIO_BEEPER is not set
+# CONFIG_INPUT_GPIO_DECODER is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+# CONFIG_INPUT_GPIO_TILT_POLLED is not set
+# CONFIG_INPUT_GPIO_VIBRA is not set
+# CONFIG_INPUT_IDEAPAD_SLIDEBAR is not set
+# CONFIG_INPUT_IMS_PCU is not set
+# CONFIG_INPUT_IQS269A is not set
+# CONFIG_INPUT_IQS626A is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_KXTJ9 is not set
+# CONFIG_INPUT_LEDS is not set
+# CONFIG_INPUT_MATRIXKMAP is not set
+# CONFIG_INPUT_MAX8997_HAPTIC is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_MMA8450 is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_MPU3050 is not set
+# CONFIG_INPUT_MSM_VIBRATOR is not set
+# CONFIG_INPUT_PALMAS_PWRBUTTON is not set
+# CONFIG_INPUT_PCF8574 is not set
+# CONFIG_INPUT_PCSPKR is not set
+# CONFIG_INPUT_PM8941_PWRKEY is not set
+# CONFIG_INPUT_PM8XXX_VIBRATOR is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_PWM_BEEPER is not set
+# CONFIG_INPUT_PWM_VIBRA is not set
+# CONFIG_INPUT_REGULATOR_HAPTIC is not set
+# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_TPS65218_PWRBUTTON is not set
+# CONFIG_INPUT_TWL4030_PWRBUTTON is not set
+# CONFIG_INPUT_TWL4030_VIBRA is not set
+# CONFIG_INPUT_TWL6040_VIBRA is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_INPUT_WISTRON_BTNS is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INT340X_THERMAL is not set
+# CONFIG_INTEGRITY is not set
+# CONFIG_INTEGRITY_AUDIT is not set
+# CONFIG_INTEGRITY_SIGNATURE is not set
+# CONFIG_INTEL_ATOMISP2_LED is not set
+# CONFIG_INTEL_ATOMISP2_PM is not set
+# CONFIG_INTEL_CHT_INT33FE is not set
+# CONFIG_INTEL_HID_EVENT is not set
+# CONFIG_INTEL_IDLE is not set
+# CONFIG_INTEL_IDMA64 is not set
+# CONFIG_INTEL_IDXD is not set
+# CONFIG_INTEL_IDXD_COMPAT is not set
+# CONFIG_INTEL_INT0002_VGPIO is not set
+# CONFIG_INTEL_IOATDMA is not set
+# CONFIG_INTEL_ISH_HID is not set
+# CONFIG_INTEL_MEI is not set
+# CONFIG_INTEL_MEI_ME is not set
+# CONFIG_INTEL_MEI_TXE is not set
+# CONFIG_INTEL_MIC_CARD is not set
+# CONFIG_INTEL_MIC_HOST is not set
+# CONFIG_INTEL_MID_PTI is not set
+# CONFIG_INTEL_OAKTRAIL is not set
+# CONFIG_INTEL_PMC_CORE is not set
+# CONFIG_INTEL_PUNIT_IPC is not set
+# CONFIG_INTEL_RST is not set
+# CONFIG_INTEL_SMARTCONNECT is not set
+# CONFIG_INTEL_SOC_PMIC is not set
+# CONFIG_INTEL_SOC_PMIC_CHTDC_TI is not set
+# CONFIG_INTEL_SOC_PMIC_CHTWC is not set
+# CONFIG_INTEL_TH is not set
+# CONFIG_INTEL_VBTN is not set
+# CONFIG_INTEL_XWAY_PHY is not set
+# CONFIG_INTERCONNECT is not set
+# CONFIG_INTERVAL_TREE_TEST is not set
+# CONFIG_INV_ICM42600_I2C is not set
+# CONFIG_INV_ICM42600_SPI is not set
+# CONFIG_INV_MPU6050_I2C is not set
+# CONFIG_INV_MPU6050_IIO is not set
+# CONFIG_INV_MPU6050_SPI is not set
+# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set
+# CONFIG_IOMMU_DEFAULT_DMA_STRICT is not set
+# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set
+# CONFIG_IOMMU_SUPPORT is not set
+# CONFIG_IONIC is not set
+# CONFIG_IOSCHED_BFQ is not set
+# CONFIG_IOSM is not set
+CONFIG_IO_STRICT_DEVMEM=y
+# CONFIG_IO_URING is not set
+CONFIG_IO_WQ=y
+# CONFIG_IP17XX_PHY is not set
+# CONFIG_IP6_NF_FILTER is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP6_NF_MANGLE is not set
+# CONFIG_IP6_NF_MATCH_AH is not set
+# CONFIG_IP6_NF_MATCH_EUI64 is not set
+# CONFIG_IP6_NF_MATCH_FRAG is not set
+# CONFIG_IP6_NF_MATCH_HL is not set
+# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set
+# CONFIG_IP6_NF_MATCH_MH is not set
+# CONFIG_IP6_NF_MATCH_OPTS is not set
+# CONFIG_IP6_NF_MATCH_RPFILTER is not set
+# CONFIG_IP6_NF_MATCH_RT is not set
+# CONFIG_IP6_NF_MATCH_SRH is not set
+# CONFIG_IP6_NF_NAT is not set
+# CONFIG_IP6_NF_RAW is not set
+# CONFIG_IP6_NF_SECURITY is not set
+# CONFIG_IP6_NF_TARGET_HL is not set
+# CONFIG_IP6_NF_TARGET_MASQUERADE is not set
+# CONFIG_IP6_NF_TARGET_REJECT is not set
+# CONFIG_IP6_NF_TARGET_SYNPROXY is not set
+# CONFIG_IPACK_BUS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_IPMB_DEVICE_INTERFACE is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_IPV6 is not set
+# CONFIG_IPV6_FOU is not set
+# CONFIG_IPV6_FOU_TUNNEL is not set
+# CONFIG_IPV6_ILA is not set
+# CONFIG_IPV6_IOAM6_LWTUNNEL is not set
+# CONFIG_IPV6_MIP6 is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_IPV6_MROUTE_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+CONFIG_IPV6_NDISC_NODETYPE=y
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_ROUTE_INFO is not set
+# CONFIG_IPV6_RPL_LWTUNNEL is not set
+# CONFIG_IPV6_SEG6_HMAC is not set
+# CONFIG_IPV6_SEG6_LWTUNNEL is not set
+# CONFIG_IPV6_SIT is not set
+# CONFIG_IPV6_SIT_6RD is not set
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_IPV6_VTI is not set
+# CONFIG_IPVLAN is not set
+# CONFIG_IPVTAP is not set
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2100_DEBUG is not set
+CONFIG_IPW2100_MONITOR=y
+# CONFIG_IPW2200 is not set
+# CONFIG_IPW2200_DEBUG is not set
+CONFIG_IPW2200_MONITOR=y
+# CONFIG_IPW2200_PROMISCUOUS is not set
+# CONFIG_IPW2200_QOS is not set
+# CONFIG_IPW2200_RADIOTAP is not set
+# CONFIG_IPWIRELESS is not set
+# CONFIG_IPX is not set
+CONFIG_IP_ADVANCED_ROUTER=y
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_FIB_TRIE_STATS is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_MULTIPLE_TABLES=y
+# CONFIG_IP_NF_ARPFILTER is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+# CONFIG_IP_NF_ARP_MANGLE is not set
+# CONFIG_IP_NF_FILTER is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_MANGLE is not set
+# CONFIG_IP_NF_MATCH_AH is not set
+# CONFIG_IP_NF_MATCH_ECN is not set
+# CONFIG_IP_NF_MATCH_RPFILTER is not set
+# CONFIG_IP_NF_MATCH_TTL is not set
+# CONFIG_IP_NF_RAW is not set
+# CONFIG_IP_NF_SECURITY is not set
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+# CONFIG_IP_NF_TARGET_ECN is not set
+# CONFIG_IP_NF_TARGET_MASQUERADE is not set
+# CONFIG_IP_NF_TARGET_NETMAP is not set
+# CONFIG_IP_NF_TARGET_REDIRECT is not set
+# CONFIG_IP_NF_TARGET_REJECT is not set
+# CONFIG_IP_NF_TARGET_SYNPROXY is not set
+# CONFIG_IP_NF_TARGET_TTL is not set
+# CONFIG_IP_PIMSM_V1 is not set
+# CONFIG_IP_PIMSM_V2 is not set
+# CONFIG_IP_PNP is not set
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+# CONFIG_IP_SCTP is not set
+# CONFIG_IP_SET is not set
+# CONFIG_IP_SET_HASH_IPMAC is not set
+# CONFIG_IP_VS is not set
+# CONFIG_IP_VS_MH is not set
+CONFIG_IP_VS_MH_TAB_INDEX=10
+# CONFIG_IP_VS_TWOS is not set
+# CONFIG_IRDA is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_IRQ_ALL_CPUS is not set
+# CONFIG_IRQ_DOMAIN_DEBUG is not set
+# CONFIG_IRQ_POLL is not set
+# CONFIG_IRQ_TIME_ACCOUNTING is not set
+# CONFIG_IR_GPIO_CIR is not set
+# CONFIG_IR_HIX5HD2 is not set
+# CONFIG_IR_IGORPLUGUSB is not set
+# CONFIG_IR_IGUANA is not set
+# CONFIG_IR_IMG is not set
+# CONFIG_IR_IMON is not set
+# CONFIG_IR_IMON_RAW is not set
+# CONFIG_IR_JVC_DECODER is not set
+# CONFIG_IR_LIRC_CODEC is not set
+# CONFIG_IR_MCEUSB is not set
+# CONFIG_IR_NEC_DECODER is not set
+# CONFIG_IR_RC5_DECODER is not set
+# CONFIG_IR_RC6_DECODER is not set
+# CONFIG_IR_REDRAT3 is not set
+# CONFIG_IR_SERIAL is not set
+# CONFIG_IR_SIR is not set
+# CONFIG_IR_SONY_DECODER is not set
+# CONFIG_IR_STREAMZAP is not set
+# CONFIG_IR_TOY is not set
+# CONFIG_IR_TTUSBIR is not set
+# CONFIG_ISA_BUS is not set
+# CONFIG_ISA_BUS_API is not set
+# CONFIG_ISCSI_BOOT_SYSFS is not set
+# CONFIG_ISCSI_TCP is not set
+CONFIG_ISDN=y
+# CONFIG_ISDN_AUDIO is not set
+# CONFIG_ISDN_CAPI is not set
+# CONFIG_ISDN_CAPI_CAPIDRV is not set
+# CONFIG_ISDN_DIVERSION is not set
+# CONFIG_ISDN_DRV_ACT2000 is not set
+# CONFIG_ISDN_DRV_GIGASET is not set
+# CONFIG_ISDN_DRV_HISAX is not set
+# CONFIG_ISDN_DRV_ICN is not set
+# CONFIG_ISDN_DRV_LOOP is not set
+# CONFIG_ISDN_DRV_PCBIT is not set
+# CONFIG_ISDN_DRV_SC is not set
+# CONFIG_ISDN_I4L is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_ISL29020 is not set
+# CONFIG_ISL29125 is not set
+# CONFIG_ISL29501 is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_ISS4xx is not set
+# CONFIG_ITG3200 is not set
+# CONFIG_IWL3945 is not set
+# CONFIG_IWLWIFI is not set
+# CONFIG_IXGB is not set
+# CONFIG_IXGBE is not set
+# CONFIG_IXGBEVF is not set
+# CONFIG_JAILHOUSE_GUEST is not set
+# CONFIG_JBD2_DEBUG is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS_POSIX_ACL is not set
+# CONFIG_JFFS2_FS_SECURITY is not set
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+CONFIG_JFFS2_FS_XATTR=y
+CONFIG_JFFS2_LZMA=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_ZLIB is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_SECURITY is not set
+# CONFIG_JFS_STATISTICS is not set
+# CONFIG_JME is not set
+CONFIG_JOLIET=y
+# CONFIG_JSA1212 is not set
+# CONFIG_JUMP_LABEL is not set
+# CONFIG_JZ4740_WDT is not set
+# CONFIG_JZ4770_PHY is not set
+# CONFIG_KALLSYMS is not set
+# CONFIG_KALLSYMS_ABSOLUTE_PERCPU is not set
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_KALLSYMS_BASE_RELATIVE=y
+# CONFIG_KALLSYMS_UNCOMPRESSED is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_KASAN is not set
+# CONFIG_KASAN_MODULE_TEST is not set
+CONFIG_KASAN_STACK=y
+# CONFIG_KCMP is not set
+# CONFIG_KCOV is not set
+CONFIG_KCOV_IRQ_AREA_SIZE=0x40000
+# CONFIG_KCSAN is not set
+# CONFIG_KERNEL_BZIP2 is not set
+# CONFIG_KERNEL_CAT is not set
+# CONFIG_KERNEL_GZIP is not set
+# CONFIG_KERNEL_LZ4 is not set
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_KERNEL_MODE_NEON=y
+CONFIG_KERNEL_XZ=y
+# CONFIG_KERNEL_ZSTD is not set
+CONFIG_KERNFS=y
+# CONFIG_KEXEC is not set
+# CONFIG_KEXEC_FILE is not set
+# CONFIG_KEXEC_SIG is not set
+# CONFIG_KEYBOARD_ADC is not set
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+# CONFIG_KEYBOARD_APPLESPI is not set
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_BCM is not set
+# CONFIG_KEYBOARD_CAP11XX is not set
+# CONFIG_KEYBOARD_DLINK_DIR685 is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_KEYBOARD_GPIO_POLLED is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+# CONFIG_KEYBOARD_LM8333 is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OMAP4 is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_PXA27x is not set
+# CONFIG_KEYBOARD_QT1050 is not set
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_SAMSUNG is not set
+# CONFIG_KEYBOARD_SH_KEYSC is not set
+# CONFIG_KEYBOARD_SNVS_PWRKEY is not set
+# CONFIG_KEYBOARD_STMPE is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_TCA8418 is not set
+# CONFIG_KEYBOARD_TEGRA is not set
+# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
+# CONFIG_KEYBOARD_TWL4030 is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYS is not set
+# CONFIG_KEYS_REQUEST_CACHE is not set
+# CONFIG_KEY_DH_OPERATIONS is not set
+# CONFIG_KFENCE is not set
+# CONFIG_KGDB is not set
+# CONFIG_KMEMCHECK is not set
+# CONFIG_KMX61 is not set
+# CONFIG_KPC2000 is not set
+# CONFIG_KPROBES is not set
+# CONFIG_KPROBES_SANITY_TEST is not set
+# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set
+# CONFIG_KPROBE_EVENT_GEN_TEST is not set
+# CONFIG_KS7010 is not set
+# CONFIG_KS8842 is not set
+# CONFIG_KS8851 is not set
+# CONFIG_KS8851_MLL is not set
+# CONFIG_KSM is not set
+# CONFIG_KSZ884X_PCI is not set
+# CONFIG_KUNIT is not set
+CONFIG_KUSER_HELPERS=y
+# CONFIG_KVM_AMD is not set
+# CONFIG_KVM_AMD_SEV is not set
+# CONFIG_KVM_GUEST is not set
+# CONFIG_KVM_INTEL is not set
+# CONFIG_KVM_WERROR is not set
+# CONFIG_KVM_XEN is not set
+# CONFIG_KXCJK1013 is not set
+# CONFIG_KXSD9 is not set
+# CONFIG_L2TP is not set
+# CONFIG_L2TP_ETH is not set
+# CONFIG_L2TP_IP is not set
+# CONFIG_L2TP_V3 is not set
+# CONFIG_LAN743X is not set
+# CONFIG_LANMEDIA is not set
+# CONFIG_LANTIQ is not set
+# CONFIG_LAPB is not set
+# CONFIG_LASAT is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+CONFIG_LBDAF=y
+# CONFIG_LCD_AMS369FG06 is not set
+# CONFIG_LCD_CLASS_DEVICE is not set
+# CONFIG_LCD_HX8357 is not set
+# CONFIG_LCD_ILI922X is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_L4F00242T03 is not set
+# CONFIG_LCD_LD9040 is not set
+# CONFIG_LCD_LMS283GF05 is not set
+# CONFIG_LCD_LMS501KF03 is not set
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_OTM3225A is not set
+# CONFIG_LCD_S6E63M0 is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+CONFIG_LDISC_AUTOLOAD=y
+# CONFIG_LDM_PARTITION is not set
+CONFIG_LD_DEAD_CODE_DATA_ELIMINATION=y
+# CONFIG_LD_HEAD_STUB_CATCH is not set
+# CONFIG_LEDS_AN30259A is not set
+# CONFIG_LEDS_APU is not set
+# CONFIG_LEDS_AW2013 is not set
+# CONFIG_LEDS_BCM6328 is not set
+# CONFIG_LEDS_BCM6358 is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_BLINKM is not set
+CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y
+CONFIG_LEDS_CLASS=y
+# CONFIG_LEDS_CLASS_FLASH is not set
+CONFIG_LEDS_CLASS_MULTICOLOR=y
+# CONFIG_LEDS_CR0014114 is not set
+# CONFIG_LEDS_DAC124S085 is not set
+# CONFIG_LEDS_EL15203000 is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_LEDS_INTEL_SS4200 is not set
+# CONFIG_LEDS_IS31FL319X is not set
+# CONFIG_LEDS_IS31FL32XX is not set
+# CONFIG_LEDS_LM3530 is not set
+# CONFIG_LEDS_LM3532 is not set
+# CONFIG_LEDS_LM355x is not set
+# CONFIG_LEDS_LM3642 is not set
+# CONFIG_LEDS_LM3692X is not set
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_LP3952 is not set
+# CONFIG_LEDS_LP50XX is not set
+# CONFIG_LEDS_LP5521 is not set
+# CONFIG_LEDS_LP5523 is not set
+# CONFIG_LEDS_LP5562 is not set
+# CONFIG_LEDS_LP55XX_COMMON is not set
+# CONFIG_LEDS_LP8501 is not set
+# CONFIG_LEDS_LP8860 is not set
+# CONFIG_LEDS_LT3593 is not set
+# CONFIG_LEDS_MLXCPLD is not set
+# CONFIG_LEDS_MLXREG is not set
+# CONFIG_LEDS_NIC78BX is not set
+# CONFIG_LEDS_NS2 is not set
+# CONFIG_LEDS_OT200 is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_PCA963X is not set
+# CONFIG_LEDS_PWM is not set
+# CONFIG_LEDS_REGULATOR is not set
+# CONFIG_LEDS_SPI_BYTE is not set
+# CONFIG_LEDS_SYSCON is not set
+# CONFIG_LEDS_TCA6507 is not set
+# CONFIG_LEDS_TI_LMU_COMMON is not set
+# CONFIG_LEDS_TLC591XX is not set
+CONFIG_LEDS_TRIGGERS=y
+# CONFIG_LEDS_TRIGGER_ACTIVITY is not set
+# CONFIG_LEDS_TRIGGER_AUDIO is not set
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+# CONFIG_LEDS_TRIGGER_CAMERA is not set
+# CONFIG_LEDS_TRIGGER_CPU is not set
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+# CONFIG_LEDS_TRIGGER_DISK is not set
+# CONFIG_LEDS_TRIGGER_GPIO is not set
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+# CONFIG_LEDS_TRIGGER_MTD is not set
+CONFIG_LEDS_TRIGGER_NETDEV=y
+# CONFIG_LEDS_TRIGGER_ONESHOT is not set
+# CONFIG_LEDS_TRIGGER_PANIC is not set
+# CONFIG_LEDS_TRIGGER_PATTERN is not set
+CONFIG_LEDS_TRIGGER_TIMER=y
+# CONFIG_LEDS_TRIGGER_TRANSIENT is not set
+# CONFIG_LEDS_TRIGGER_TTY is not set
+# CONFIG_LEDS_TURRIS_OMNIA is not set
+# CONFIG_LEDS_USER is not set
+# CONFIG_LED_TRIGGER_PHY is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_LGUEST is not set
+# CONFIG_LIB80211 is not set
+# CONFIG_LIB80211_CRYPT_CCMP is not set
+# CONFIG_LIB80211_CRYPT_TKIP is not set
+# CONFIG_LIB80211_CRYPT_WEP is not set
+# CONFIG_LIB80211_DEBUG is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_LIBERTAS_THINFIRM is not set
+# CONFIG_LIBERTAS_USB is not set
+# CONFIG_LIBFC is not set
+# CONFIG_LIBFCOE is not set
+# CONFIG_LIBIPW_DEBUG is not set
+# CONFIG_LIBNVDIMM is not set
+CONFIG_LIB_MEMNEQ=y
+# CONFIG_LIDAR_LITE_V2 is not set
+CONFIG_LINEAR_RANGES=y
+# CONFIG_LIQUIDIO is not set
+# CONFIG_LIQUIDIO_VF is not set
+# CONFIG_LIRC is not set
+# CONFIG_LIS3L02DQ is not set
+# CONFIG_LITEX_LITEETH is not set
+# CONFIG_LITEX_SOC_CONTROLLER is not set
+# CONFIG_LIVEPATCH is not set
+# CONFIG_LKDTM is not set
+CONFIG_LLC=y
+# CONFIG_LLC2 is not set
+# CONFIG_LMK04832 is not set
+# CONFIG_LMP91000 is not set
+# CONFIG_LNET is not set
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_LOCKD is not set
+CONFIG_LOCKDEP_BITS=15
+CONFIG_LOCKDEP_CHAINS_BITS=16
+CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS=12
+CONFIG_LOCKDEP_STACK_TRACE_BITS=19
+CONFIG_LOCKDEP_STACK_TRACE_HASH_BITS=14
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_LOCKD_V4=y
+# CONFIG_LOCKUP_DETECTOR is not set
+# CONFIG_LOCK_EVENT_COUNTS is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_LOCK_TORTURE_TEST is not set
+# CONFIG_LOGFS is not set
+# CONFIG_LOGIG940_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIWHEELS_FF is not set
+# CONFIG_LOGO is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=12
+# CONFIG_LOONGSON_MC146818 is not set
+# CONFIG_LPC_ICH is not set
+# CONFIG_LPC_SCH is not set
+# CONFIG_LP_CONSOLE is not set
+CONFIG_LRU_GEN=y
+CONFIG_LRU_GEN_ENABLED=y
+# CONFIG_LRU_GEN_STATS is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity"
+CONFIG_LSM_MMAP_MIN_ADDR=65536
+# CONFIG_LTC1660 is not set
+# CONFIG_LTC2471 is not set
+# CONFIG_LTC2485 is not set
+# CONFIG_LTC2496 is not set
+# CONFIG_LTC2497 is not set
+# CONFIG_LTC2632 is not set
+# CONFIG_LTC2983 is not set
+# CONFIG_LTE_GDM724X is not set
+CONFIG_LTO_NONE=y
+# CONFIG_LTPC is not set
+# CONFIG_LTR501 is not set
+# CONFIG_LUSTRE_FS is not set
+# CONFIG_LV0104CS is not set
+# CONFIG_LWTUNNEL is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_LZ4HC_COMPRESS is not set
+# CONFIG_LZ4_COMPRESS is not set
+# CONFIG_LZ4_DECOMPRESS is not set
+CONFIG_LZMA_COMPRESS=y
+CONFIG_LZMA_DECOMPRESS=y
+# CONFIG_LZO_COMPRESS is not set
+# CONFIG_LZO_DECOMPRESS is not set
+# CONFIG_M62332 is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_MAC80211_MESSAGE_TRACING is not set
+CONFIG_MAC80211_STA_HASH_MAX_SIZE=0
+# CONFIG_MACB is not set
+# CONFIG_MACH_ASM9260 is not set
+# CONFIG_MACH_DECSTATION is not set
+# CONFIG_MACH_INGENIC is not set
+# CONFIG_MACH_INGENIC_SOC is not set
+# CONFIG_MACH_JAZZ is not set
+# CONFIG_MACH_JZ4740 is not set
+# CONFIG_MACH_LOONGSON2EF is not set
+# CONFIG_MACH_LOONGSON32 is not set
+# CONFIG_MACH_LOONGSON64 is not set
+# CONFIG_MACH_NINTENDO64 is not set
+# CONFIG_MACH_PIC32 is not set
+# CONFIG_MACH_PISTACHIO is not set
+# CONFIG_MACH_REALTEK_RTL is not set
+# CONFIG_MACH_TX39XX is not set
+# CONFIG_MACH_TX49XX is not set
+# CONFIG_MACH_VR41XX is not set
+# CONFIG_MACH_XILFPGA is not set
+# CONFIG_MACINTOSH_DRIVERS is not set
+# CONFIG_MACSEC is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_MACVTAP is not set
+# CONFIG_MAC_EMUMOUSEBTN is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MAG3110 is not set
+# CONFIG_MAGIC_SYSRQ is not set
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
+# CONFIG_MAGIC_SYSRQ_SERIAL is not set
+CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
+# CONFIG_MAILBOX is not set
+# CONFIG_MANAGER_SBS is not set
+# CONFIG_MANDATORY_FILE_LOCKING is not set
+# CONFIG_MANGLE_BOOTARGS is not set
+# CONFIG_MARVELL_10G_PHY is not set
+# CONFIG_MARVELL_88X2222_PHY is not set
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_MAX1027 is not set
+# CONFIG_MAX11100 is not set
+# CONFIG_MAX1118 is not set
+# CONFIG_MAX1241 is not set
+# CONFIG_MAX1363 is not set
+# CONFIG_MAX30100 is not set
+# CONFIG_MAX30102 is not set
+# CONFIG_MAX31856 is not set
+# CONFIG_MAX44000 is not set
+# CONFIG_MAX44009 is not set
+# CONFIG_MAX517 is not set
+# CONFIG_MAX5432 is not set
+# CONFIG_MAX5481 is not set
+# CONFIG_MAX5487 is not set
+# CONFIG_MAX5821 is not set
+# CONFIG_MAX63XX_WATCHDOG is not set
+# CONFIG_MAX9611 is not set
+# CONFIG_MAXIM_THERMOCOUPLE is not set
+# CONFIG_MAXLINEAR_GPHY is not set
+CONFIG_MAY_USE_DEVLINK=y
+# CONFIG_MB1232 is not set
+# CONFIG_MC3230 is not set
+# CONFIG_MCB is not set
+# CONFIG_MCP320X is not set
+# CONFIG_MCP3422 is not set
+# CONFIG_MCP3911 is not set
+# CONFIG_MCP4018 is not set
+# CONFIG_MCP41010 is not set
+# CONFIG_MCP4131 is not set
+# CONFIG_MCP4531 is not set
+# CONFIG_MCP4725 is not set
+# CONFIG_MCP4922 is not set
+# CONFIG_MCPM is not set
+# CONFIG_MCTP is not set
+# CONFIG_MD is not set
+# CONFIG_MDIO_BCM_UNIMAC is not set
+# CONFIG_MDIO_BITBANG is not set
+# CONFIG_MDIO_BUS_MUX_GPIO is not set
+# CONFIG_MDIO_BUS_MUX_MMIOREG is not set
+# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set
+# CONFIG_MDIO_DEVICE is not set
+# CONFIG_MDIO_DEVRES is not set
+# CONFIG_MDIO_HISI_FEMAC is not set
+# CONFIG_MDIO_IPQ4019 is not set
+# CONFIG_MDIO_IPQ8064 is not set
+# CONFIG_MDIO_MSCC_MIIM is not set
+# CONFIG_MDIO_MVUSB is not set
+# CONFIG_MDIO_OCTEON is not set
+# CONFIG_MDIO_THUNDER is not set
+# CONFIG_MDIO_XPCS is not set
+# CONFIG_MDM_GCC_9607 is not set
+# CONFIG_MD_FAULTY is not set
+# CONFIG_MEDIATEK_GE_PHY is not set
+# CONFIG_MEDIATEK_MT6577_AUXADC is not set
+# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set
+# CONFIG_MEDIA_ATTACH is not set
+# CONFIG_MEDIA_CAMERA_SUPPORT is not set
+# CONFIG_MEDIA_CEC_SUPPORT is not set
+# CONFIG_MEDIA_CONTROLLER is not set
+# CONFIG_MEDIA_CONTROLLER_DVB is not set
+# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set
+# CONFIG_MEDIA_PCI_SUPPORT is not set
+# CONFIG_MEDIA_PLATFORM_SUPPORT is not set
+# CONFIG_MEDIA_RADIO_SUPPORT is not set
+# CONFIG_MEDIA_RC_SUPPORT is not set
+# CONFIG_MEDIA_SDR_SUPPORT is not set
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+# CONFIG_MEDIA_SUPPORT is not set
+# CONFIG_MEDIA_SUPPORT_FILTER is not set
+# CONFIG_MEDIA_TEST_SUPPORT is not set
+# CONFIG_MEDIA_TUNER_E4000 is not set
+# CONFIG_MEDIA_TUNER_FC0011 is not set
+# CONFIG_MEDIA_TUNER_FC0012 is not set
+# CONFIG_MEDIA_TUNER_FC0013 is not set
+# CONFIG_MEDIA_TUNER_FC2580 is not set
+# CONFIG_MEDIA_TUNER_IT913X is not set
+# CONFIG_MEDIA_TUNER_M88RS6000T is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MSI001 is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MXL301RF is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_QM1D1B0004 is not set
+# CONFIG_MEDIA_TUNER_QM1D1C0042 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_R820T is not set
+# CONFIG_MEDIA_TUNER_SI2157 is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18250 is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_TUA9001 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_USB_SUPPORT is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_MELLANOX_PLATFORM is not set
+CONFIG_MEMBARRIER=y
+# CONFIG_MEMORY is not set
+# CONFIG_MEMORY_FAILURE is not set
+# CONFIG_MEMORY_HOTPLUG is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_MEMTEST is not set
+# CONFIG_MEN_A21_WDT is not set
+# CONFIG_MESON_SM is not set
+CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
+# CONFIG_MFD_88PM800 is not set
+# CONFIG_MFD_88PM805 is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_AAT2870_CORE is not set
+# CONFIG_MFD_AC100 is not set
+# CONFIG_MFD_ACT8945A is not set
+# CONFIG_MFD_ARIZONA_I2C is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_AS3711 is not set
+# CONFIG_MFD_AS3722 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_MFD_ATC260X_I2C is not set
+# CONFIG_MFD_ATMEL_FLEXCOM is not set
+# CONFIG_MFD_ATMEL_HLCDC is not set
+# CONFIG_MFD_AXP20X is not set
+# CONFIG_MFD_AXP20X_I2C is not set
+# CONFIG_MFD_BCM590XX is not set
+# CONFIG_MFD_BD9571MWV is not set
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_CPCAP is not set
+# CONFIG_MFD_CROS_EC is not set
+# CONFIG_MFD_CS5535 is not set
+# CONFIG_MFD_DA9052_I2C is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_DA9055 is not set
+# CONFIG_MFD_DA9062 is not set
+# CONFIG_MFD_DA9063 is not set
+# CONFIG_MFD_DA9150 is not set
+# CONFIG_MFD_DLN2 is not set
+# CONFIG_MFD_EXYNOS_LPASS is not set
+# CONFIG_MFD_GATEWORKS_GSC is not set
+# CONFIG_MFD_HI6421_PMIC is not set
+# CONFIG_MFD_INTEL_M10_BMC is not set
+# CONFIG_MFD_INTEL_PMT is not set
+# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set
+# CONFIG_MFD_IQS62X is not set
+# CONFIG_MFD_JANZ_CMODIO is not set
+# CONFIG_MFD_KEMPLD is not set
+# CONFIG_MFD_LM3533 is not set
+# CONFIG_MFD_LOCHNAGAR is not set
+# CONFIG_MFD_LP3943 is not set
+# CONFIG_MFD_LP8788 is not set
+# CONFIG_MFD_MADERA is not set
+# CONFIG_MFD_MAX14577 is not set
+# CONFIG_MFD_MAX77620 is not set
+# CONFIG_MFD_MAX77650 is not set
+# CONFIG_MFD_MAX77686 is not set
+# CONFIG_MFD_MAX77693 is not set
+# CONFIG_MFD_MAX77843 is not set
+# CONFIG_MFD_MAX8907 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_MC13XXX is not set
+# CONFIG_MFD_MC13XXX_I2C is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_MENF21BMC is not set
+# CONFIG_MFD_MP2629 is not set
+# CONFIG_MFD_MT6360 is not set
+# CONFIG_MFD_MT6397 is not set
+# CONFIG_MFD_NTXEC is not set
+# CONFIG_MFD_OMAP_USB_HOST is not set
+# CONFIG_MFD_PALMAS is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_PM8921_CORE is not set
+# CONFIG_MFD_PM8XXX is not set
+# CONFIG_MFD_QCOM_PM8008 is not set
+# CONFIG_MFD_RC5T583 is not set
+# CONFIG_MFD_RDC321X is not set
+# CONFIG_MFD_RETU is not set
+# CONFIG_MFD_RK808 is not set
+# CONFIG_MFD_RN5T618 is not set
+# CONFIG_MFD_ROHM_BD70528 is not set
+# CONFIG_MFD_ROHM_BD71828 is not set
+# CONFIG_MFD_ROHM_BD718XX is not set
+# CONFIG_MFD_ROHM_BD957XMUF is not set
+# CONFIG_MFD_RSMU_I2C is not set
+# CONFIG_MFD_RSMU_SPI is not set
+# CONFIG_MFD_RT4831 is not set
+# CONFIG_MFD_RT5033 is not set
+# CONFIG_MFD_RTSX_PCI is not set
+# CONFIG_MFD_RTSX_USB is not set
+# CONFIG_MFD_SEC_CORE is not set
+# CONFIG_MFD_SI476X_CORE is not set
+# CONFIG_MFD_SKY81452 is not set
+# CONFIG_MFD_SL28CPLD is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_SMSC is not set
+# CONFIG_MFD_STMFX is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_STPMIC1 is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_MFD_TIMBERDALE is not set
+# CONFIG_MFD_TI_AM335X_TSCADC is not set
+# CONFIG_MFD_TI_LMU is not set
+# CONFIG_MFD_TI_LP873X is not set
+# CONFIG_MFD_TI_LP87565 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_TPS65086 is not set
+# CONFIG_MFD_TPS65090 is not set
+# CONFIG_MFD_TPS65217 is not set
+# CONFIG_MFD_TPS65218 is not set
+# CONFIG_MFD_TPS6586X is not set
+# CONFIG_MFD_TPS65910 is not set
+# CONFIG_MFD_TPS65912 is not set
+# CONFIG_MFD_TPS65912_I2C is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_TPS68470 is not set
+# CONFIG_MFD_TPS80031 is not set
+# CONFIG_MFD_TQMX86 is not set
+# CONFIG_MFD_VIPERBOARD is not set
+# CONFIG_MFD_VX855 is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_MFD_WM831X is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_MHI_BUS is not set
+# CONFIG_MHI_BUS_DEBUG is not set
+# CONFIG_MHI_BUS_PCI_GENERIC is not set
+# CONFIG_MHI_NET is not set
+# CONFIG_MHI_WWAN_CTRL is not set
+# CONFIG_MHI_WWAN_MBIM is not set
+# CONFIG_MICREL_KS8995MA is not set
+# CONFIG_MICREL_PHY is not set
+# CONFIG_MICROCHIP_KSZ is not set
+# CONFIG_MICROCHIP_PHY is not set
+# CONFIG_MICROCHIP_PIT64B is not set
+# CONFIG_MICROCHIP_T1_PHY is not set
+# CONFIG_MICROSEMI_PHY is not set
+# CONFIG_MIGRATION is not set
+CONFIG_MII=y
+# CONFIG_MIKROTIK is not set
+# CONFIG_MIKROTIK_RB532 is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_MINIX_FS_NATIVE_ENDIAN is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_MIPS32_N32 is not set
+# CONFIG_MIPS32_O32 is not set
+# CONFIG_MIPS_ALCHEMY is not set
+# CONFIG_MIPS_CDMM is not set
+# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set
+# CONFIG_MIPS_CMDLINE_FROM_DTB is not set
+# CONFIG_MIPS_CMP is not set
+# CONFIG_MIPS_COBALT is not set
+# CONFIG_MIPS_CPS is not set
+# CONFIG_MIPS_ELF_APPENDED_DTB is not set
+# CONFIG_MIPS_FPU_EMULATOR is not set
+# CONFIG_MIPS_FP_SUPPORT is not set
+# CONFIG_MIPS_GENERIC is not set
+# CONFIG_MIPS_GENERIC_KERNEL is not set
+# CONFIG_MIPS_MALTA is not set
+# CONFIG_MIPS_O32_FP64_SUPPORT is not set
+# CONFIG_MIPS_PARAVIRT is not set
+# CONFIG_MIPS_PLATFORM_DEVICES is not set
+# CONFIG_MIPS_RAW_APPENDED_DTB is not set
+# CONFIG_MIPS_SEAD3 is not set
+# CONFIG_MIPS_VA_BITS_48 is not set
+# CONFIG_MIPS_VPE_LOADER is not set
+# CONFIG_MISC_ALCOR_PCI is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_MISC_RTSX_PCI is not set
+# CONFIG_MISC_RTSX_USB is not set
+# CONFIG_MISDN is not set
+# CONFIG_MISDN_AVMFRITZ is not set
+# CONFIG_MISDN_HFCPCI is not set
+# CONFIG_MISDN_HFCUSB is not set
+# CONFIG_MISDN_INFINEON is not set
+# CONFIG_MISDN_NETJET is not set
+# CONFIG_MISDN_SPEEDFAX is not set
+# CONFIG_MISDN_W6692 is not set
+CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY=y
+# CONFIG_MKISS is not set
+# CONFIG_MLX4_CORE is not set
+# CONFIG_MLX4_EN is not set
+# CONFIG_MLX5_CORE is not set
+# CONFIG_MLX5_SF is not set
+# CONFIG_MLX90614 is not set
+# CONFIG_MLX90632 is not set
+# CONFIG_MLXFW is not set
+# CONFIG_MLXSW_CORE is not set
+# CONFIG_MLX_CPLD_PLATFORM is not set
+# CONFIG_MLX_PLATFORM is not set
+# CONFIG_MMA7455_I2C is not set
+# CONFIG_MMA7455_SPI is not set
+# CONFIG_MMA7660 is not set
+# CONFIG_MMA8452 is not set
+# CONFIG_MMA9551 is not set
+# CONFIG_MMA9553 is not set
+# CONFIG_MMC is not set
+# CONFIG_MMC35240 is not set
+# CONFIG_MMC_ARMMMCI is not set
+# CONFIG_MMC_AU1X is not set
+# CONFIG_MMC_BLOCK is not set
+CONFIG_MMC_BLOCK_MINORS=8
+# CONFIG_MMC_CAVIUM_THUNDERX is not set
+# CONFIG_MMC_CB710 is not set
+# CONFIG_MMC_CQHCI is not set
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_DW is not set
+# CONFIG_MMC_HSQ is not set
+# CONFIG_MMC_JZ4740 is not set
+# CONFIG_MMC_MTK is not set
+# CONFIG_MMC_MVSDIO is not set
+# CONFIG_MMC_S3C is not set
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SDHCI_ACPI is not set
+# CONFIG_MMC_SDHCI_AM654 is not set
+# CONFIG_MMC_SDHCI_BCM_KONA is not set
+# CONFIG_MMC_SDHCI_BRCMSTB is not set
+# CONFIG_MMC_SDHCI_CADENCE is not set
+# CONFIG_MMC_SDHCI_F_SDH30 is not set
+# CONFIG_MMC_SDHCI_IPROC is not set
+# CONFIG_MMC_SDHCI_MILBEAUT is not set
+# CONFIG_MMC_SDHCI_MSM is not set
+# CONFIG_MMC_SDHCI_OF_ARASAN is not set
+# CONFIG_MMC_SDHCI_OF_ASPEED is not set
+# CONFIG_MMC_SDHCI_OF_AT91 is not set
+# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set
+# CONFIG_MMC_SDHCI_OF_ESDHC is not set
+# CONFIG_MMC_SDHCI_OF_HLWD is not set
+# CONFIG_MMC_SDHCI_OMAP is not set
+# CONFIG_MMC_SDHCI_PXAV2 is not set
+# CONFIG_MMC_SDHCI_PXAV3 is not set
+# CONFIG_MMC_SDHCI_S3C is not set
+# CONFIG_MMC_SDHCI_XENON is not set
+# CONFIG_MMC_SDRICOH_CS is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_STM32_SDMMC is not set
+# CONFIG_MMC_TEST is not set
+# CONFIG_MMC_TIFM_SD is not set
+# CONFIG_MMC_TOSHIBA_PCI is not set
+# CONFIG_MMC_USDHI6ROL0 is not set
+# CONFIG_MMC_USHC is not set
+# CONFIG_MMC_VIA_SDMMC is not set
+# CONFIG_MMC_VUB300 is not set
+# CONFIG_MMIOTRACE is not set
+CONFIG_MMU=y
+CONFIG_MMU_GATHER_RCU_TABLE_FREE=y
+CONFIG_MMU_GATHER_TABLE_FREE=y
+CONFIG_MODPROBE_PATH="/sbin/modprobe"
+CONFIG_MODULES=y
+# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set
+# CONFIG_MODULE_COMPRESS is not set
+# CONFIG_MODULE_COMPRESS_GZIP is not set
+CONFIG_MODULE_COMPRESS_NONE=y
+# CONFIG_MODULE_COMPRESS_XZ is not set
+# CONFIG_MODULE_COMPRESS_ZSTD is not set
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODULE_SIG is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_MODULE_STRIPPED=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MOST is not set
+# CONFIG_MOTORCOMM_PHY is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_ELAN_I2C is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_MOUSE_INPORT is not set
+# CONFIG_MOUSE_LOGIBM is not set
+# CONFIG_MOUSE_PC110PAD is not set
+# CONFIG_MOUSE_PS2_FOCALTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_MOUSE_SYNAPTICS_USB is not set
+# CONFIG_MOXTET is not set
+# CONFIG_MPL115 is not set
+# CONFIG_MPL115_I2C is not set
+# CONFIG_MPL115_SPI is not set
+# CONFIG_MPL3115 is not set
+# CONFIG_MPLS is not set
+# CONFIG_MPLS_IPTUNNEL is not set
+# CONFIG_MPLS_ROUTING is not set
+# CONFIG_MPTCP is not set
+# CONFIG_MPU3050_I2C is not set
+# CONFIG_MQ_IOSCHED_DEADLINE is not set
+# CONFIG_MQ_IOSCHED_KYBER is not set
+# CONFIG_MS5611 is not set
+# CONFIG_MS5637 is not set
+# CONFIG_MSCC_OCELOT_SWITCH is not set
+# CONFIG_MSDOS_FS is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_MSI_BITMAP_SELFTEST is not set
+# CONFIG_MSI_LAPTOP is not set
+# CONFIG_MSM_GCC_8953 is not set
+# CONFIG_MSM_MMCC_8994 is not set
+# CONFIG_MST_IRQ is not set
+CONFIG_MTD=y
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_MTD_BLOCK2MTD is not set
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_CMDLINE_PARTS is not set
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_DOCG3 is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_GPIO_ADDR is not set
+# CONFIG_MTD_HYPERBUS is not set
+# CONFIG_MTD_IMPA7 is not set
+# CONFIG_MTD_INTEL_VR_NOR is not set
+# CONFIG_MTD_JEDECPROBE is not set
+# CONFIG_MTD_LATCH_ADDR is not set
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_LPDDR2_NVM is not set
+# CONFIG_MTD_M25P80 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MCHP23K256 is not set
+# CONFIG_MTD_MCHP48L640 is not set
+# CONFIG_MTD_MT81xx_NOR is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_MYLOADER_PARTS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_NAND_AMS_DELTA is not set
+# CONFIG_MTD_NAND_AR934X is not set
+# CONFIG_MTD_NAND_AR934X_HW_ECC is not set
+# CONFIG_MTD_NAND_ARASAN is not set
+# CONFIG_MTD_NAND_ATMEL is not set
+# CONFIG_MTD_NAND_AU1550 is not set
+# CONFIG_MTD_NAND_BCH is not set
+# CONFIG_MTD_NAND_BF5XX is not set
+# CONFIG_MTD_NAND_BRCMNAND is not set
+# CONFIG_MTD_NAND_CADENCE is not set
+# CONFIG_MTD_NAND_CAFE is not set
+# CONFIG_MTD_NAND_CM_X270 is not set
+# CONFIG_MTD_NAND_CS553X is not set
+# CONFIG_MTD_NAND_DAVINCI is not set
+# CONFIG_MTD_NAND_DENALI is not set
+# CONFIG_MTD_NAND_DENALI_DT is not set
+# CONFIG_MTD_NAND_DENALI_PCI is not set
+CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR=0xff108018
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_DOCG4 is not set
+# CONFIG_MTD_NAND_ECC is not set
+# CONFIG_MTD_NAND_ECC_BCH is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_ECC_SW_BCH is not set
+# CONFIG_MTD_NAND_ECC_SW_HAMMING is not set
+# CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC is not set
+# CONFIG_MTD_NAND_FSL_ELBC is not set
+# CONFIG_MTD_NAND_FSL_IFC is not set
+# CONFIG_MTD_NAND_FSL_UPM is not set
+# CONFIG_MTD_NAND_FSMC is not set
+# CONFIG_MTD_NAND_GPIO is not set
+# CONFIG_MTD_NAND_GPMI_NAND is not set
+# CONFIG_MTD_NAND_HISI504 is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_INTEL_LGM is not set
+# CONFIG_MTD_NAND_JZ4740 is not set
+# CONFIG_MTD_NAND_MPC5121_NFC is not set
+# CONFIG_MTD_NAND_MTK is not set
+# CONFIG_MTD_NAND_MTK_BMT is not set
+# CONFIG_MTD_NAND_MXC is not set
+# CONFIG_MTD_NAND_MXIC is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_NDFC is not set
+# CONFIG_MTD_NAND_NUC900 is not set
+# CONFIG_MTD_NAND_OMAP2 is not set
+# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set
+# CONFIG_MTD_NAND_ORION is not set
+# CONFIG_MTD_NAND_PASEMI is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_NAND_PXA3xx is not set
+# CONFIG_MTD_NAND_RB4XX is not set
+# CONFIG_MTD_NAND_RB750 is not set
+# CONFIG_MTD_NAND_RB91X is not set
+# CONFIG_MTD_NAND_RICOH is not set
+# CONFIG_MTD_NAND_S3C2410 is not set
+# CONFIG_MTD_NAND_SHARPSL is not set
+# CONFIG_MTD_NAND_SH_FLCTL is not set
+# CONFIG_MTD_NAND_SOCRATES is not set
+# CONFIG_MTD_NAND_TMIO is not set
+# CONFIG_MTD_NAND_TXX9NDFMC is not set
+CONFIG_MTD_OF_PARTS=y
+# CONFIG_MTD_ONENAND is not set
+# CONFIG_MTD_OOPS is not set
+# CONFIG_MTD_OTP is not set
+# CONFIG_MTD_PARSER_TRX is not set
+# CONFIG_MTD_PARTITIONED_MASTER is not set
+# CONFIG_MTD_PCI is not set
+# CONFIG_MTD_PCMCIA is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_PHYSMAP_GEMINI is not set
+# CONFIG_MTD_PHYSMAP_GPIO_ADDR is not set
+# CONFIG_MTD_PHYSMAP_IXP4XX is not set
+CONFIG_MTD_PHYSMAP_OF=y
+# CONFIG_MTD_PHYSMAP_OF_GEMINI is not set
+# CONFIG_MTD_PHYSMAP_OF_VERSATILE is not set
+# CONFIG_MTD_PHYSMAP_VERSATILE is not set
+# CONFIG_MTD_PLATRAM is not set
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_RAW_NAND is not set
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_ROM is not set
+CONFIG_MTD_ROOTFS_ROOT_DEV=y
+# CONFIG_MTD_ROUTERBOOT_PARTS is not set
+# CONFIG_MTD_SERCOMM_PARTS is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_SM_COMMON is not set
+# CONFIG_MTD_SPINAND_MT29F is not set
+# CONFIG_MTD_SPI_NAND is not set
+# CONFIG_MTD_SPI_NOR is not set
+# CONFIG_MTD_SPI_NOR_SWP_DISABLE is not set
+CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE=y
+# CONFIG_MTD_SPI_NOR_SWP_KEEP is not set
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+# CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE is not set
+CONFIG_MTD_SPLIT=y
+# CONFIG_MTD_SPLIT_BCM63XX_FW is not set
+# CONFIG_MTD_SPLIT_BCM_WFI_FW is not set
+# CONFIG_MTD_SPLIT_BRNIMAGE_FW is not set
+# CONFIG_MTD_SPLIT_ELF_FW is not set
+# CONFIG_MTD_SPLIT_EVA_FW is not set
+# CONFIG_MTD_SPLIT_FIRMWARE is not set
+CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware"
+# CONFIG_MTD_SPLIT_FIT_FW is not set
+# CONFIG_MTD_SPLIT_H3C_VFS is not set
+# CONFIG_MTD_SPLIT_JIMAGE_FW is not set
+# CONFIG_MTD_SPLIT_LZMA_FW is not set
+# CONFIG_MTD_SPLIT_MINOR_FW is not set
+# CONFIG_MTD_SPLIT_SEAMA_FW is not set
+CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y
+CONFIG_MTD_SPLIT_SUPPORT=y
+# CONFIG_MTD_SPLIT_TPLINK_FW is not set
+# CONFIG_MTD_SPLIT_TRX_FW is not set
+# CONFIG_MTD_SPLIT_UIMAGE_FW is not set
+# CONFIG_MTD_SPLIT_WRGG_FW is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_MTD_SWAP is not set
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_UBI is not set
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+# CONFIG_MTD_UIMAGE_SPLIT is not set
+# CONFIG_MTD_VIRT_CONCAT is not set
+# CONFIG_MTK_DEVAPC is not set
+# CONFIG_MTK_MMC is not set
+# CONFIG_MTK_MMSYS is not set
+# CONFIG_MTK_THERMAL is not set
+# CONFIG_MULTIPLEXER is not set
+CONFIG_MULTIUSER=y
+# CONFIG_MUTEX_SPIN_ON_OWNER is not set
+# CONFIG_MUX_ADG792A is not set
+# CONFIG_MUX_ADGS1408 is not set
+# CONFIG_MUX_GPIO is not set
+# CONFIG_MUX_MMIO is not set
+# CONFIG_MV643XX_ETH is not set
+# CONFIG_MVMDIO is not set
+# CONFIG_MVNETA_BM is not set
+# CONFIG_MVSW61XX_PHY is not set
+# CONFIG_MV_XOR_V2 is not set
+# CONFIG_MWAVE is not set
+# CONFIG_MWL8K is not set
+# CONFIG_MXC4005 is not set
+# CONFIG_MXC6255 is not set
+# CONFIG_MYRI10GE is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NAU7802 is not set
+# CONFIG_NBPFAXI_DMA is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NE2000 is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_NEC_MARKEINS is not set
+CONFIG_NET=y
+# CONFIG_NETCONSOLE is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVSIM is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_NETFILTER_ADVANCED is not set
+# CONFIG_NETFILTER_DEBUG is not set
+# CONFIG_NETFILTER_INGRESS is not set
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NETFILTER_NETLINK_ACCT is not set
+# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set
+# CONFIG_NETFILTER_NETLINK_HOOK is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NETFILTER_NETLINK_OSF is not set
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_XTABLES is not set
+# CONFIG_NETFILTER_XTABLES_COMPAT is not set
+# CONFIG_NETFILTER_XT_CONNMARK is not set
+# CONFIG_NETFILTER_XT_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_BPF is not set
+# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set
+# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNTRACK is not set
+# CONFIG_NETFILTER_XT_MATCH_CPU is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ECN is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_HELPER is not set
+# CONFIG_NETFILTER_XT_MATCH_HL is not set
+# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set
+# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
+# CONFIG_NETFILTER_XT_MATCH_L2TP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set
+# CONFIG_NETFILTER_XT_MATCH_OSF is not set
+# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
+# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_RECENT is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set
+# CONFIG_NETFILTER_XT_MATCH_STATE is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set
+# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_CT is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_HL is not set
+# CONFIG_NETFILTER_XT_TARGET_HMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set
+# CONFIG_NETFILTER_XT_TARGET_LED is not set
+# CONFIG_NETFILTER_XT_TARGET_LOG is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+# CONFIG_NETFILTER_XT_TARGET_REDIRECT is not set
+# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+# CONFIG_NETFILTER_XT_TARGET_TEE is not set
+# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETLABEL is not set
+# CONFIG_NETLINK_DIAG is not set
+# CONFIG_NETLINK_MMAP is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NETROM is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETXEN_NIC is not set
+# CONFIG_NET_9P is not set
+# CONFIG_NET_ACT_BPF is not set
+# CONFIG_NET_ACT_CSUM is not set
+# CONFIG_NET_ACT_CT is not set
+# CONFIG_NET_ACT_GACT is not set
+# CONFIG_NET_ACT_GATE is not set
+# CONFIG_NET_ACT_IFE is not set
+# CONFIG_NET_ACT_IPT is not set
+# CONFIG_NET_ACT_MIRRED is not set
+# CONFIG_NET_ACT_MPLS is not set
+# CONFIG_NET_ACT_NAT is not set
+# CONFIG_NET_ACT_PEDIT is not set
+# CONFIG_NET_ACT_POLICE is not set
+# CONFIG_NET_ACT_SAMPLE is not set
+# CONFIG_NET_ACT_SIMP is not set
+# CONFIG_NET_ACT_SKBEDIT is not set
+# CONFIG_NET_ACT_SKBMOD is not set
+# CONFIG_NET_ACT_TUNNEL_KEY is not set
+# CONFIG_NET_ACT_VLAN is not set
+CONFIG_NET_CADENCE=y
+# CONFIG_NET_CALXEDA_XGMAC is not set
+CONFIG_NET_CLS=y
+# CONFIG_NET_CLS_ACT is not set
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_BPF is not set
+# CONFIG_NET_CLS_FLOW is not set
+# CONFIG_NET_CLS_FLOWER is not set
+# CONFIG_NET_CLS_FW is not set
+CONFIG_NET_CLS_IND=y
+# CONFIG_NET_CLS_MATCHALL is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_U32 is not set
+CONFIG_NET_CORE=y
+# CONFIG_NET_DEVLINK is not set
+# CONFIG_NET_DROP_MONITOR is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_NET_DSA_AR9331 is not set
+# CONFIG_NET_DSA_BCM_SF2 is not set
+# CONFIG_NET_DSA_LANTIQ_GSWIP is not set
+# CONFIG_NET_DSA_LEGACY is not set
+# CONFIG_NET_DSA_LOOP is not set
+# CONFIG_NET_DSA_MICROCHIP_KSZ8795 is not set
+# CONFIG_NET_DSA_MICROCHIP_KSZ9477 is not set
+# CONFIG_NET_DSA_MSCC_FELIX is not set
+# CONFIG_NET_DSA_MSCC_SEVILLE is not set
+# CONFIG_NET_DSA_MT7530 is not set
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6123_61_65 is not set
+# CONFIG_NET_DSA_MV88E6131 is not set
+# CONFIG_NET_DSA_MV88E6171 is not set
+# CONFIG_NET_DSA_MV88E6352 is not set
+# CONFIG_NET_DSA_MV88E6XXX is not set
+# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set
+# CONFIG_NET_DSA_MV88E6XXX_PTP is not set
+# CONFIG_NET_DSA_QCA8K is not set
+# CONFIG_NET_DSA_REALTEK_SMI is not set
+# CONFIG_NET_DSA_SJA1105 is not set
+# CONFIG_NET_DSA_SMSC_LAN9303_I2C is not set
+# CONFIG_NET_DSA_SMSC_LAN9303_MDIO is not set
+# CONFIG_NET_DSA_TAG_8021Q is not set
+# CONFIG_NET_DSA_TAG_AR9331 is not set
+# CONFIG_NET_DSA_TAG_BRCM is not set
+# CONFIG_NET_DSA_TAG_BRCM_LEGACY is not set
+# CONFIG_NET_DSA_TAG_BRCM_PREPEND is not set
+# CONFIG_NET_DSA_TAG_DSA is not set
+# CONFIG_NET_DSA_TAG_EDSA is not set
+# CONFIG_NET_DSA_TAG_GSWIP is not set
+# CONFIG_NET_DSA_TAG_HELLCREEK is not set
+# CONFIG_NET_DSA_TAG_KSZ is not set
+# CONFIG_NET_DSA_TAG_LAN9303 is not set
+# CONFIG_NET_DSA_TAG_MTK is not set
+# CONFIG_NET_DSA_TAG_OCELOT is not set
+# CONFIG_NET_DSA_TAG_OCELOT_8021Q is not set
+# CONFIG_NET_DSA_TAG_QCA is not set
+# CONFIG_NET_DSA_TAG_RTL4_A is not set
+# CONFIG_NET_DSA_TAG_SJA1105 is not set
+# CONFIG_NET_DSA_TAG_TRAILER is not set
+# CONFIG_NET_DSA_TAG_XRS700X is not set
+# CONFIG_NET_DSA_VITESSE_VSC73XX is not set
+# CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM is not set
+# CONFIG_NET_DSA_VITESSE_VSC73XX_SPI is not set
+# CONFIG_NET_DSA_XRS700X_I2C is not set
+# CONFIG_NET_DSA_XRS700X_MDIO is not set
+# CONFIG_NET_EMATCH is not set
+# CONFIG_NET_EMATCH_CANID is not set
+# CONFIG_NET_EMATCH_CMP is not set
+# CONFIG_NET_EMATCH_IPT is not set
+# CONFIG_NET_EMATCH_META is not set
+# CONFIG_NET_EMATCH_NBYTE is not set
+CONFIG_NET_EMATCH_STACK=32
+# CONFIG_NET_EMATCH_TEXT is not set
+# CONFIG_NET_EMATCH_U32 is not set
+# CONFIG_NET_FAILOVER is not set
+# CONFIG_NET_FC is not set
+# CONFIG_NET_FOU is not set
+# CONFIG_NET_FOU_IP_TUNNELS is not set
+# CONFIG_NET_IFE is not set
+# CONFIG_NET_IPGRE is not set
+CONFIG_NET_IPGRE_BROADCAST=y
+# CONFIG_NET_IPGRE_DEMUX is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPVTI is not set
+# CONFIG_NET_IP_TUNNEL is not set
+# CONFIG_NET_KEY is not set
+# CONFIG_NET_KEY_MIGRATE is not set
+# CONFIG_NET_L3_MASTER_DEV is not set
+# CONFIG_NET_MEDIATEK_STAR_EMAC is not set
+# CONFIG_NET_MPLS_GSO is not set
+# CONFIG_NET_NCSI is not set
+# CONFIG_NET_NSH is not set
+# CONFIG_NET_PACKET_ENGINE is not set
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_NET_PTP_CLASSIFY is not set
+CONFIG_NET_RX_BUSY_POLL=y
+# CONFIG_NET_SB1000 is not set
+CONFIG_NET_SCHED=y
+# CONFIG_NET_SCH_ATM is not set
+# CONFIG_NET_SCH_CAKE is not set
+# CONFIG_NET_SCH_CBQ is not set
+# CONFIG_NET_SCH_CBS is not set
+# CONFIG_NET_SCH_CHOKE is not set
+# CONFIG_NET_SCH_CODEL is not set
+CONFIG_NET_SCH_DEFAULT=y
+# CONFIG_NET_SCH_DRR is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_ETF is not set
+# CONFIG_NET_SCH_ETS is not set
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_NET_SCH_FQ is not set
+CONFIG_NET_SCH_FQ_CODEL=y
+# CONFIG_NET_SCH_FQ_PIE is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_HHF is not set
+# CONFIG_NET_SCH_HTB is not set
+# CONFIG_NET_SCH_INGRESS is not set
+# CONFIG_NET_SCH_MQPRIO is not set
+# CONFIG_NET_SCH_MULTIQ is not set
+# CONFIG_NET_SCH_NETEM is not set
+# CONFIG_NET_SCH_PIE is not set
+# CONFIG_NET_SCH_PLUG is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_QFQ is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFB is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_SKBPRIO is not set
+# CONFIG_NET_SCH_TAPRIO is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCTPPROBE is not set
+# CONFIG_NET_SELFTESTS is not set
+CONFIG_NET_SOCK_MSG=y
+# CONFIG_NET_SWITCHDEV is not set
+# CONFIG_NET_TCPPROBE is not set
+# CONFIG_NET_TC_SKB_EXT is not set
+# CONFIG_NET_TEAM is not set
+# CONFIG_NET_TULIP is not set
+# CONFIG_NET_UDP_TUNNEL is not set
+CONFIG_NET_VENDOR_3COM=y
+CONFIG_NET_VENDOR_8390=y
+CONFIG_NET_VENDOR_ADAPTEC=y
+CONFIG_NET_VENDOR_AGERE=y
+CONFIG_NET_VENDOR_ALACRITECH=y
+CONFIG_NET_VENDOR_ALTEON=y
+CONFIG_NET_VENDOR_AMAZON=y
+CONFIG_NET_VENDOR_AMD=y
+CONFIG_NET_VENDOR_AQUANTIA=y
+CONFIG_NET_VENDOR_ARC=y
+CONFIG_NET_VENDOR_ATHEROS=y
+CONFIG_NET_VENDOR_AURORA=y
+CONFIG_NET_VENDOR_BROADCOM=y
+CONFIG_NET_VENDOR_BROCADE=y
+CONFIG_NET_VENDOR_CADENCE=y
+CONFIG_NET_VENDOR_CAVIUM=y
+CONFIG_NET_VENDOR_CHELSIO=y
+CONFIG_NET_VENDOR_CIRRUS=y
+CONFIG_NET_VENDOR_CISCO=y
+CONFIG_NET_VENDOR_CORTINA=y
+CONFIG_NET_VENDOR_DEC=y
+CONFIG_NET_VENDOR_DLINK=y
+CONFIG_NET_VENDOR_EMULEX=y
+CONFIG_NET_VENDOR_EXAR=y
+CONFIG_NET_VENDOR_EZCHIP=y
+CONFIG_NET_VENDOR_FARADAY=y
+CONFIG_NET_VENDOR_FREESCALE=y
+CONFIG_NET_VENDOR_FUJITSU=y
+CONFIG_NET_VENDOR_GOOGLE=y
+CONFIG_NET_VENDOR_HISILICON=y
+CONFIG_NET_VENDOR_HP=y
+CONFIG_NET_VENDOR_HUAWEI=y
+CONFIG_NET_VENDOR_I825XX=y
+CONFIG_NET_VENDOR_IBM=y
+CONFIG_NET_VENDOR_INTEL=y
+# CONFIG_NET_VENDOR_LITEX is not set
+CONFIG_NET_VENDOR_MARVELL=y
+CONFIG_NET_VENDOR_MELLANOX=y
+CONFIG_NET_VENDOR_MICREL=y
+CONFIG_NET_VENDOR_MICROCHIP=y
+CONFIG_NET_VENDOR_MICROSEMI=y
+# CONFIG_NET_VENDOR_MICROSOFT is not set
+CONFIG_NET_VENDOR_MYRI=y
+CONFIG_NET_VENDOR_NATSEMI=y
+CONFIG_NET_VENDOR_NETERION=y
+CONFIG_NET_VENDOR_NETRONOME=y
+CONFIG_NET_VENDOR_NI=y
+CONFIG_NET_VENDOR_NVIDIA=y
+CONFIG_NET_VENDOR_OKI=y
+CONFIG_NET_VENDOR_PACKET_ENGINES=y
+CONFIG_NET_VENDOR_PENSANDO=y
+CONFIG_NET_VENDOR_QLOGIC=y
+CONFIG_NET_VENDOR_QUALCOMM=y
+CONFIG_NET_VENDOR_RDC=y
+CONFIG_NET_VENDOR_REALTEK=y
+CONFIG_NET_VENDOR_RENESAS=y
+CONFIG_NET_VENDOR_ROCKER=y
+CONFIG_NET_VENDOR_SAMSUNG=y
+CONFIG_NET_VENDOR_SEEQ=y
+CONFIG_NET_VENDOR_SILAN=y
+CONFIG_NET_VENDOR_SIS=y
+CONFIG_NET_VENDOR_SMSC=y
+CONFIG_NET_VENDOR_SOCIONEXT=y
+CONFIG_NET_VENDOR_SOLARFLARE=y
+CONFIG_NET_VENDOR_STMICRO=y
+CONFIG_NET_VENDOR_SUN=y
+CONFIG_NET_VENDOR_SYNOPSYS=y
+CONFIG_NET_VENDOR_TEHUTI=y
+CONFIG_NET_VENDOR_TI=y
+CONFIG_NET_VENDOR_TOSHIBA=y
+CONFIG_NET_VENDOR_VIA=y
+CONFIG_NET_VENDOR_WIZNET=y
+CONFIG_NET_VENDOR_XILINX=y
+CONFIG_NET_VENDOR_XIRCOM=y
+# CONFIG_NET_VRF is not set
+# CONFIG_NET_XGENE is not set
+CONFIG_NEW_LEDS=y
+# CONFIG_NFC is not set
+# CONFIG_NFP is not set
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V2_ACL is not set
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+# CONFIG_NFSD_V4 is not set
+# CONFIG_NFS_ACL_SUPPORT is not set
+CONFIG_NFS_COMMON=y
+# CONFIG_NFS_DISABLE_UDP_SUPPORT is not set
+# CONFIG_NFS_FS is not set
+# CONFIG_NFS_FSCACHE is not set
+# CONFIG_NFS_SWAP is not set
+# CONFIG_NFS_V2 is not set
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_V4_1 is not set
+# CONFIG_NFTL is not set
+# CONFIG_NFT_BRIDGE_META is not set
+# CONFIG_NFT_BRIDGE_REJECT is not set
+# CONFIG_NFT_CONNLIMIT is not set
+# CONFIG_NFT_DUP_IPV4 is not set
+# CONFIG_NFT_DUP_IPV6 is not set
+# CONFIG_NFT_FIB_IPV4 is not set
+# CONFIG_NFT_FIB_IPV6 is not set
+# CONFIG_NFT_FIB_NETDEV is not set
+# CONFIG_NFT_FLOW_OFFLOAD is not set
+# CONFIG_NFT_OBJREF is not set
+# CONFIG_NFT_OSF is not set
+# CONFIG_NFT_REJECT_NETDEV is not set
+# CONFIG_NFT_RT is not set
+# CONFIG_NFT_SET_BITMAP is not set
+# CONFIG_NFT_SOCKET is not set
+# CONFIG_NFT_SYNPROXY is not set
+# CONFIG_NFT_TPROXY is not set
+# CONFIG_NFT_TUNNEL is not set
+# CONFIG_NFT_XFRM is not set
+# CONFIG_NF_CONNTRACK is not set
+# CONFIG_NF_CONNTRACK_AMANDA is not set
+# CONFIG_NF_CONNTRACK_BRIDGE is not set
+# CONFIG_NF_CONNTRACK_EVENTS is not set
+# CONFIG_NF_CONNTRACK_FTP is not set
+# CONFIG_NF_CONNTRACK_H323 is not set
+# CONFIG_NF_CONNTRACK_IRC is not set
+# CONFIG_NF_CONNTRACK_LABELS is not set
+# CONFIG_NF_CONNTRACK_MARK is not set
+# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set
+# CONFIG_NF_CONNTRACK_PPTP is not set
+CONFIG_NF_CONNTRACK_PROCFS=y
+# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set
+# CONFIG_NF_CONNTRACK_SANE is not set
+# CONFIG_NF_CONNTRACK_SECMARK is not set
+# CONFIG_NF_CONNTRACK_SIP is not set
+# CONFIG_NF_CONNTRACK_SNMP is not set
+# CONFIG_NF_CONNTRACK_TFTP is not set
+# CONFIG_NF_CONNTRACK_TIMEOUT is not set
+# CONFIG_NF_CONNTRACK_TIMESTAMP is not set
+# CONFIG_NF_CONNTRACK_ZONES is not set
+# CONFIG_NF_CT_NETLINK is not set
+# CONFIG_NF_CT_NETLINK_HELPER is not set
+# CONFIG_NF_CT_NETLINK_TIMEOUT is not set
+# CONFIG_NF_CT_PROTO_DCCP is not set
+# CONFIG_NF_CT_PROTO_GRE is not set
+# CONFIG_NF_CT_PROTO_SCTP is not set
+# CONFIG_NF_CT_PROTO_UDPLITE is not set
+# CONFIG_NF_DEFRAG_IPV4 is not set
+# CONFIG_NF_DUP_IPV4 is not set
+# CONFIG_NF_DUP_IPV6 is not set
+# CONFIG_NF_FLOW_TABLE is not set
+# CONFIG_NF_LOG_ARP is not set
+# CONFIG_NF_LOG_BRIDGE is not set
+# CONFIG_NF_LOG_IPV4 is not set
+# CONFIG_NF_LOG_NETDEV is not set
+# CONFIG_NF_LOG_SYSLOG is not set
+# CONFIG_NF_NAT is not set
+# CONFIG_NF_NAT_AMANDA is not set
+# CONFIG_NF_NAT_FTP is not set
+# CONFIG_NF_NAT_H323 is not set
+# CONFIG_NF_NAT_IRC is not set
+# CONFIG_NF_NAT_NEEDED is not set
+# CONFIG_NF_NAT_PPTP is not set
+# CONFIG_NF_NAT_PROTO_GRE is not set
+# CONFIG_NF_NAT_SIP is not set
+# CONFIG_NF_NAT_SNMP_BASIC is not set
+# CONFIG_NF_NAT_TFTP is not set
+# CONFIG_NF_REJECT_IPV4 is not set
+# CONFIG_NF_REJECT_IPV6 is not set
+# CONFIG_NF_SOCKET_IPV4 is not set
+# CONFIG_NF_SOCKET_IPV6 is not set
+# CONFIG_NF_TABLES is not set
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_TABLES_BRIDGE=y
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_IPV4=y
+CONFIG_NF_TABLES_IPV6=y
+CONFIG_NF_TABLES_NETDEV=y
+# CONFIG_NF_TABLES_SET is not set
+# CONFIG_NF_TPROXY_IPV4 is not set
+# CONFIG_NF_TPROXY_IPV6 is not set
+# CONFIG_NI65 is not set
+# CONFIG_NI903X_WDT is not set
+# CONFIG_NIC7018_WDT is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_NIU is not set
+# CONFIG_NI_XGE_MANAGEMENT_ENET is not set
+CONFIG_NLATTR=y
+# CONFIG_NLMON is not set
+# CONFIG_NLM_XLP_BOARD is not set
+# CONFIG_NLM_XLR_BOARD is not set
+# CONFIG_NLS is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+# CONFIG_NLS_UTF8 is not set
+CONFIG_NMI_LOG_BUF_SHIFT=13
+# CONFIG_NOA1305 is not set
+# CONFIG_NOP_USB_XCEIV is not set
+# CONFIG_NORTEL_HERMES is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+# CONFIG_NOUVEAU_LEGACY_CTX_SUPPORT is not set
+# CONFIG_NOZOMI is not set
+# CONFIG_NO_BOOTMEM is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_NO_HZ_FULL is not set
+# CONFIG_NO_HZ_IDLE is not set
+# CONFIG_NS83820 is not set
+# CONFIG_NTB is not set
+# CONFIG_NTFS3_64BIT_CLUSTER is not set
+# CONFIG_NTFS3_FS is not set
+# CONFIG_NTFS3_FS_POSIX_ACL is not set
+# CONFIG_NTFS3_LZX_XPRESS is not set
+# CONFIG_NTFS_DEBUG is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_NTP_PPS is not set
+# CONFIG_NULL_TTY is not set
+# CONFIG_NUMA is not set
+# CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set
+# CONFIG_NVM is not set
+# CONFIG_NVMEM is not set
+# CONFIG_NVMEM_BCM_OCOTP is not set
+# CONFIG_NVMEM_IMX_OCOTP is not set
+# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
+# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
+# CONFIG_NVMEM_REBOOT_MODE is not set
+# CONFIG_NVMEM_RMEM is not set
+# CONFIG_NVMEM_SYSFS is not set
+# CONFIG_NVMEM_U_BOOT_ENV is not set
+# CONFIG_NVME_FC is not set
+# CONFIG_NVME_TARGET is not set
+# CONFIG_NVME_TCP is not set
+# CONFIG_NVRAM is not set
+# CONFIG_NV_TCO is not set
+# CONFIG_NXP_C45_TJA11XX_PHY is not set
+# CONFIG_NXP_STB220 is not set
+# CONFIG_NXP_STB225 is not set
+# CONFIG_NXP_TJA11XX_PHY is not set
+# CONFIG_N_GSM is not set
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_OBS600 is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_OCTEONTX2_AF is not set
+# CONFIG_OCTEONTX2_PF is not set
+# CONFIG_OF_OVERLAY is not set
+CONFIG_OF_RESERVED_MEM=y
+# CONFIG_OF_UNITTEST is not set
+# CONFIG_OID_REGISTRY is not set
+# CONFIG_OMAP2_DSS_DEBUG is not set
+# CONFIG_OMAP2_DSS_DEBUGFS is not set
+# CONFIG_OMAP2_DSS_SDI is not set
+# CONFIG_OMAP_OCP2SCP is not set
+# CONFIG_OMAP_USB2 is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_OPENVSWITCH is not set
+# CONFIG_OPROFILE is not set
+# CONFIG_OPROFILE_EVENT_MULTIPLEX is not set
+# CONFIG_OPT3001 is not set
+CONFIG_OPTIMIZE_INLINING=y
+# CONFIG_ORANGEFS_FS is not set
+# CONFIG_ORION_WATCHDOG is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_OSNOISE_TRACER is not set
+CONFIG_OVERLAY_FS=y
+# CONFIG_OVERLAY_FS_INDEX is not set
+# CONFIG_OVERLAY_FS_METACOPY is not set
+CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y
+# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set
+CONFIG_OVERLAY_FS_XINO_AUTO=y
+# CONFIG_OWL_LOADER is not set
+# CONFIG_P54_COMMON is not set
+# CONFIG_PA12203001 is not set
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+# CONFIG_PACKING is not set
+# CONFIG_PAGE_EXTENSION is not set
+# CONFIG_PAGE_OWNER is not set
+# CONFIG_PAGE_POISONING is not set
+# CONFIG_PAGE_POOL is not set
+# CONFIG_PAGE_POOL_STATS is not set
+# CONFIG_PAGE_REPORTING is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_32KB is not set
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_64KB is not set
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PALMAS_GPADC is not set
+# CONFIG_PANASONIC_LAPTOP is not set
+# CONFIG_PANEL is not set
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PANIC_ON_OOPS_VALUE=1
+CONFIG_PANIC_TIMEOUT=1
+# CONFIG_PANTHERLORD_FF is not set
+# CONFIG_PARAVIRT is not set
+# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set
+# CONFIG_PARPORT is not set
+# CONFIG_PARPORT_1284 is not set
+# CONFIG_PARPORT_AX88796 is not set
+# CONFIG_PARPORT_GSC is not set
+# CONFIG_PARPORT_PC is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_PATA_ALI is not set
+# CONFIG_PATA_AMD is not set
+# CONFIG_PATA_ARASAN_CF is not set
+# CONFIG_PATA_ARTOP is not set
+# CONFIG_PATA_ATIIXP is not set
+# CONFIG_PATA_ATP867X is not set
+# CONFIG_PATA_CMD640_PCI is not set
+# CONFIG_PATA_CMD64X is not set
+# CONFIG_PATA_CS5520 is not set
+# CONFIG_PATA_CS5530 is not set
+# CONFIG_PATA_CS5535 is not set
+# CONFIG_PATA_CS5536 is not set
+# CONFIG_PATA_CYPRESS is not set
+# CONFIG_PATA_EFAR is not set
+# CONFIG_PATA_HPT366 is not set
+# CONFIG_PATA_HPT37X is not set
+# CONFIG_PATA_HPT3X2N is not set
+# CONFIG_PATA_HPT3X3 is not set
+# CONFIG_PATA_IMX is not set
+# CONFIG_PATA_ISAPNP is not set
+# CONFIG_PATA_IT8213 is not set
+# CONFIG_PATA_IT821X is not set
+# CONFIG_PATA_JMICRON is not set
+# CONFIG_PATA_LEGACY is not set
+# CONFIG_PATA_MARVELL is not set
+# CONFIG_PATA_MPIIX is not set
+# CONFIG_PATA_NETCELL is not set
+# CONFIG_PATA_NINJA32 is not set
+# CONFIG_PATA_NS87410 is not set
+# CONFIG_PATA_NS87415 is not set
+# CONFIG_PATA_OCTEON_CF is not set
+# CONFIG_PATA_OF_PLATFORM is not set
+# CONFIG_PATA_OLDPIIX is not set
+# CONFIG_PATA_OPTI is not set
+# CONFIG_PATA_OPTIDMA is not set
+# CONFIG_PATA_PCMCIA is not set
+# CONFIG_PATA_PDC2027X is not set
+# CONFIG_PATA_PDC_OLD is not set
+# CONFIG_PATA_PLATFORM is not set
+# CONFIG_PATA_QDI is not set
+# CONFIG_PATA_RADISYS is not set
+# CONFIG_PATA_RDC is not set
+# CONFIG_PATA_RZ1000 is not set
+# CONFIG_PATA_SC1200 is not set
+# CONFIG_PATA_SCH is not set
+# CONFIG_PATA_SERVERWORKS is not set
+# CONFIG_PATA_SIL680 is not set
+# CONFIG_PATA_SIS is not set
+# CONFIG_PATA_TOSHIBA is not set
+# CONFIG_PATA_TRIFLEX is not set
+# CONFIG_PATA_VIA is not set
+# CONFIG_PATA_WINBOND is not set
+# CONFIG_PATA_WINBOND_VLB is not set
+# CONFIG_PC104 is not set
+# CONFIG_PC300TOO is not set
+# CONFIG_PCCARD is not set
+# CONFIG_PCH_DMA is not set
+# CONFIG_PCH_GBE is not set
+# CONFIG_PCH_PHUB is not set
+# CONFIG_PCI is not set
+# CONFIG_PCI200SYN is not set
+# CONFIG_PCIEAER is not set
+# CONFIG_PCIEAER_INJECT is not set
+# CONFIG_PCIEASPM is not set
+# CONFIG_PCIEPORTBUS is not set
+# CONFIG_PCIE_AL is not set
+# CONFIG_PCIE_ALTERA is not set
+# CONFIG_PCIE_ARMADA_8K is not set
+CONFIG_PCIE_BUS_DEFAULT=y
+# CONFIG_PCIE_BUS_PEER2PEER is not set
+# CONFIG_PCIE_BUS_PERFORMANCE is not set
+# CONFIG_PCIE_BUS_SAFE is not set
+# CONFIG_PCIE_BUS_TUNE_OFF is not set
+# CONFIG_PCIE_BW is not set
+# CONFIG_PCIE_CADENCE_HOST is not set
+# CONFIG_PCIE_CADENCE_PLAT_HOST is not set
+# CONFIG_PCIE_DPC is not set
+# CONFIG_PCIE_DW_PLAT is not set
+# CONFIG_PCIE_DW_PLAT_HOST is not set
+# CONFIG_PCIE_ECRC is not set
+# CONFIG_PCIE_IPROC is not set
+# CONFIG_PCIE_KIRIN is not set
+# CONFIG_PCIE_LAYERSCAPE_GEN4 is not set
+# CONFIG_PCIE_MEDIATEK_GEN3 is not set
+# CONFIG_PCIE_MICROCHIP_HOST is not set
+# CONFIG_PCIE_PTM is not set
+# CONFIG_PCIE_XILINX is not set
+# CONFIG_PCIPCWATCHDOG is not set
+# CONFIG_PCI_ATMEL is not set
+# CONFIG_PCI_CNB20LE_QUIRK is not set
+# CONFIG_PCI_DEBUG is not set
+# CONFIG_PCI_DISABLE_COMMON_QUIRKS is not set
+# CONFIG_PCI_ENDPOINT is not set
+# CONFIG_PCI_ENDPOINT_TEST is not set
+# CONFIG_PCI_FTPCI100 is not set
+# CONFIG_PCI_HERMES is not set
+# CONFIG_PCI_HISI is not set
+# CONFIG_PCI_HOST_GENERIC is not set
+# CONFIG_PCI_HOST_THUNDER_ECAM is not set
+# CONFIG_PCI_HOST_THUNDER_PEM is not set
+# CONFIG_PCI_IOV is not set
+# CONFIG_PCI_J721E_HOST is not set
+# CONFIG_PCI_LAYERSCAPE is not set
+# CONFIG_PCI_MESON is not set
+# CONFIG_PCI_MSI is not set
+# CONFIG_PCI_PASID is not set
+# CONFIG_PCI_PF_STUB is not set
+# CONFIG_PCI_PRI is not set
+CONFIG_PCI_QUIRKS=y
+# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set
+# CONFIG_PCI_STUB is not set
+# CONFIG_PCI_SW_SWITCHTEC is not set
+CONFIG_PCI_SYSCALL=y
+# CONFIG_PCI_V3_SEMI is not set
+# CONFIG_PCI_XGENE is not set
+# CONFIG_PCMCIA is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_AHA152X is not set
+# CONFIG_PCMCIA_ATMEL is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_PCMCIA_DEBUG is not set
+# CONFIG_PCMCIA_FDOMAIN is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+# CONFIG_PCMCIA_HERMES is not set
+# CONFIG_PCMCIA_LOAD_CIS is not set
+# CONFIG_PCMCIA_NINJA_SCSI is not set
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_QLOGIC is not set
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_SPECTRUM is not set
+# CONFIG_PCMCIA_SYM53C500 is not set
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_XIRCOM is not set
+# CONFIG_PCNET32 is not set
+# CONFIG_PCPU_DEV_REFCNT is not set
+# CONFIG_PCSPKR_PLATFORM is not set
+# CONFIG_PCS_XPCS is not set
+# CONFIG_PD6729 is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_PDC_ADMA is not set
+# CONFIG_PERCPU_STATS is not set
+# CONFIG_PERCPU_TEST is not set
+# CONFIG_PERF_EVENTS is not set
+# CONFIG_PERF_EVENTS_AMD_POWER is not set
+# CONFIG_PERSISTENT_KEYRINGS is not set
+# CONFIG_PHANTOM is not set
+# CONFIG_PHONET is not set
+# CONFIG_PHYLIB is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+# CONFIG_PHY_CADENCE_DP is not set
+# CONFIG_PHY_CADENCE_DPHY is not set
+# CONFIG_PHY_CADENCE_SALVO is not set
+# CONFIG_PHY_CADENCE_SIERRA is not set
+# CONFIG_PHY_CADENCE_TORRENT is not set
+# CONFIG_PHY_CAN_TRANSCEIVER is not set
+# CONFIG_PHY_CPCAP_USB is not set
+# CONFIG_PHY_EXYNOS_DP_VIDEO is not set
+# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set
+# CONFIG_PHY_FSL_IMX8MQ_USB is not set
+# CONFIG_PHY_INGENIC_USB is not set
+# CONFIG_PHY_INTEL_KEEMBAY_EMMC is not set
+# CONFIG_PHY_MAPPHONE_MDM6600 is not set
+# CONFIG_PHY_MIXEL_MIPI_DPHY is not set
+# CONFIG_PHY_MTK_HDMI is not set
+# CONFIG_PHY_MTK_MIPI_DSI is not set
+# CONFIG_PHY_MVEBU_CP110_UTMI is not set
+# CONFIG_PHY_OCELOT_SERDES is not set
+# CONFIG_PHY_PISTACHIO_USB is not set
+# CONFIG_PHY_PXA_28NM_HSIC is not set
+# CONFIG_PHY_PXA_28NM_USB2 is not set
+# CONFIG_PHY_QCOM_DWC3 is not set
+# CONFIG_PHY_QCOM_USB_HS is not set
+# CONFIG_PHY_QCOM_USB_HSIC is not set
+# CONFIG_PHY_SAMSUNG_USB2 is not set
+# CONFIG_PHY_TUSB1210 is not set
+# CONFIG_PHY_XGENE is not set
+# CONFIG_PI433 is not set
+# CONFIG_PID_IN_CONTEXTIDR is not set
+# CONFIG_PID_NS is not set
+CONFIG_PINCONF=y
+# CONFIG_PINCTRL is not set
+# CONFIG_PINCTRL_AMD is not set
+# CONFIG_PINCTRL_AXP209 is not set
+# CONFIG_PINCTRL_CEDARFORK is not set
+# CONFIG_PINCTRL_EXYNOS is not set
+# CONFIG_PINCTRL_EXYNOS5440 is not set
+# CONFIG_PINCTRL_ICELAKE is not set
+# CONFIG_PINCTRL_INGENIC is not set
+# CONFIG_PINCTRL_LPASS_LPI is not set
+# CONFIG_PINCTRL_MCP23S08 is not set
+# CONFIG_PINCTRL_MDM9607 is not set
+# CONFIG_PINCTRL_MICROCHIP_SGPIO is not set
+# CONFIG_PINCTRL_MSM8953 is not set
+# CONFIG_PINCTRL_MSM8X74 is not set
+# CONFIG_PINCTRL_MT6779 is not set
+# CONFIG_PINCTRL_MT8167 is not set
+# CONFIG_PINCTRL_MT8192 is not set
+# CONFIG_PINCTRL_MT8195 is not set
+# CONFIG_PINCTRL_MT8365 is not set
+# CONFIG_PINCTRL_MTK_V2 is not set
+# CONFIG_PINCTRL_OCELOT is not set
+# CONFIG_PINCTRL_PISTACHIO is not set
+# CONFIG_PINCTRL_SC7280 is not set
+# CONFIG_PINCTRL_SC8180X is not set
+# CONFIG_PINCTRL_SDX55 is not set
+CONFIG_PINCTRL_SINGLE=y
+# CONFIG_PINCTRL_SM6115 is not set
+# CONFIG_PINCTRL_SM6125 is not set
+# CONFIG_PINCTRL_SM8350 is not set
+# CONFIG_PINCTRL_STMFX is not set
+# CONFIG_PINCTRL_SX150X is not set
+# CONFIG_PING is not set
+CONFIG_PINMUX=y
+# CONFIG_PKCS7_MESSAGE_PARSER is not set
+# CONFIG_PL310_ERRATA_588369 is not set
+# CONFIG_PL310_ERRATA_727915 is not set
+# CONFIG_PL310_ERRATA_753970 is not set
+# CONFIG_PL310_ERRATA_769419 is not set
+# CONFIG_PL320_MBOX is not set
+# CONFIG_PL330_DMA is not set
+# CONFIG_PLATFORM_MHU is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_PLIP is not set
+# CONFIG_PLX_DMA is not set
+# CONFIG_PLX_HERMES is not set
+# CONFIG_PM is not set
+# CONFIG_PMBUS is not set
+# CONFIG_PMC_MSP is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_PMS7003 is not set
+# CONFIG_PM_AUTOSLEEP is not set
+# CONFIG_PM_DEBUG is not set
+# CONFIG_PM_DEVFREQ is not set
+# CONFIG_PM_WAKELOCKS is not set
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_POSIX_TIMERS=y
+# CONFIG_POWERCAP is not set
+# CONFIG_POWER_AVS is not set
+# CONFIG_POWER_RESET is not set
+# CONFIG_POWER_RESET_BRCMKONA is not set
+# CONFIG_POWER_RESET_BRCMSTB is not set
+# CONFIG_POWER_RESET_GPIO is not set
+# CONFIG_POWER_RESET_GPIO_RESTART is not set
+# CONFIG_POWER_RESET_LINKSTATION is not set
+# CONFIG_POWER_RESET_LTC2952 is not set
+# CONFIG_POWER_RESET_PIIX4_POWEROFF is not set
+# CONFIG_POWER_RESET_QNAP is not set
+# CONFIG_POWER_RESET_REGULATOR is not set
+# CONFIG_POWER_RESET_RESTART is not set
+# CONFIG_POWER_RESET_SYSCON is not set
+# CONFIG_POWER_RESET_SYSCON_POWEROFF is not set
+# CONFIG_POWER_RESET_VERSATILE is not set
+# CONFIG_POWER_RESET_XGENE is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_POWER_SUPPLY_HWMON is not set
+# CONFIG_PPC4xx_GPIO is not set
+# CONFIG_PPC_16K_PAGES is not set
+# CONFIG_PPC_256K_PAGES is not set
+CONFIG_PPC_4K_PAGES=y
+# CONFIG_PPC_64K_PAGES is not set
+# CONFIG_PPC_DISABLE_WERROR is not set
+# CONFIG_PPC_EMULATED_STATS is not set
+# CONFIG_PPC_EPAPR_HV_BYTECHAN is not set
+# CONFIG_PPC_QUEUED_SPINLOCKS is not set
+# CONFIG_PPP is not set
+# CONFIG_PPPOATM is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_PPP_ASYNC is not set
+# CONFIG_PPP_BSDCOMP is not set
+# CONFIG_PPP_DEFLATE is not set
+CONFIG_PPP_FILTER=y
+# CONFIG_PPP_MPPE is not set
+CONFIG_PPP_MULTILINK=y
+# CONFIG_PPP_SYNC_TTY is not set
+# CONFIG_PPS is not set
+# CONFIG_PPS_CLIENT_GPIO is not set
+# CONFIG_PPS_CLIENT_KTIMER is not set
+# CONFIG_PPS_CLIENT_LDISC is not set
+# CONFIG_PPS_CLIENT_PARPORT is not set
+# CONFIG_PPS_DEBUG is not set
+# CONFIG_PPTP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
+# CONFIG_PREEMPTIRQ_EVENTS is not set
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PRESTERA is not set
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_PRIME_NUMBERS is not set
+CONFIG_PRINTK=y
+# CONFIG_PRINTK_CALLER is not set
+# CONFIG_PRINTK_INDEX is not set
+CONFIG_PRINTK_NMI=y
+CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13
+# CONFIG_PRINTK_TIME is not set
+CONFIG_PRINT_STACK_DEPTH=64
+# CONFIG_PRISM2_USB is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_PROC_CHILDREN is not set
+CONFIG_PROC_FS=y
+# CONFIG_PROC_KCORE is not set
+# CONFIG_PROC_PAGE_MONITOR is not set
+# CONFIG_PROC_STRIPPED is not set
+CONFIG_PROC_SYSCTL=y
+# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILING is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_PROVE_RAW_LOCK_NESTING is not set
+# CONFIG_PROVE_RCU is not set
+# CONFIG_PROVE_RCU_LIST is not set
+# CONFIG_PROVE_RCU_REPEATEDLY is not set
+# CONFIG_PSAMPLE is not set
+# CONFIG_PSB6970_PHY is not set
+# CONFIG_PSI is not set
+# CONFIG_PSTORE is not set
+# CONFIG_PSTORE_842_COMPRESS is not set
+# CONFIG_PSTORE_BLK is not set
+# CONFIG_PSTORE_COMPRESS is not set
+# CONFIG_PSTORE_CONSOLE is not set
+CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240
+# CONFIG_PSTORE_DEFLATE_COMPRESS is not set
+# CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT is not set
+# CONFIG_PSTORE_FTRACE is not set
+# CONFIG_PSTORE_LZ4HC_COMPRESS is not set
+# CONFIG_PSTORE_LZ4_COMPRESS is not set
+# CONFIG_PSTORE_LZO_COMPRESS is not set
+# CONFIG_PSTORE_PMSG is not set
+# CONFIG_PSTORE_RAM is not set
+# CONFIG_PSTORE_ZSTD_COMPRESS is not set
+# CONFIG_PTDUMP_DEBUGFS is not set
+# CONFIG_PTP_1588_CLOCK is not set
+# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set
+# CONFIG_PTP_1588_CLOCK_IDTCM is not set
+# CONFIG_PTP_1588_CLOCK_IXP46X is not set
+# CONFIG_PTP_1588_CLOCK_KVM is not set
+# CONFIG_PTP_1588_CLOCK_OCP is not set
+# CONFIG_PTP_1588_CLOCK_PCH is not set
+# CONFIG_PTP_1588_CLOCK_VMW is not set
+# CONFIG_PUBLIC_KEY_ALGO_RSA is not set
+# CONFIG_PVPANIC is not set
+# CONFIG_PWM is not set
+# CONFIG_PWM_ATMEL_TCB is not set
+# CONFIG_PWM_DEBUG is not set
+# CONFIG_PWM_DWC is not set
+# CONFIG_PWM_FSL_FTM is not set
+# CONFIG_PWM_IMG is not set
+# CONFIG_PWM_JZ4740 is not set
+# CONFIG_PWM_MEDIATEK is not set
+# CONFIG_PWM_PCA9685 is not set
+# CONFIG_PWM_RASPBERRYPI_POE is not set
+CONFIG_PWRSEQ_EMMC=y
+# CONFIG_PWRSEQ_SD8787 is not set
+CONFIG_PWRSEQ_SIMPLE=y
+# CONFIG_QCA7000 is not set
+# CONFIG_QCA7000_SPI is not set
+# CONFIG_QCA7000_UART is not set
+# CONFIG_QCOM_A7PLL is not set
+# CONFIG_QCOM_EMAC is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set
+# CONFIG_QCOM_GPI_DMA is not set
+# CONFIG_QCOM_HIDMA is not set
+# CONFIG_QCOM_HIDMA_MGMT is not set
+# CONFIG_QCOM_LMH is not set
+# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set
+# CONFIG_QCOM_SPMI_ADC5 is not set
+# CONFIG_QCOM_SPMI_ADC_TM5 is not set
+# CONFIG_QCOM_SPMI_IADC is not set
+# CONFIG_QCOM_SPMI_TEMP_ALARM is not set
+# CONFIG_QCOM_SPMI_VADC is not set
+# CONFIG_QED is not set
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QLA3XXX is not set
+# CONFIG_QLCNIC is not set
+# CONFIG_QLGE is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX6FS_FS is not set
+# CONFIG_QORIQ_CPUFREQ is not set
+# CONFIG_QORIQ_THERMAL is not set
+# CONFIG_QRTR is not set
+# CONFIG_QRTR_MHI is not set
+# CONFIG_QRTR_TUN is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_QUEUED_LOCK_STAT is not set
+# CONFIG_QUICC_ENGINE is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+# CONFIG_QUOTA_DEBUG is not set
+# CONFIG_QUOTA_NETLINK_INTERFACE is not set
+# CONFIG_R3964 is not set
+# CONFIG_R6040 is not set
+# CONFIG_R8169 is not set
+# CONFIG_R8188EU is not set
+# CONFIG_R8712U is not set
+# CONFIG_R8723AU is not set
+# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+# CONFIG_RAID6_PQ_BENCHMARK is not set
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_RALINK is not set
+# CONFIG_RANDOM32_SELFTEST is not set
+# CONFIG_RANDOMIZE_BASE is not set
+# CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set
+CONFIG_RANDOM_TRUST_BOOTLOADER=y
+CONFIG_RANDOM_TRUST_CPU=y
+# CONFIG_RAPIDIO is not set
+# CONFIG_RAS is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_RCU_BOOST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_RCU_EQS_DEBUG is not set
+# CONFIG_RCU_EXPEDITE_BOOT is not set
+# CONFIG_RCU_EXPERT is not set
+CONFIG_RCU_KTHREAD_PRIO=0
+CONFIG_RCU_NEED_SEGCBLIST=y
+# CONFIG_RCU_PERF_TEST is not set
+# CONFIG_RCU_REF_SCALE_TEST is not set
+# CONFIG_RCU_SCALE_TEST is not set
+CONFIG_RCU_STALL_COMMON=y
+# CONFIG_RCU_STRICT_GRACE_PERIOD is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY=3
+# CONFIG_RCU_TRACE is not set
+# CONFIG_RC_ATI_REMOTE is not set
+# CONFIG_RC_CORE is not set
+# CONFIG_RC_DECODERS is not set
+# CONFIG_RC_LOOPBACK is not set
+# CONFIG_RC_MAP is not set
+# CONFIG_RC_XBOX_DVD is not set
+# CONFIG_RDS is not set
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_GZIP is not set
+# CONFIG_RD_LZ4 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_ZSTD is not set
+# CONFIG_READABLE_ASM is not set
+# CONFIG_READ_ONLY_THP_FOR_FS is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_REDWOOD is not set
+# CONFIG_REED_SOLOMON is not set
+# CONFIG_REED_SOLOMON_DEC8 is not set
+# CONFIG_REED_SOLOMON_ENC8 is not set
+# CONFIG_REED_SOLOMON_TEST is not set
+# CONFIG_REGMAP is not set
+# CONFIG_REGMAP_I2C is not set
+# CONFIG_REGMAP_MMIO is not set
+# CONFIG_REGMAP_SPI is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_88PG86X is not set
+# CONFIG_REGULATOR_ACT8865 is not set
+# CONFIG_REGULATOR_AD5398 is not set
+# CONFIG_REGULATOR_ANATOP is not set
+# CONFIG_REGULATOR_DA9121 is not set
+# CONFIG_REGULATOR_DA9210 is not set
+# CONFIG_REGULATOR_DA9211 is not set
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FAN53555 is not set
+# CONFIG_REGULATOR_FAN53880 is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_GPIO is not set
+# CONFIG_REGULATOR_ISL6271A is not set
+# CONFIG_REGULATOR_ISL9305 is not set
+# CONFIG_REGULATOR_LP3971 is not set
+# CONFIG_REGULATOR_LP3972 is not set
+# CONFIG_REGULATOR_LP872X is not set
+# CONFIG_REGULATOR_LP8755 is not set
+# CONFIG_REGULATOR_LTC3589 is not set
+# CONFIG_REGULATOR_LTC3676 is not set
+# CONFIG_REGULATOR_MAX1586 is not set
+# CONFIG_REGULATOR_MAX77620 is not set
+# CONFIG_REGULATOR_MAX77826 is not set
+# CONFIG_REGULATOR_MAX8649 is not set
+# CONFIG_REGULATOR_MAX8660 is not set
+# CONFIG_REGULATOR_MAX8893 is not set
+# CONFIG_REGULATOR_MAX8952 is not set
+# CONFIG_REGULATOR_MAX8973 is not set
+# CONFIG_REGULATOR_MCP16502 is not set
+# CONFIG_REGULATOR_MP5416 is not set
+# CONFIG_REGULATOR_MP8859 is not set
+# CONFIG_REGULATOR_MP886X is not set
+# CONFIG_REGULATOR_MPQ7920 is not set
+# CONFIG_REGULATOR_MT6311 is not set
+# CONFIG_REGULATOR_MT6315 is not set
+# CONFIG_REGULATOR_MT6359 is not set
+# CONFIG_REGULATOR_PCA9450 is not set
+# CONFIG_REGULATOR_PF8X00 is not set
+# CONFIG_REGULATOR_PFUZE100 is not set
+# CONFIG_REGULATOR_PV88060 is not set
+# CONFIG_REGULATOR_PV88080 is not set
+# CONFIG_REGULATOR_PV88090 is not set
+# CONFIG_REGULATOR_PWM is not set
+# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set
+# CONFIG_REGULATOR_RT4801 is not set
+# CONFIG_REGULATOR_RT6160 is not set
+# CONFIG_REGULATOR_RT6245 is not set
+# CONFIG_REGULATOR_RTMV20 is not set
+# CONFIG_REGULATOR_RTQ2134 is not set
+# CONFIG_REGULATOR_RTQ6752 is not set
+# CONFIG_REGULATOR_SLG51000 is not set
+# CONFIG_REGULATOR_SY8106A is not set
+# CONFIG_REGULATOR_SY8824X is not set
+# CONFIG_REGULATOR_SY8827N is not set
+# CONFIG_REGULATOR_TI_ABB is not set
+# CONFIG_REGULATOR_TPS51632 is not set
+# CONFIG_REGULATOR_TPS62360 is not set
+# CONFIG_REGULATOR_TPS65023 is not set
+# CONFIG_REGULATOR_TPS6507X is not set
+# CONFIG_REGULATOR_TPS65132 is not set
+# CONFIG_REGULATOR_TPS6524X is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+# CONFIG_REGULATOR_VCTRL is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_REISERFS_FS_POSIX_ACL is not set
+# CONFIG_REISERFS_FS_SECURITY is not set
+CONFIG_REISERFS_FS_XATTR=y
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_RELAY is not set
+# CONFIG_RELOCATABLE is not set
+# CONFIG_REMOTEPROC is not set
+# CONFIG_RENESAS_PHY is not set
+# CONFIG_RESET_ATH79 is not set
+# CONFIG_RESET_BERLIN is not set
+# CONFIG_RESET_BRCMSTB_RESCAL is not set
+# CONFIG_RESET_CONTROLLER is not set
+# CONFIG_RESET_IMX7 is not set
+# CONFIG_RESET_INTEL_GW is not set
+# CONFIG_RESET_LANTIQ is not set
+# CONFIG_RESET_LPC18XX is not set
+# CONFIG_RESET_MESON is not set
+# CONFIG_RESET_PISTACHIO is not set
+# CONFIG_RESET_SOCFPGA is not set
+# CONFIG_RESET_STM32 is not set
+# CONFIG_RESET_SUNXI is not set
+# CONFIG_RESET_TEGRA_BPMP is not set
+# CONFIG_RESET_TI_SYSCON is not set
+# CONFIG_RESET_ZYNQ is not set
+# CONFIG_RFD77402 is not set
+# CONFIG_RFD_FTL is not set
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_FULL is not set
+# CONFIG_RFKILL_GPIO is not set
+# CONFIG_RFKILL_INPUT is not set
+# CONFIG_RFKILL_LEDS is not set
+# CONFIG_RFKILL_REGULATOR is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_RING_BUFFER_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
+# CONFIG_RMI4_CORE is not set
+# CONFIG_RMNET is not set
+# CONFIG_ROCKCHIP_PHY is not set
+# CONFIG_ROCKER is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_ROSE is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPMSG_QCOM_GLINK_RPM is not set
+# CONFIG_RPMSG_VIRTIO is not set
+# CONFIG_RPMSG_WWAN_CTRL is not set
+# CONFIG_RPR0521 is not set
+# CONFIG_RSEQ is not set
+# CONFIG_RT2X00 is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_RTC_DEBUG is not set
+# CONFIG_RTC_DRV_ABB5ZES3 is not set
+# CONFIG_RTC_DRV_ABEOZ9 is not set
+# CONFIG_RTC_DRV_ABX80X is not set
+# CONFIG_RTC_DRV_ARMADA38X is not set
+# CONFIG_RTC_DRV_AU1XXX is not set
+# CONFIG_RTC_DRV_BQ32K is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_CADENCE is not set
+CONFIG_RTC_DRV_CMOS=y
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1302 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1307_CENTURY is not set
+# CONFIG_RTC_DRV_DS1307_HWMON is not set
+# CONFIG_RTC_DRV_DS1343 is not set
+# CONFIG_RTC_DRV_DS1347 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_DS1685_FAMILY is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_DS2404 is not set
+# CONFIG_RTC_DRV_DS3232 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_EM3027 is not set
+# CONFIG_RTC_DRV_EP93XX is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_FTRTC010 is not set
+# CONFIG_RTC_DRV_GENERIC is not set
+# CONFIG_RTC_DRV_GOLDFISH is not set
+# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set
+# CONFIG_RTC_DRV_HYM8563 is not set
+# CONFIG_RTC_DRV_ISL12022 is not set
+# CONFIG_RTC_DRV_ISL12026 is not set
+# CONFIG_RTC_DRV_ISL12057 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_JZ4740 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_M41T93 is not set
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_MAX6916 is not set
+# CONFIG_RTC_DRV_MAX77686 is not set
+# CONFIG_RTC_DRV_MCP795 is not set
+# CONFIG_RTC_DRV_MOXART is not set
+# CONFIG_RTC_DRV_MPC5121 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_MT2712 is not set
+# CONFIG_RTC_DRV_OMAP is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+# CONFIG_RTC_DRV_PCF2127 is not set
+# CONFIG_RTC_DRV_PCF85063 is not set
+# CONFIG_RTC_DRV_PCF8523 is not set
+# CONFIG_RTC_DRV_PCF85363 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_PL030 is not set
+# CONFIG_RTC_DRV_PL031 is not set
+# CONFIG_RTC_DRV_PS3 is not set
+# CONFIG_RTC_DRV_PT7C4338 is not set
+# CONFIG_RTC_DRV_R7301 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_RTC7301 is not set
+# CONFIG_RTC_DRV_RV3028 is not set
+# CONFIG_RTC_DRV_RV3029C2 is not set
+# CONFIG_RTC_DRV_RV3032 is not set
+# CONFIG_RTC_DRV_RV8803 is not set
+# CONFIG_RTC_DRV_RX4581 is not set
+# CONFIG_RTC_DRV_RX6110 is not set
+# CONFIG_RTC_DRV_RX8010 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_SD3078 is not set
+# CONFIG_RTC_DRV_SNVS is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_SUN6I is not set
+# CONFIG_RTC_DRV_TEGRA is not set
+# CONFIG_RTC_DRV_TEST is not set
+# CONFIG_RTC_DRV_V3020 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_XGENE is not set
+# CONFIG_RTC_DRV_ZYNQMP is not set
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_NVMEM is not set
+CONFIG_RTC_SYSTOHC=y
+CONFIG_RTC_SYSTOHC_DEVICE="rtc0"
+# CONFIG_RTL8180 is not set
+# CONFIG_RTL8187 is not set
+# CONFIG_RTL8192E is not set
+# CONFIG_RTL8192U is not set
+# CONFIG_RTL8306_PHY is not set
+# CONFIG_RTL8366RB_PHY is not set
+# CONFIG_RTL8366S_PHY is not set
+# CONFIG_RTL8366_SMI is not set
+# CONFIG_RTL8366_SMI_DEBUG_FS is not set
+# CONFIG_RTL8367B_PHY is not set
+# CONFIG_RTL8367_PHY is not set
+# CONFIG_RTLLIB is not set
+# CONFIG_RTL_CARDS is not set
+# CONFIG_RTS5208 is not set
+CONFIG_RT_MUTEXES=y
+# CONFIG_RUNTIME_DEBUG is not set
+CONFIG_RUNTIME_TESTING_MENU=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_RXKAD=y
+# CONFIG_S2IO is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_SAMSUNG_LAPTOP is not set
+# CONFIG_SATA_ACARD_AHCI is not set
+# CONFIG_SATA_AHCI is not set
+# CONFIG_SATA_AHCI_PLATFORM is not set
+# CONFIG_SATA_DWC is not set
+# CONFIG_SATA_FSL is not set
+# CONFIG_SATA_HIGHBANK is not set
+# CONFIG_SATA_HOST is not set
+# CONFIG_SATA_INIC162X is not set
+CONFIG_SATA_MOBILE_LPM_POLICY=0
+# CONFIG_SATA_MV is not set
+# CONFIG_SATA_NV is not set
+# CONFIG_SATA_PMP is not set
+# CONFIG_SATA_PROMISE is not set
+# CONFIG_SATA_QSTOR is not set
+# CONFIG_SATA_RCAR is not set
+# CONFIG_SATA_SIL is not set
+# CONFIG_SATA_SIL24 is not set
+# CONFIG_SATA_SIS is not set
+# CONFIG_SATA_SVW is not set
+# CONFIG_SATA_SX4 is not set
+# CONFIG_SATA_ULI is not set
+# CONFIG_SATA_VIA is not set
+# CONFIG_SATA_VITESSE is not set
+# CONFIG_SBC_FITPC2_WATCHDOG is not set
+CONFIG_SBITMAP=y
+# CONFIG_SC92031 is not set
+# CONFIG_SCA3000 is not set
+# CONFIG_SCA3300 is not set
+# CONFIG_SCACHE_DEBUGFS is not set
+# CONFIG_SCC is not set
+# CONFIG_SCD30_CORE is not set
+# CONFIG_SCF_TORTURE_TEST is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_SCHED_AUTOGROUP is not set
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCHED_MC is not set
+CONFIG_SCHED_OMIT_FRAME_POINTER=y
+# CONFIG_SCHED_SMT is not set
+CONFIG_SCHED_STACK_END_CHECK=y
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_SCR24X is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_3W_SAS is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC94XX is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_SCSI_BFA_FC is not set
+# CONFIG_SCSI_BNX2X_FCOE is not set
+# CONFIG_SCSI_BNX2_ISCSI is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CHELSIO_FCOE is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_CXGB3_ISCSI is not set
+# CONFIG_SCSI_CXGB4_ISCSI is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_ESAS2R is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_FDOMAIN_PCI is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_HISI_SAS is not set
+# CONFIG_SCSI_HPSA is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_ISCI is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_LOGGING is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_SCSI_LPFC is not set
+CONFIG_SCSI_MOD=y
+# CONFIG_SCSI_MPI3MR is not set
+# CONFIG_SCSI_MPT2SAS is not set
+# CONFIG_SCSI_MPT3SAS is not set
+# CONFIG_SCSI_MQ_DEFAULT is not set
+# CONFIG_SCSI_MVSAS is not set
+# CONFIG_SCSI_MVSAS_DEBUG is not set
+# CONFIG_SCSI_MVUMI is not set
+# CONFIG_SCSI_MYRB is not set
+# CONFIG_SCSI_MYRS is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_OSD_INITIATOR is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PM8001 is not set
+# CONFIG_SCSI_PMCRAID is not set
+CONFIG_SCSI_PROC_FS=y
+# CONFIG_SCSI_QLA_FC is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SAS_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+# CONFIG_SCSI_SMARTPQI is not set
+# CONFIG_SCSI_SNIC is not set
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_STEX is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_UFSHCD is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_VIRTIO is not set
+# CONFIG_SCSI_WD719X is not set
+# CONFIG_SC_CAMCC_7180 is not set
+# CONFIG_SC_DISPCC_7280 is not set
+# CONFIG_SC_GCC_7280 is not set
+# CONFIG_SC_GCC_8180X is not set
+# CONFIG_SC_GPUCC_7280 is not set
+# CONFIG_SC_VIDEOCC_7280 is not set
+# CONFIG_SCx200_ACB is not set
+# CONFIG_SDIO_UART is not set
+# CONFIG_SDM_GPUCC_660 is not set
+# CONFIG_SDM_MMCC_660 is not set
+# CONFIG_SDR_MAX2175 is not set
+# CONFIG_SDR_PLATFORM_DRIVERS is not set
+# CONFIG_SDX_GCC_55 is not set
+# CONFIG_SD_ADC_MODULATOR is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_SECCOMP_CACHE_DEBUG is not set
+CONFIG_SECTION_MISMATCH_WARN_ONLY=y
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_APPARMOR is not set
+CONFIG_SECURITY_DMESG_RESTRICT=y
+# CONFIG_SECURITY_LANDLOCK is not set
+# CONFIG_SECURITY_LOADPIN is not set
+# CONFIG_SECURITY_LOCKDOWN_LSM is not set
+# CONFIG_SECURITY_NETWORK_XFRM is not set
+# CONFIG_SECURITY_PATH is not set
+# CONFIG_SECURITY_SAFESETID is not set
+# CONFIG_SECURITY_SELINUX_AVC_STATS is not set
+# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set
+CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=0
+# CONFIG_SECURITY_SELINUX_DEVELOP is not set
+# CONFIG_SECURITY_SELINUX_DISABLE is not set
+# CONFIG_SECURITY_SMACK is not set
+# CONFIG_SECURITY_TOMOYO is not set
+# CONFIG_SECURITY_YAMA is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_SENSIRION_SGP30 is not set
+# CONFIG_SENSIRION_SGP40 is not set
+# CONFIG_SENSORS_ABITUGURU is not set
+# CONFIG_SENSORS_ABITUGURU3 is not set
+# CONFIG_SENSORS_ACPI_POWER is not set
+# CONFIG_SENSORS_AD7314 is not set
+# CONFIG_SENSORS_AD7414 is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADC128D818 is not set
+# CONFIG_SENSORS_ADCXX is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM1177 is not set
+# CONFIG_SENSORS_ADM1266 is not set
+# CONFIG_SENSORS_ADM1275 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADS1015 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_ADS7871 is not set
+# CONFIG_SENSORS_ADT7310 is not set
+# CONFIG_SENSORS_ADT7410 is not set
+# CONFIG_SENSORS_ADT7411 is not set
+# CONFIG_SENSORS_ADT7462 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+# CONFIG_SENSORS_ADT7475 is not set
+# CONFIG_SENSORS_AHT10 is not set
+# CONFIG_SENSORS_AMC6821 is not set
+# CONFIG_SENSORS_APDS990X is not set
+# CONFIG_SENSORS_APPLESMC is not set
+# CONFIG_SENSORS_AQUACOMPUTER_D5NEXT is not set
+# CONFIG_SENSORS_AS370 is not set
+# CONFIG_SENSORS_ASB100 is not set
+# CONFIG_SENSORS_ASC7621 is not set
+# CONFIG_SENSORS_ASPEED is not set
+# CONFIG_SENSORS_ATK0110 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_AXI_FAN_CONTROL is not set
+# CONFIG_SENSORS_BEL_PFE is not set
+# CONFIG_SENSORS_BH1770 is not set
+# CONFIG_SENSORS_BH1780 is not set
+# CONFIG_SENSORS_BPA_RS600 is not set
+# CONFIG_SENSORS_CORETEMP is not set
+# CONFIG_SENSORS_CORSAIR_CPRO is not set
+# CONFIG_SENSORS_CORSAIR_PSU is not set
+# CONFIG_SENSORS_DELL_SMM is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_DPS920AB is not set
+# CONFIG_SENSORS_DRIVETEMP is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_DS620 is not set
+# CONFIG_SENSORS_EMC1403 is not set
+# CONFIG_SENSORS_EMC2103 is not set
+# CONFIG_SENSORS_EMC2305 is not set
+# CONFIG_SENSORS_EMC6W201 is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_FAM15H_POWER is not set
+# CONFIG_SENSORS_FSCHMD is not set
+# CONFIG_SENSORS_FSP_3Y is not set
+# CONFIG_SENSORS_FTSTEUTATES is not set
+# CONFIG_SENSORS_G760A is not set
+# CONFIG_SENSORS_G762 is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_GPIO_FAN is not set
+# CONFIG_SENSORS_GSC is not set
+# CONFIG_SENSORS_HDAPS is not set
+# CONFIG_SENSORS_HIH6130 is not set
+# CONFIG_SENSORS_HMC5843 is not set
+# CONFIG_SENSORS_HMC5843_I2C is not set
+# CONFIG_SENSORS_HMC5843_SPI is not set
+# CONFIG_SENSORS_HTU21 is not set
+# CONFIG_SENSORS_I5500 is not set
+# CONFIG_SENSORS_I5K_AMB is not set
+# CONFIG_SENSORS_IBM_CFFPS is not set
+# CONFIG_SENSORS_IIO_HWMON is not set
+# CONFIG_SENSORS_INA209 is not set
+# CONFIG_SENSORS_INA2XX is not set
+# CONFIG_SENSORS_INA3221 is not set
+# CONFIG_SENSORS_INSPUR_IPSPS is not set
+# CONFIG_SENSORS_IR35221 is not set
+# CONFIG_SENSORS_IR36021 is not set
+# CONFIG_SENSORS_IR38064 is not set
+# CONFIG_SENSORS_IRPS5401 is not set
+# CONFIG_SENSORS_ISL29018 is not set
+# CONFIG_SENSORS_ISL29028 is not set
+# CONFIG_SENSORS_ISL68137 is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_JC42 is not set
+# CONFIG_SENSORS_K10TEMP is not set
+# CONFIG_SENSORS_K8TEMP is not set
+# CONFIG_SENSORS_LINEAGE is not set
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSORS_LM25066 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_LM73 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_LM95234 is not set
+# CONFIG_SENSORS_LM95241 is not set
+# CONFIG_SENSORS_LM95245 is not set
+# CONFIG_SENSORS_LTC2945 is not set
+# CONFIG_SENSORS_LTC2947_I2C is not set
+# CONFIG_SENSORS_LTC2947_SPI is not set
+# CONFIG_SENSORS_LTC2978 is not set
+# CONFIG_SENSORS_LTC2990 is not set
+# CONFIG_SENSORS_LTC2992 is not set
+# CONFIG_SENSORS_LTC3815 is not set
+# CONFIG_SENSORS_LTC4151 is not set
+# CONFIG_SENSORS_LTC4215 is not set
+# CONFIG_SENSORS_LTC4222 is not set
+# CONFIG_SENSORS_LTC4245 is not set
+# CONFIG_SENSORS_LTC4260 is not set
+# CONFIG_SENSORS_LTC4261 is not set
+# CONFIG_SENSORS_LTQ_CPUTEMP is not set
+# CONFIG_SENSORS_MAX1111 is not set
+# CONFIG_SENSORS_MAX127 is not set
+# CONFIG_SENSORS_MAX15301 is not set
+# CONFIG_SENSORS_MAX16064 is not set
+# CONFIG_SENSORS_MAX16065 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX16601 is not set
+# CONFIG_SENSORS_MAX1668 is not set
+# CONFIG_SENSORS_MAX197 is not set
+# CONFIG_SENSORS_MAX20730 is not set
+# CONFIG_SENSORS_MAX20751 is not set
+# CONFIG_SENSORS_MAX31722 is not set
+# CONFIG_SENSORS_MAX31730 is not set
+# CONFIG_SENSORS_MAX31785 is not set
+# CONFIG_SENSORS_MAX31790 is not set
+# CONFIG_SENSORS_MAX34440 is not set
+# CONFIG_SENSORS_MAX6621 is not set
+# CONFIG_SENSORS_MAX6639 is not set
+# CONFIG_SENSORS_MAX6642 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_MAX6697 is not set
+# CONFIG_SENSORS_MAX8688 is not set
+# CONFIG_SENSORS_MCP3021 is not set
+# CONFIG_SENSORS_MP2888 is not set
+# CONFIG_SENSORS_MP2975 is not set
+# CONFIG_SENSORS_MR75203 is not set
+# CONFIG_SENSORS_NCT6683 is not set
+# CONFIG_SENSORS_NCT6775 is not set
+# CONFIG_SENSORS_NCT7802 is not set
+# CONFIG_SENSORS_NCT7904 is not set
+# CONFIG_SENSORS_NPCM7XX is not set
+# CONFIG_SENSORS_NSA320 is not set
+# CONFIG_SENSORS_NTC_THERMISTOR is not set
+# CONFIG_SENSORS_NZXT_KRAKEN2 is not set
+# CONFIG_SENSORS_OCC_P8_I2C is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_PIM4328 is not set
+# CONFIG_SENSORS_PM6764TR is not set
+# CONFIG_SENSORS_PMBUS is not set
+# CONFIG_SENSORS_POWR1220 is not set
+# CONFIG_SENSORS_PWM_FAN is not set
+# CONFIG_SENSORS_PXE1610 is not set
+# CONFIG_SENSORS_Q54SJ108A2 is not set
+# CONFIG_SENSORS_RM3100_I2C is not set
+# CONFIG_SENSORS_RM3100_SPI is not set
+# CONFIG_SENSORS_SBRMI is not set
+# CONFIG_SENSORS_SBTSI is not set
+# CONFIG_SENSORS_SCH5627 is not set
+# CONFIG_SENSORS_SCH5636 is not set
+# CONFIG_SENSORS_SCH56XX_COMMON is not set
+# CONFIG_SENSORS_SHT15 is not set
+# CONFIG_SENSORS_SHT21 is not set
+# CONFIG_SENSORS_SHT3x is not set
+# CONFIG_SENSORS_SHT4x is not set
+# CONFIG_SENSORS_SHTC1 is not set
+# CONFIG_SENSORS_SIS5595 is not set
+# CONFIG_SENSORS_SMM665 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_STPDDC60 is not set
+# CONFIG_SENSORS_STTS751 is not set
+# CONFIG_SENSORS_TC654 is not set
+# CONFIG_SENSORS_TC74 is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_TMP102 is not set
+# CONFIG_SENSORS_TMP103 is not set
+# CONFIG_SENSORS_TMP108 is not set
+# CONFIG_SENSORS_TMP401 is not set
+# CONFIG_SENSORS_TMP421 is not set
+# CONFIG_SENSORS_TMP513 is not set
+# CONFIG_SENSORS_TPS23861 is not set
+# CONFIG_SENSORS_TPS40422 is not set
+# CONFIG_SENSORS_TPS53679 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_SENSORS_TSL2563 is not set
+# CONFIG_SENSORS_UCD9000 is not set
+# CONFIG_SENSORS_UCD9200 is not set
+# CONFIG_SENSORS_VEXPRESS is not set
+# CONFIG_SENSORS_VIA686A is not set
+# CONFIG_SENSORS_VIA_CPUTEMP is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_VT8231 is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83773G is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83795 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_XDPE122 is not set
+# CONFIG_SENSORS_XGENE is not set
+# CONFIG_SENSORS_ZL6100 is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_16550A_VARIANTS=y
+# CONFIG_SERIAL_8250_ACCENT is not set
+# CONFIG_SERIAL_8250_ASPEED_VUART is not set
+# CONFIG_SERIAL_8250_BOCA is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_CS is not set
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+CONFIG_SERIAL_8250_DMA=y
+# CONFIG_SERIAL_8250_DW is not set
+# CONFIG_SERIAL_8250_EM is not set
+# CONFIG_SERIAL_8250_EXAR is not set
+# CONFIG_SERIAL_8250_EXAR_ST16C554 is not set
+# CONFIG_SERIAL_8250_EXTENDED is not set
+# CONFIG_SERIAL_8250_FINTEK is not set
+# CONFIG_SERIAL_8250_FOURPORT is not set
+# CONFIG_SERIAL_8250_HUB6 is not set
+# CONFIG_SERIAL_8250_INGENIC is not set
+# CONFIG_SERIAL_8250_LPSS is not set
+# CONFIG_SERIAL_8250_MANY_PORTS is not set
+# CONFIG_SERIAL_8250_MID is not set
+# CONFIG_SERIAL_8250_MOXA is not set
+CONFIG_SERIAL_8250_NR_UARTS=2
+# CONFIG_SERIAL_8250_PCI is not set
+# CONFIG_SERIAL_8250_RSA is not set
+# CONFIG_SERIAL_8250_RT288X is not set
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_AMBA_PL010 is not set
+# CONFIG_SERIAL_AMBA_PL011 is not set
+# CONFIG_SERIAL_ARC is not set
+# CONFIG_SERIAL_BCM63XX is not set
+# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_DEV_BUS is not set
+CONFIG_SERIAL_EARLYCON=y
+# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set
+# CONFIG_SERIAL_FSL_LINFLEXUART is not set
+# CONFIG_SERIAL_FSL_LPUART is not set
+# CONFIG_SERIAL_GRLIB_GAISLER_APBUART is not set
+# CONFIG_SERIAL_IFX6X60 is not set
+# CONFIG_SERIAL_JSM is not set
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX310X is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_SERIAL_OF_PLATFORM is not set
+# CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL is not set
+# CONFIG_SERIAL_PCH_UART is not set
+# CONFIG_SERIAL_RP2 is not set
+# CONFIG_SERIAL_SC16IS7XX is not set
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_SH_SCI is not set
+# CONFIG_SERIAL_SIFIVE is not set
+# CONFIG_SERIAL_SPRD is not set
+# CONFIG_SERIAL_STM32 is not set
+# CONFIG_SERIAL_ST_ASC is not set
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_UARTLITE is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_SERIO is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_AMBAKMI is not set
+# CONFIG_SERIO_APBPS2 is not set
+# CONFIG_SERIO_ARC_PS2 is not set
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_GPIO_PS2 is not set
+# CONFIG_SERIO_I8042 is not set
+# CONFIG_SERIO_LIBPS2 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_SERIO_SUN4I_PS2 is not set
+# CONFIG_SFC is not set
+# CONFIG_SFC_FALCON is not set
+# CONFIG_SFI is not set
+# CONFIG_SFP is not set
+# CONFIG_SF_PDMA is not set
+# CONFIG_SGETMASK_SYSCALL is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_SGI_IP22 is not set
+# CONFIG_SGI_IP27 is not set
+# CONFIG_SGI_IP28 is not set
+# CONFIG_SGI_IP30 is not set
+# CONFIG_SGI_IP32 is not set
+# CONFIG_SGI_MFD_IOC3 is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SG_POOL is not set
+# CONFIG_SG_SPLIT is not set
+CONFIG_SHMEM=y
+# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set
+# CONFIG_SH_ETH is not set
+# CONFIG_SH_TIMER_CMT is not set
+# CONFIG_SH_TIMER_MTU2 is not set
+# CONFIG_SH_TIMER_TMU is not set
+# CONFIG_SI1133 is not set
+# CONFIG_SI1145 is not set
+# CONFIG_SI7005 is not set
+# CONFIG_SI7020 is not set
+# CONFIG_SIBYTE_BIGSUR is not set
+# CONFIG_SIBYTE_CARMEL is not set
+# CONFIG_SIBYTE_CRHINE is not set
+# CONFIG_SIBYTE_CRHONE is not set
+# CONFIG_SIBYTE_LITTLESUR is not set
+# CONFIG_SIBYTE_RHONE is not set
+# CONFIG_SIBYTE_SENTOSA is not set
+# CONFIG_SIBYTE_SWARM is not set
+CONFIG_SIGNALFD=y
+# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set
+# CONFIG_SIMPLE_GPIO is not set
+# CONFIG_SIMPLE_PM_BUS is not set
+# CONFIG_SIOX is not set
+# CONFIG_SIS190 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_SKGE is not set
+# CONFIG_SKY2 is not set
+# CONFIG_SKY2_DEBUG is not set
+# CONFIG_SLAB is not set
+CONFIG_SLABINFO=y
+CONFIG_SLAB_FREELIST_HARDENED=y
+CONFIG_SLAB_FREELIST_RANDOM=y
+CONFIG_SLAB_MERGE_DEFAULT=y
+# CONFIG_SLHC is not set
+# CONFIG_SLICOSS is not set
+# CONFIG_SLIMBUS is not set
+# CONFIG_SLIP is not set
+# CONFIG_SLOB is not set
+CONFIG_SLUB=y
+CONFIG_SLUB_CPU_PARTIAL=y
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_MEMCG_SYSFS_ON is not set
+# CONFIG_SLUB_STATS is not set
+# CONFIG_SMARTJOYPLUS_FF is not set
+# CONFIG_SMB_SERVER is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMC9194 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_SMP is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_SMSC9420 is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_SMS_SDIO_DRV is not set
+# CONFIG_SMS_USB_DRV is not set
+# CONFIG_SM_CAMCC_8250 is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_SM_GCC_6115 is not set
+# CONFIG_SM_GCC_6125 is not set
+# CONFIG_SM_GCC_6350 is not set
+# CONFIG_SM_GCC_8350 is not set
+# CONFIG_SND is not set
+# CONFIG_SND_AC97_POWER_SAVE is not set
+# CONFIG_SND_AD1816A is not set
+# CONFIG_SND_AD1848 is not set
+# CONFIG_SND_AD1889 is not set
+# CONFIG_SND_ADLIB is not set
+# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ALOOP is not set
+# CONFIG_SND_ALS100 is not set
+# CONFIG_SND_ALS300 is not set
+# CONFIG_SND_ALS4000 is not set
+# CONFIG_SND_ARM is not set
+# CONFIG_SND_ASIHPI is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_ATIIXP_MODEM is not set
+# CONFIG_SND_ATMEL_AC97C is not set
+# CONFIG_SND_ATMEL_SOC is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
+# CONFIG_SND_AUDIO_GRAPH_CARD is not set
+# CONFIG_SND_AUDIO_GRAPH_SCU_CARD is not set
+# CONFIG_SND_AW2 is not set
+# CONFIG_SND_AZT2320 is not set
+# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BCD2000 is not set
+# CONFIG_SND_BCM63XX_I2S_WHISTLER is not set
+# CONFIG_SND_BT87X is not set
+# CONFIG_SND_CA0106 is not set
+# CONFIG_SND_CMI8330 is not set
+# CONFIG_SND_CMIPCI is not set
+# CONFIG_SND_CS4231 is not set
+# CONFIG_SND_CS4236 is not set
+# CONFIG_SND_CS4281 is not set
+# CONFIG_SND_CS46XX is not set
+# CONFIG_SND_CS5530 is not set
+# CONFIG_SND_CS5535AUDIO is not set
+# CONFIG_SND_CTXFI is not set
+# CONFIG_SND_DARLA20 is not set
+# CONFIG_SND_DARLA24 is not set
+# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_DESIGNWARE_I2S is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+# CONFIG_SND_ECHO3G is not set
+# CONFIG_SND_EDMA_SOC is not set
+# CONFIG_SND_EMU10K1 is not set
+# CONFIG_SND_EMU10K1X is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+# CONFIG_SND_ENS1370 is not set
+# CONFIG_SND_ENS1371 is not set
+# CONFIG_SND_ES1688 is not set
+# CONFIG_SND_ES18XX is not set
+# CONFIG_SND_ES1938 is not set
+# CONFIG_SND_ES1968 is not set
+# CONFIG_SND_FIREWIRE is not set
+# CONFIG_SND_FM801 is not set
+# CONFIG_SND_GINA20 is not set
+# CONFIG_SND_GINA24 is not set
+# CONFIG_SND_GUSCLASSIC is not set
+# CONFIG_SND_GUSEXTREME is not set
+# CONFIG_SND_GUSMAX is not set
+# CONFIG_SND_HDA_CODEC_CS8409 is not set
+# CONFIG_SND_HDA_INTEL is not set
+# CONFIG_SND_HDA_INTEL_DETECT_DMIC is not set
+# CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM is not set
+CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0
+CONFIG_SND_HDA_PREALLOC_SIZE=64
+# CONFIG_SND_HDSP is not set
+# CONFIG_SND_HDSPM is not set
+# CONFIG_SND_HRTIMER is not set
+# CONFIG_SND_HWDEP is not set
+# CONFIG_SND_I2S_HI6210_I2S is not set
+# CONFIG_SND_ICE1712 is not set
+# CONFIG_SND_ICE1724 is not set
+# CONFIG_SND_INDIGO is not set
+# CONFIG_SND_INDIGODJ is not set
+# CONFIG_SND_INDIGODJX is not set
+# CONFIG_SND_INDIGOIO is not set
+# CONFIG_SND_INDIGOIOX is not set
+# CONFIG_SND_INTEL8X0 is not set
+# CONFIG_SND_INTEL8X0M is not set
+# CONFIG_SND_INTERWAVE is not set
+# CONFIG_SND_INTERWAVE_STB is not set
+# CONFIG_SND_ISA is not set
+# CONFIG_SND_JZ4740_SOC_I2S is not set
+# CONFIG_SND_KIRKWOOD_SOC is not set
+# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_LAYLA20 is not set
+# CONFIG_SND_LAYLA24 is not set
+# CONFIG_SND_LOLA is not set
+# CONFIG_SND_LX6464ES is not set
+# CONFIG_SND_MAESTRO3 is not set
+CONFIG_SND_MAX_CARDS=16
+# CONFIG_SND_MIA is not set
+# CONFIG_SND_MIPS is not set
+# CONFIG_SND_MIRO is not set
+# CONFIG_SND_MIXART is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_MONA is not set
+# CONFIG_SND_MPC52xx_SOC_EFIKA is not set
+# CONFIG_SND_MPU401 is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_MTS64 is not set
+# CONFIG_SND_MXS_SOC is not set
+# CONFIG_SND_NM256 is not set
+# CONFIG_SND_OPL3SA2 is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_OPTI92X_AD1848 is not set
+# CONFIG_SND_OPTI92X_CS4231 is not set
+# CONFIG_SND_OPTI93X is not set
+CONFIG_SND_OSSEMUL=y
+# CONFIG_SND_OXYGEN is not set
+CONFIG_SND_PCI=y
+# CONFIG_SND_PCM is not set
+# CONFIG_SND_PCMCIA is not set
+# CONFIG_SND_PCM_OSS is not set
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_PCM_TIMER is not set
+# CONFIG_SND_PCM_XRUN_DEBUG is not set
+# CONFIG_SND_PCXHR is not set
+# CONFIG_SND_PDAUDIOCF is not set
+# CONFIG_SND_PORTMAN2X4 is not set
+# CONFIG_SND_POWERPC_SOC is not set
+# CONFIG_SND_PPC is not set
+CONFIG_SND_PROC_FS=y
+# CONFIG_SND_RAWMIDI is not set
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_RIPTIDE is not set
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_RTCTIMER is not set
+# CONFIG_SND_SB16 is not set
+# CONFIG_SND_SB8 is not set
+# CONFIG_SND_SBAWE is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_SE6X is not set
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_SIMPLE_CARD is not set
+# CONFIG_SND_SIMPLE_SCU_CARD is not set
+# CONFIG_SND_SIS7019 is not set
+# CONFIG_SND_SOC is not set
+# CONFIG_SND_SOC_AC97_CODEC is not set
+# CONFIG_SND_SOC_AD193X_I2C is not set
+# CONFIG_SND_SOC_AD193X_SPI is not set
+# CONFIG_SND_SOC_ADAU1372_I2C is not set
+# CONFIG_SND_SOC_ADAU1372_SPI is not set
+# CONFIG_SND_SOC_ADAU1701 is not set
+# CONFIG_SND_SOC_ADAU1761_I2C is not set
+# CONFIG_SND_SOC_ADAU1761_SPI is not set
+# CONFIG_SND_SOC_ADAU7002 is not set
+# CONFIG_SND_SOC_ADAU7118_HW is not set
+# CONFIG_SND_SOC_ADAU7118_I2C is not set
+# CONFIG_SND_SOC_ADI is not set
+# CONFIG_SND_SOC_AK4104 is not set
+# CONFIG_SND_SOC_AK4118 is not set
+# CONFIG_SND_SOC_AK4458 is not set
+# CONFIG_SND_SOC_AK4554 is not set
+# CONFIG_SND_SOC_AK4613 is not set
+# CONFIG_SND_SOC_AK4642 is not set
+# CONFIG_SND_SOC_AK5386 is not set
+# CONFIG_SND_SOC_AK5558 is not set
+# CONFIG_SND_SOC_ALC5623 is not set
+# CONFIG_SND_SOC_AMD_ACP is not set
+# CONFIG_SND_SOC_AMD_ACP3x is not set
+# CONFIG_SND_SOC_AMD_ACP5x is not set
+# CONFIG_SND_SOC_AMD_RENOIR is not set
+# CONFIG_SND_SOC_AU1XAUDIO is not set
+# CONFIG_SND_SOC_AU1XPSC is not set
+# CONFIG_SND_SOC_BD28623 is not set
+# CONFIG_SND_SOC_BT_SCO is not set
+# CONFIG_SND_SOC_CS35L32 is not set
+# CONFIG_SND_SOC_CS35L33 is not set
+# CONFIG_SND_SOC_CS35L34 is not set
+# CONFIG_SND_SOC_CS35L35 is not set
+# CONFIG_SND_SOC_CS35L36 is not set
+# CONFIG_SND_SOC_CS4234 is not set
+# CONFIG_SND_SOC_CS4265 is not set
+# CONFIG_SND_SOC_CS4270 is not set
+# CONFIG_SND_SOC_CS4271 is not set
+# CONFIG_SND_SOC_CS4271_I2C is not set
+# CONFIG_SND_SOC_CS4271_SPI is not set
+# CONFIG_SND_SOC_CS42L42 is not set
+# CONFIG_SND_SOC_CS42L51_I2C is not set
+# CONFIG_SND_SOC_CS42L52 is not set
+# CONFIG_SND_SOC_CS42L56 is not set
+# CONFIG_SND_SOC_CS42L73 is not set
+# CONFIG_SND_SOC_CS42XX8_I2C is not set
+# CONFIG_SND_SOC_CS43130 is not set
+# CONFIG_SND_SOC_CS4341 is not set
+# CONFIG_SND_SOC_CS4349 is not set
+# CONFIG_SND_SOC_CS53L30 is not set
+# CONFIG_SND_SOC_CX2072X is not set
+# CONFIG_SND_SOC_DA7213 is not set
+# CONFIG_SND_SOC_DIO2125 is not set
+# CONFIG_SND_SOC_DMIC is not set
+# CONFIG_SND_SOC_ES7134 is not set
+# CONFIG_SND_SOC_ES7241 is not set
+# CONFIG_SND_SOC_ES8316 is not set
+# CONFIG_SND_SOC_ES8328 is not set
+# CONFIG_SND_SOC_ES8328_I2C is not set
+# CONFIG_SND_SOC_ES8328_SPI is not set
+# CONFIG_SND_SOC_EUKREA_TLV320 is not set
+# CONFIG_SND_SOC_FSL_ASOC_CARD is not set
+# CONFIG_SND_SOC_FSL_ASRC is not set
+# CONFIG_SND_SOC_FSL_AUD2HTX is not set
+# CONFIG_SND_SOC_FSL_AUDMIX is not set
+# CONFIG_SND_SOC_FSL_ESAI is not set
+# CONFIG_SND_SOC_FSL_MICFIL is not set
+# CONFIG_SND_SOC_FSL_RPMSG is not set
+# CONFIG_SND_SOC_FSL_SAI is not set
+# CONFIG_SND_SOC_FSL_SPDIF is not set
+# CONFIG_SND_SOC_FSL_SSI is not set
+# CONFIG_SND_SOC_FSL_XCVR is not set
+# CONFIG_SND_SOC_GTM601 is not set
+# CONFIG_SND_SOC_ICS43432 is not set
+# CONFIG_SND_SOC_IMG is not set
+# CONFIG_SND_SOC_IMX_AUDMIX is not set
+# CONFIG_SND_SOC_IMX_AUDMUX is not set
+# CONFIG_SND_SOC_IMX_CARD is not set
+# CONFIG_SND_SOC_IMX_ES8328 is not set
+# CONFIG_SND_SOC_IMX_HDMI is not set
+# CONFIG_SND_SOC_IMX_RPMSG is not set
+# CONFIG_SND_SOC_IMX_SPDIF is not set
+# CONFIG_SND_SOC_IMX_WM8962 is not set
+# CONFIG_SND_SOC_INNO_RK3036 is not set
+# CONFIG_SND_SOC_INTEL_APL is not set
+# CONFIG_SND_SOC_INTEL_BAYTRAIL is not set
+# CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH is not set
+# CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH is not set
+# CONFIG_SND_SOC_INTEL_BXT_RT298_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH is not set
+# CONFIG_SND_SOC_INTEL_CATPT is not set
+# CONFIG_SND_SOC_INTEL_CFL is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH is not set
+# CONFIG_SND_SOC_INTEL_CML_H is not set
+# CONFIG_SND_SOC_INTEL_CML_LP is not set
+# CONFIG_SND_SOC_INTEL_CNL is not set
+# CONFIG_SND_SOC_INTEL_GLK is not set
+# CONFIG_SND_SOC_INTEL_HASWELL is not set
+# CONFIG_SND_SOC_INTEL_KBL is not set
+# CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH is not set
+# CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH is not set
+# CONFIG_SND_SOC_INTEL_KEEMBAY is not set
+# CONFIG_SND_SOC_INTEL_SKL is not set
+# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH is not set
+# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH is not set
+# CONFIG_SND_SOC_INTEL_SKL_RT286_MACH is not set
+# CONFIG_SND_SOC_INTEL_SKYLAKE is not set
+# CONFIG_SND_SOC_INTEL_SST is not set
+CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y
+# CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES is not set
+# CONFIG_SND_SOC_JZ4725B_CODEC is not set
+# CONFIG_SND_SOC_JZ4740_CODEC is not set
+# CONFIG_SND_SOC_JZ4770_CODEC is not set
+# CONFIG_SND_SOC_LPASS_RX_MACRO is not set
+# CONFIG_SND_SOC_LPASS_TX_MACRO is not set
+# CONFIG_SND_SOC_LPASS_VA_MACRO is not set
+# CONFIG_SND_SOC_LPASS_WSA_MACRO is not set
+# CONFIG_SND_SOC_MA120X0P is not set
+# CONFIG_SND_SOC_MAX9759 is not set
+# CONFIG_SND_SOC_MAX98088 is not set
+# CONFIG_SND_SOC_MAX98357A is not set
+# CONFIG_SND_SOC_MAX98373 is not set
+# CONFIG_SND_SOC_MAX98373_I2C is not set
+# CONFIG_SND_SOC_MAX98390 is not set
+# CONFIG_SND_SOC_MAX98504 is not set
+# CONFIG_SND_SOC_MAX9860 is not set
+# CONFIG_SND_SOC_MAX9867 is not set
+# CONFIG_SND_SOC_MAX98927 is not set
+# CONFIG_SND_SOC_MEDIATEK is not set
+# CONFIG_SND_SOC_MPC5200_AC97 is not set
+# CONFIG_SND_SOC_MPC5200_I2S is not set
+# CONFIG_SND_SOC_MSM8916_WCD_ANALOG is not set
+# CONFIG_SND_SOC_MSM8916_WCD_DIGITAL is not set
+# CONFIG_SND_SOC_MT2701 is not set
+# CONFIG_SND_SOC_MT6351 is not set
+# CONFIG_SND_SOC_MT6358 is not set
+# CONFIG_SND_SOC_MT6359 is not set
+# CONFIG_SND_SOC_MT6359_ACCDET is not set
+# CONFIG_SND_SOC_MT6660 is not set
+# CONFIG_SND_SOC_MT6797 is not set
+# CONFIG_SND_SOC_MT8173 is not set
+# CONFIG_SND_SOC_MT8183 is not set
+# CONFIG_SND_SOC_MT8192 is not set
+# CONFIG_SND_SOC_MT8195 is not set
+# CONFIG_SND_SOC_MTK_BTCVSD is not set
+# CONFIG_SND_SOC_NAU8315 is not set
+# CONFIG_SND_SOC_NAU8540 is not set
+# CONFIG_SND_SOC_NAU8810 is not set
+# CONFIG_SND_SOC_NAU8822 is not set
+# CONFIG_SND_SOC_NAU8824 is not set
+# CONFIG_SND_SOC_PCM1681 is not set
+# CONFIG_SND_SOC_PCM1789_I2C is not set
+# CONFIG_SND_SOC_PCM1792A is not set
+# CONFIG_SND_SOC_PCM179X_I2C is not set
+# CONFIG_SND_SOC_PCM179X_SPI is not set
+# CONFIG_SND_SOC_PCM186X_I2C is not set
+# CONFIG_SND_SOC_PCM186X_SPI is not set
+# CONFIG_SND_SOC_PCM3060_I2C is not set
+# CONFIG_SND_SOC_PCM3060_SPI is not set
+# CONFIG_SND_SOC_PCM3168A_I2C is not set
+# CONFIG_SND_SOC_PCM3168A_SPI is not set
+# CONFIG_SND_SOC_PCM5102A is not set
+# CONFIG_SND_SOC_PCM512x_I2C is not set
+# CONFIG_SND_SOC_PCM512x_SPI is not set
+# CONFIG_SND_SOC_QCOM is not set
+# CONFIG_SND_SOC_RK3328 is not set
+# CONFIG_SND_SOC_RK817 is not set
+# CONFIG_SND_SOC_ROCKCHIP is not set
+# CONFIG_SND_SOC_RT5616 is not set
+# CONFIG_SND_SOC_RT5631 is not set
+# CONFIG_SND_SOC_RT5640 is not set
+# CONFIG_SND_SOC_RT5659 is not set
+# CONFIG_SND_SOC_RT5677_SPI is not set
+# CONFIG_SND_SOC_SGTL5000 is not set
+# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set
+# CONFIG_SND_SOC_SIMPLE_MUX is not set
+# CONFIG_SND_SOC_SIRF_AUDIO_CODEC is not set
+# CONFIG_SND_SOC_SOF_TOPLEVEL is not set
+# CONFIG_SND_SOC_SPDIF is not set
+# CONFIG_SND_SOC_SSM2305 is not set
+# CONFIG_SND_SOC_SSM2518 is not set
+# CONFIG_SND_SOC_SSM2602_I2C is not set
+# CONFIG_SND_SOC_SSM2602_SPI is not set
+# CONFIG_SND_SOC_SSM4567 is not set
+# CONFIG_SND_SOC_STA32X is not set
+# CONFIG_SND_SOC_STA350 is not set
+# CONFIG_SND_SOC_STI_SAS is not set
+# CONFIG_SND_SOC_TAS2552 is not set
+# CONFIG_SND_SOC_TAS2562 is not set
+# CONFIG_SND_SOC_TAS2764 is not set
+# CONFIG_SND_SOC_TAS2770 is not set
+# CONFIG_SND_SOC_TAS5086 is not set
+# CONFIG_SND_SOC_TAS571X is not set
+# CONFIG_SND_SOC_TAS5720 is not set
+# CONFIG_SND_SOC_TAS6424 is not set
+# CONFIG_SND_SOC_TDA7419 is not set
+# CONFIG_SND_SOC_TFA9879 is not set
+# CONFIG_SND_SOC_TFA989X is not set
+# CONFIG_SND_SOC_TLV320ADCX140 is not set
+# CONFIG_SND_SOC_TLV320AIC23_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC23_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC31XX is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC3X is not set
+# CONFIG_SND_SOC_TLV320AIC3X_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC3X_SPI is not set
+# CONFIG_SND_SOC_TPA6130A2 is not set
+# CONFIG_SND_SOC_TS3A227E is not set
+# CONFIG_SND_SOC_TSCS42XX is not set
+# CONFIG_SND_SOC_TSCS454 is not set
+# CONFIG_SND_SOC_UDA1334 is not set
+# CONFIG_SND_SOC_WM8510 is not set
+# CONFIG_SND_SOC_WM8523 is not set
+# CONFIG_SND_SOC_WM8524 is not set
+# CONFIG_SND_SOC_WM8580 is not set
+# CONFIG_SND_SOC_WM8711 is not set
+# CONFIG_SND_SOC_WM8728 is not set
+# CONFIG_SND_SOC_WM8731 is not set
+# CONFIG_SND_SOC_WM8737 is not set
+# CONFIG_SND_SOC_WM8741 is not set
+# CONFIG_SND_SOC_WM8750 is not set
+# CONFIG_SND_SOC_WM8753 is not set
+# CONFIG_SND_SOC_WM8770 is not set
+# CONFIG_SND_SOC_WM8776 is not set
+# CONFIG_SND_SOC_WM8782 is not set
+# CONFIG_SND_SOC_WM8804_I2C is not set
+# CONFIG_SND_SOC_WM8804_SPI is not set
+# CONFIG_SND_SOC_WM8903 is not set
+# CONFIG_SND_SOC_WM8904 is not set
+# CONFIG_SND_SOC_WM8960 is not set
+# CONFIG_SND_SOC_WM8962 is not set
+# CONFIG_SND_SOC_WM8974 is not set
+# CONFIG_SND_SOC_WM8978 is not set
+# CONFIG_SND_SOC_WM8985 is not set
+# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set
+# CONFIG_SND_SOC_XILINX_I2S is not set
+# CONFIG_SND_SOC_XILINX_SPDIF is not set
+# CONFIG_SND_SOC_XTFPGA_I2S is not set
+# CONFIG_SND_SOC_ZL38060 is not set
+# CONFIG_SND_SOC_ZX_AUD96P22 is not set
+# CONFIG_SND_SONICVIBES is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SND_SSCAPE is not set
+# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI is not set
+# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set
+# CONFIG_SND_SUN4I_CODEC is not set
+# CONFIG_SND_SUPPORT_OLD_API is not set
+# CONFIG_SND_TIMER is not set
+# CONFIG_SND_TRIDENT is not set
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_6FIRE is not set
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+# CONFIG_SND_USB_HIFACE is not set
+# CONFIG_SND_USB_POD is not set
+# CONFIG_SND_USB_PODHD is not set
+# CONFIG_SND_USB_TONEPORT is not set
+# CONFIG_SND_USB_UA101 is not set
+# CONFIG_SND_USB_US122L is not set
+# CONFIG_SND_USB_USX2Y is not set
+# CONFIG_SND_USB_VARIAX is not set
+# CONFIG_SND_VERBOSE_PRINTK is not set
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VIA82XX is not set
+# CONFIG_SND_VIA82XX_MODEM is not set
+# CONFIG_SND_VIRTIO is not set
+# CONFIG_SND_VIRTUOSO is not set
+# CONFIG_SND_VX222 is not set
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_WAVEFRONT is not set
+CONFIG_SND_X86=y
+# CONFIG_SND_XEN_FRONTEND is not set
+# CONFIG_SND_YMFPCI is not set
+# CONFIG_SNI_RM is not set
+# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set
+# CONFIG_SOCK_CGROUP_DATA is not set
+# CONFIG_SOC_AM33XX is not set
+# CONFIG_SOC_AM43XX is not set
+# CONFIG_SOC_BRCMSTB is not set
+# CONFIG_SOC_CAMERA is not set
+# CONFIG_SOC_DRA7XX is not set
+# CONFIG_SOC_HAS_OMAP2_SDRC is not set
+# CONFIG_SOC_OMAP5 is not set
+# CONFIG_SOC_TI is not set
+# CONFIG_SOFTLOCKUP_DETECTOR is not set
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_SONYPI is not set
+# CONFIG_SONY_LAPTOP is not set
+# CONFIG_SOUND is not set
+# CONFIG_SOUNDWIRE is not set
+# CONFIG_SOUND_OSS_CORE is not set
+# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set
+# CONFIG_SOUND_PRIME is not set
+# CONFIG_SP5100_TCO is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+# CONFIG_SPARSE_IRQ is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_SPEAKUP is not set
+# CONFIG_SPI is not set
+# CONFIG_SPINLOCK_TEST is not set
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_AMD is not set
+# CONFIG_SPI_AU1550 is not set
+# CONFIG_SPI_AXI_SPI_ENGINE is not set
+# CONFIG_SPI_BCM2835 is not set
+# CONFIG_SPI_BCM_QSPI is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_BUTTERFLY is not set
+# CONFIG_SPI_CADENCE is not set
+# CONFIG_SPI_CADENCE_QUADSPI is not set
+# CONFIG_SPI_DEBUG is not set
+# CONFIG_SPI_DESIGNWARE is not set
+# CONFIG_SPI_FSL_DSPI is not set
+# CONFIG_SPI_FSL_ESPI is not set
+# CONFIG_SPI_FSL_SPI is not set
+# CONFIG_SPI_GPIO is not set
+# CONFIG_SPI_GPIO_OLD is not set
+# CONFIG_SPI_IMG_SPFI is not set
+# CONFIG_SPI_LANTIQ_SSC is not set
+# CONFIG_SPI_LM70_LLP is not set
+# CONFIG_SPI_LOOPBACK_TEST is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_SPI_MEM is not set
+# CONFIG_SPI_MPC52xx is not set
+# CONFIG_SPI_MPC52xx_PSC is not set
+# CONFIG_SPI_MTK_QUADSPI is not set
+# CONFIG_SPI_MUX is not set
+# CONFIG_SPI_MXIC is not set
+# CONFIG_SPI_NXP_FLEXSPI is not set
+# CONFIG_SPI_OCTEON is not set
+# CONFIG_SPI_OC_TINY is not set
+# CONFIG_SPI_ORION is not set
+# CONFIG_SPI_PL022 is not set
+# CONFIG_SPI_PPC4xx is not set
+# CONFIG_SPI_PXA2XX is not set
+# CONFIG_SPI_PXA2XX_PCI is not set
+# CONFIG_SPI_QCOM_QSPI is not set
+# CONFIG_SPI_ROCKCHIP is not set
+# CONFIG_SPI_S3C64XX is not set
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_SIFIVE is not set
+# CONFIG_SPI_SLAVE is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_THUNDERX is not set
+# CONFIG_SPI_TI_QSPI is not set
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_SPI_TOPCLIFF_PCH is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_XWAY is not set
+# CONFIG_SPI_ZYNQMP_GQSPI is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_SPMI is not set
+# CONFIG_SPS30 is not set
+# CONFIG_SPS30_I2C is not set
+# CONFIG_SPS30_SERIAL is not set
+CONFIG_SQUASHFS=y
+# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set
+# CONFIG_SQUASHFS_DECOMP_MULTI is not set
+CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
+# CONFIG_SQUASHFS_DECOMP_SINGLE is not set
+CONFIG_SQUASHFS_EMBEDDED=y
+# CONFIG_SQUASHFS_FILE_CACHE is not set
+CONFIG_SQUASHFS_FILE_DIRECT=y
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_SQUASHFS_LZ4 is not set
+# CONFIG_SQUASHFS_LZO is not set
+# CONFIG_SQUASHFS_XATTR is not set
+CONFIG_SQUASHFS_XZ=y
+# CONFIG_SQUASHFS_ZLIB is not set
+# CONFIG_SQUASHFS_ZSTD is not set
+# CONFIG_SRAM is not set
+# CONFIG_SRF04 is not set
+# CONFIG_SRF08 is not set
+# CONFIG_SSB is not set
+# CONFIG_SSB_DEBUG is not set
+# CONFIG_SSB_DRIVER_GPIO is not set
+# CONFIG_SSB_HOST_SOC is not set
+# CONFIG_SSB_PCMCIAHOST is not set
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB_SDIOHOST is not set
+# CONFIG_SSB_SILENT is not set
+# CONFIG_SSFDC is not set
+# CONFIG_STACKPROTECTOR is not set
+# CONFIG_STACKPROTECTOR_STRONG is not set
+# CONFIG_STACKTRACE is not set
+# CONFIG_STACKTRACE_BUILD_ID is not set
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_STACK_HASH_ORDER=20
+# CONFIG_STACK_TRACER is not set
+# CONFIG_STACK_VALIDATION is not set
+CONFIG_STAGING=y
+# CONFIG_STAGING_BOARD is not set
+# CONFIG_STAGING_GASKET_FRAMEWORK is not set
+# CONFIG_STAGING_MEDIA is not set
+CONFIG_STANDALONE=y
+# CONFIG_STATIC_KEYS_SELFTEST is not set
+# CONFIG_STATIC_USERMODEHELPER is not set
+CONFIG_STDBINUTILS=y
+# CONFIG_STE10XP is not set
+# CONFIG_STE_MODEM_RPROC is not set
+# CONFIG_STK3310 is not set
+# CONFIG_STK8312 is not set
+# CONFIG_STK8BA50 is not set
+# CONFIG_STM is not set
+# CONFIG_STMMAC_ETH is not set
+# CONFIG_STMMAC_PCI is not set
+# CONFIG_STMMAC_PLATFORM is not set
+# CONFIG_STMMAC_SELFTESTS is not set
+# CONFIG_STM_DUMMY is not set
+# CONFIG_STM_SOURCE_CONSOLE is not set
+CONFIG_STP=y
+# CONFIG_STREAM_PARSER is not set
+# CONFIG_STRICT_DEVMEM is not set
+CONFIG_STRICT_KERNEL_RWX=y
+CONFIG_STRICT_MODULE_RWX=y
+# CONFIG_STRING_SELFTEST is not set
+CONFIG_STRIP_ASM_SYMS=y
+# CONFIG_STX104 is not set
+# CONFIG_ST_UVIS25 is not set
+# CONFIG_SUN4I_GPADC is not set
+# CONFIG_SUN50I_DE2_BUS is not set
+# CONFIG_SUN50I_ERRATUM_UNKNOWN1 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_SUNRPC is not set
+# CONFIG_SUNRPC_DEBUG is not set
+CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES=y
+# CONFIG_SUNRPC_GSS is not set
+# CONFIG_SUNXI_SRAM is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_SURFACE_3_BUTTON is not set
+# CONFIG_SUSPEND is not set
+# CONFIG_SUSPEND_SKIP_SYNC is not set
+CONFIG_SWAP=y
+# CONFIG_SWCONFIG is not set
+# CONFIG_SWCONFIG_B53 is not set
+# CONFIG_SWCONFIG_B53_MDIO_DRIVER is not set
+# CONFIG_SWCONFIG_B53_MMAP_DRIVER is not set
+# CONFIG_SWCONFIG_B53_SPI_DRIVER is not set
+# CONFIG_SWCONFIG_B53_SRAB_DRIVER is not set
+# CONFIG_SWCONFIG_LEDS is not set
+# CONFIG_SW_SYNC is not set
+# CONFIG_SX9310 is not set
+# CONFIG_SX9500 is not set
+# CONFIG_SXGBE_ETH is not set
+CONFIG_SYMBOLIC_ERRNAME=y
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_SYNC_FILE is not set
+# CONFIG_SYNOPSYS_DWC_ETH_QOS is not set
+# CONFIG_SYNTH_EVENTS is not set
+# CONFIG_SYNTH_EVENT_GEN_TEST is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_SYSCON_REBOOT_MODE is not set
+CONFIG_SYSCTL=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_SYSFS=y
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+# CONFIG_SYSFS_SYSCALL is not set
+# CONFIG_SYSTEMPORT is not set
+# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set
+# CONFIG_SYSTEM_DATA_VERIFICATION is not set
+# CONFIG_SYSTEM_TRUSTED_KEYRING is not set
+CONFIG_SYSTEM_TRUSTED_KEYS=""
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_T5403 is not set
+# CONFIG_TARGET_CORE is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_TASKS_RCU is not set
+# CONFIG_TASK_XACCT is not set
+# CONFIG_TC35815 is not set
+# CONFIG_TCG_ATMEL is not set
+# CONFIG_TCG_CRB is not set
+# CONFIG_TCG_FTPM_TEE is not set
+# CONFIG_TCG_INFINEON is not set
+# CONFIG_TCG_NSC is not set
+# CONFIG_TCG_ST33_I2C is not set
+# CONFIG_TCG_TIS is not set
+# CONFIG_TCG_TIS_I2C_ATMEL is not set
+# CONFIG_TCG_TIS_I2C_CR50 is not set
+# CONFIG_TCG_TIS_I2C_INFINEON is not set
+# CONFIG_TCG_TIS_I2C_NUVOTON is not set
+# CONFIG_TCG_TIS_SPI is not set
+# CONFIG_TCG_TIS_ST33ZP24_I2C is not set
+# CONFIG_TCG_TIS_ST33ZP24_SPI is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_TCG_VTPM_PROXY is not set
+# CONFIG_TCG_XEN is not set
+# CONFIG_TCIC is not set
+CONFIG_TCP_CONG_ADVANCED=y
+# CONFIG_TCP_CONG_BBR is not set
+# CONFIG_TCP_CONG_BIC is not set
+# CONFIG_TCP_CONG_CDG is not set
+CONFIG_TCP_CONG_CUBIC=y
+# CONFIG_TCP_CONG_DCTCP is not set
+# CONFIG_TCP_CONG_HSTCP is not set
+# CONFIG_TCP_CONG_HTCP is not set
+# CONFIG_TCP_CONG_HYBLA is not set
+# CONFIG_TCP_CONG_ILLINOIS is not set
+# CONFIG_TCP_CONG_LP is not set
+# CONFIG_TCP_CONG_NV is not set
+# CONFIG_TCP_CONG_SCALABLE is not set
+# CONFIG_TCP_CONG_VEGAS is not set
+# CONFIG_TCP_CONG_VENO is not set
+# CONFIG_TCP_CONG_WESTWOOD is not set
+# CONFIG_TCP_CONG_YEAH is not set
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_TCS3414 is not set
+# CONFIG_TCS3472 is not set
+# CONFIG_TEE is not set
+# CONFIG_TEGRA_AHB is not set
+# CONFIG_TEGRA_HOST1X is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_TERANETICS_PHY is not set
+# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
+# CONFIG_TEST_BITFIELD is not set
+# CONFIG_TEST_BITMAP is not set
+# CONFIG_TEST_BITOPS is not set
+# CONFIG_TEST_BLACKHOLE_DEV is not set
+# CONFIG_TEST_BPF is not set
+# CONFIG_TEST_CLOCKSOURCE_WATCHDOG is not set
+# CONFIG_TEST_DEBUG_VIRTUAL is not set
+# CONFIG_TEST_DIV64 is not set
+# CONFIG_TEST_FIRMWARE is not set
+# CONFIG_TEST_FREE_PAGES is not set
+# CONFIG_TEST_HASH is not set
+# CONFIG_TEST_HEXDUMP is not set
+# CONFIG_TEST_IDA is not set
+# CONFIG_TEST_KASAN_MODULE is not set
+# CONFIG_TEST_KMOD is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_TEST_LKM is not set
+# CONFIG_TEST_LOCKUP is not set
+# CONFIG_TEST_MEMCAT_P is not set
+# CONFIG_TEST_MEMINIT is not set
+# CONFIG_TEST_MIN_HEAP is not set
+# CONFIG_TEST_OVERFLOW is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_TEST_PRINTF is not set
+# CONFIG_TEST_RHASHTABLE is not set
+# CONFIG_TEST_SCANF is not set
+# CONFIG_TEST_SORT is not set
+# CONFIG_TEST_STACKINIT is not set
+# CONFIG_TEST_STATIC_KEYS is not set
+# CONFIG_TEST_STRING_HELPERS is not set
+# CONFIG_TEST_STRSCPY is not set
+# CONFIG_TEST_SYSCTL is not set
+# CONFIG_TEST_UBSAN is not set
+# CONFIG_TEST_UDELAY is not set
+# CONFIG_TEST_USER_COPY is not set
+# CONFIG_TEST_UUID is not set
+# CONFIG_TEST_VMALLOC is not set
+# CONFIG_TEST_XARRAY is not set
+CONFIG_TEXTSEARCH=y
+# CONFIG_TEXTSEARCH_BM is not set
+# CONFIG_TEXTSEARCH_FSM is not set
+# CONFIG_TEXTSEARCH_KMP is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
+# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_EMULATION is not set
+# CONFIG_THERMAL_GOV_BANG_BANG is not set
+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
+# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set
+# CONFIG_THERMAL_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_THERMAL_MMIO is not set
+# CONFIG_THERMAL_NETLINK is not set
+# CONFIG_THERMAL_STATISTICS is not set
+# CONFIG_THERMAL_WRITABLE_TRIPS is not set
+# CONFIG_THINKPAD_ACPI is not set
+CONFIG_THIN_ARCHIVES=y
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_THUMB2_KERNEL is not set
+# CONFIG_THUNDERBOLT is not set
+# CONFIG_THUNDER_NIC_BGX is not set
+# CONFIG_THUNDER_NIC_PF is not set
+# CONFIG_THUNDER_NIC_RGX is not set
+# CONFIG_THUNDER_NIC_VF is not set
+# CONFIG_TICK_CPU_ACCOUNTING is not set
+CONFIG_TICK_ONESHOT=y
+# CONFIG_TIFM_CORE is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_TIMERFD=y
+# CONFIG_TIMERLAT_TRACER is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_TIME_NS is not set
+# CONFIG_TINYDRM_HX8357D is not set
+# CONFIG_TINYDRM_ILI9225 is not set
+# CONFIG_TINYDRM_ILI9341 is not set
+# CONFIG_TINYDRM_ILI9486 is not set
+# CONFIG_TINYDRM_MI0283QT is not set
+# CONFIG_TINYDRM_REPAPER is not set
+# CONFIG_TINYDRM_ST7586 is not set
+# CONFIG_TINYDRM_ST7735R is not set
+CONFIG_TINY_RCU=y
+# CONFIG_TIPC is not set
+# CONFIG_TI_ADC081C is not set
+# CONFIG_TI_ADC0832 is not set
+# CONFIG_TI_ADC084S021 is not set
+# CONFIG_TI_ADC108S102 is not set
+# CONFIG_TI_ADC12138 is not set
+# CONFIG_TI_ADC128S052 is not set
+# CONFIG_TI_ADC161S626 is not set
+# CONFIG_TI_ADS1015 is not set
+# CONFIG_TI_ADS124S08 is not set
+# CONFIG_TI_ADS131E08 is not set
+# CONFIG_TI_ADS7950 is not set
+# CONFIG_TI_ADS8344 is not set
+# CONFIG_TI_ADS8688 is not set
+# CONFIG_TI_AM335X_ADC is not set
+# CONFIG_TI_CPSW is not set
+# CONFIG_TI_CPSW_ALE is not set
+# CONFIG_TI_CPSW_PHY_SEL is not set
+# CONFIG_TI_CPTS is not set
+# CONFIG_TI_DAC082S085 is not set
+# CONFIG_TI_DAC5571 is not set
+# CONFIG_TI_DAC7311 is not set
+# CONFIG_TI_DAC7512 is not set
+# CONFIG_TI_DAC7612 is not set
+# CONFIG_TI_DAVINCI_CPDMA is not set
+# CONFIG_TI_DAVINCI_MDIO is not set
+# CONFIG_TI_ST is not set
+# CONFIG_TI_SYSCON_RESET is not set
+# CONFIG_TI_TLC4541 is not set
+# CONFIG_TI_TSC2046 is not set
+# CONFIG_TLAN is not set
+# CONFIG_TLS is not set
+# CONFIG_TLS_DEVICE is not set
+# CONFIG_TLS_TOE is not set
+# CONFIG_TMD_HERMES is not set
+# CONFIG_TMP006 is not set
+# CONFIG_TMP007 is not set
+# CONFIG_TMP117 is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_INODE64 is not set
+# CONFIG_TMPFS_POSIX_ACL is not set
+CONFIG_TMPFS_XATTR=y
+# CONFIG_TOPSTAR_LAPTOP is not set
+# CONFIG_TORTURE_TEST is not set
+# CONFIG_TOSHIBA_HAPS is not set
+# CONFIG_TOUCHSCREEN_88PM860X is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_AD7879_I2C is not set
+# CONFIG_TOUCHSCREEN_AD7879_SPI is not set
+# CONFIG_TOUCHSCREEN_ADC is not set
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AR1021_I2C is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 is not set
+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_BU21029 is not set
+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set
+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8505 is not set
+# CONFIG_TOUCHSCREEN_COLIBRI_VF50 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_I2C is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_SPI is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_SPI is not set
+# CONFIG_TOUCHSCREEN_DA9034 is not set
+# CONFIG_TOUCHSCREEN_DA9052 is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_EGALAX is not set
+# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set
+# CONFIG_TOUCHSCREEN_EKTF2127 is not set
+# CONFIG_TOUCHSCREEN_ELAN is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_EXC3000 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GOODIX is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_HIDEEP is not set
+# CONFIG_TOUCHSCREEN_HP600 is not set
+# CONFIG_TOUCHSCREEN_HP7XX is not set
+# CONFIG_TOUCHSCREEN_HTCPEN is not set
+# CONFIG_TOUCHSCREEN_HYCON_HY46XX is not set
+# CONFIG_TOUCHSCREEN_ILI210X is not set
+# CONFIG_TOUCHSCREEN_ILITEK is not set
+# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_IPAQ_MICRO is not set
+# CONFIG_TOUCHSCREEN_IPROC is not set
+# CONFIG_TOUCHSCREEN_IQS5XX is not set
+# CONFIG_TOUCHSCREEN_LPC32XX is not set
+# CONFIG_TOUCHSCREEN_MAX11801 is not set
+# CONFIG_TOUCHSCREEN_MC13783 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set
+# CONFIG_TOUCHSCREEN_MIGOR is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_MMS114 is not set
+# CONFIG_TOUCHSCREEN_MSG2638 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MX25 is not set
+# CONFIG_TOUCHSCREEN_MXS_LRADC is not set
+# CONFIG_TOUCHSCREEN_PCAP is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_PROPERTIES is not set
+# CONFIG_TOUCHSCREEN_RASPBERRYPI_FW is not set
+# CONFIG_TOUCHSCREEN_RM_TS is not set
+# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set
+# CONFIG_TOUCHSCREEN_RPI_FT5406 is not set
+# CONFIG_TOUCHSCREEN_S3C2410 is not set
+# CONFIG_TOUCHSCREEN_S6SY761 is not set
+# CONFIG_TOUCHSCREEN_SILEAD is not set
+# CONFIG_TOUCHSCREEN_SIS_I2C is not set
+# CONFIG_TOUCHSCREEN_ST1232 is not set
+# CONFIG_TOUCHSCREEN_STMFTS is not set
+# CONFIG_TOUCHSCREEN_STMPE is not set
+# CONFIG_TOUCHSCREEN_SUN4I is not set
+# CONFIG_TOUCHSCREEN_SUR40 is not set
+# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set
+# CONFIG_TOUCHSCREEN_SX8654 is not set
+# CONFIG_TOUCHSCREEN_TI_AM335X_TSC is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+# CONFIG_TOUCHSCREEN_TS4800 is not set
+# CONFIG_TOUCHSCREEN_TSC2004 is not set
+# CONFIG_TOUCHSCREEN_TSC2005 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_TSC2007_IIO is not set
+# CONFIG_TOUCHSCREEN_TSC200X_CORE is not set
+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_3M is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_USB_DMC_TSC10 is not set
+# CONFIG_TOUCHSCREEN_USB_E2I is not set
+# CONFIG_TOUCHSCREEN_USB_EASYTOUCH is not set
+# CONFIG_TOUCHSCREEN_USB_EGALAX is not set
+# CONFIG_TOUCHSCREEN_USB_ELO is not set
+# CONFIG_TOUCHSCREEN_USB_ETT_TC45USB is not set
+# CONFIG_TOUCHSCREEN_USB_ETURBO is not set
+# CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH is not set
+# CONFIG_TOUCHSCREEN_USB_GOTOP is not set
+# CONFIG_TOUCHSCREEN_USB_GUNZE is not set
+# CONFIG_TOUCHSCREEN_USB_IDEALTEK is not set
+# CONFIG_TOUCHSCREEN_USB_IRTOUCH is not set
+# CONFIG_TOUCHSCREEN_USB_ITM is not set
+# CONFIG_TOUCHSCREEN_USB_JASTEC is not set
+# CONFIG_TOUCHSCREEN_USB_NEXIO is not set
+# CONFIG_TOUCHSCREEN_USB_PANJIT is not set
+# CONFIG_TOUCHSCREEN_USB_ZYTRONIC is not set
+# CONFIG_TOUCHSCREEN_W90X900 is not set
+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set
+# CONFIG_TOUCHSCREEN_WM831X is not set
+# CONFIG_TOUCHSCREEN_WM9705 is not set
+# CONFIG_TOUCHSCREEN_WM9712 is not set
+# CONFIG_TOUCHSCREEN_WM9713 is not set
+# CONFIG_TOUCHSCREEN_WM97XX is not set
+# CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE is not set
+# CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE is not set
+# CONFIG_TOUCHSCREEN_ZET6223 is not set
+# CONFIG_TOUCHSCREEN_ZFORCE is not set
+# CONFIG_TOUCHSCREEN_ZINITIX is not set
+# CONFIG_TPL0102 is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_TRACEPOINT_BENCHMARK is not set
+# CONFIG_TRACER_SNAPSHOT is not set
+# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set
+# CONFIG_TRACE_BRANCH_PROFILING is not set
+# CONFIG_TRACE_EVAL_MAP_FILE is not set
+# CONFIG_TRACE_EVENT_INJECT is not set
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_TRACE_SINK is not set
+# CONFIG_TRACING_EVENTS_GPIO is not set
+CONFIG_TRACING_SUPPORT=y
+CONFIG_TRAD_SIGNALS=y
+# CONFIG_TRANSPARENT_HUGEPAGE is not set
+# CONFIG_TREE_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_TRIM_UNUSED_KSYMS is not set
+# CONFIG_TRUSTED_FOUNDATIONS is not set
+# CONFIG_TRUSTED_KEYS is not set
+# CONFIG_TSL2583 is not set
+# CONFIG_TSL2591 is not set
+# CONFIG_TSL2772 is not set
+# CONFIG_TSL2x7x is not set
+# CONFIG_TSL4531 is not set
+# CONFIG_TSYS01 is not set
+# CONFIG_TSYS02D is not set
+# CONFIG_TTPCI_EEPROM is not set
+CONFIG_TTY=y
+# CONFIG_TTY_PRINTK is not set
+# CONFIG_TUN is not set
+# CONFIG_TUN_VNET_CROSS_LE is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_TWL4030_MADC is not set
+# CONFIG_TWL6030_GPADC is not set
+# CONFIG_TWL6040_CORE is not set
+# CONFIG_TYPEC is not set
+# CONFIG_TYPEC_TCPM is not set
+# CONFIG_TYPEC_UCSI is not set
+# CONFIG_TYPHOON is not set
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_UBIFS_ATIME_SUPPORT is not set
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+# CONFIG_UBIFS_FS_AUTHENTICATION is not set
+# CONFIG_UBIFS_FS_ENCRYPTION is not set
+CONFIG_UBIFS_FS_LZO=y
+# CONFIG_UBIFS_FS_SECURITY is not set
+CONFIG_UBIFS_FS_XATTR=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_UBIFS_FS_ZSTD=y
+# CONFIG_UBSAN is not set
+CONFIG_UBSAN_ALIGNMENT=y
+CONFIG_UBSAN_BOOL=y
+# CONFIG_UBSAN_DIV_ZERO is not set
+CONFIG_UBSAN_ENUM=y
+# CONFIG_UBSAN_MISC is not set
+CONFIG_UBSAN_SHIFT=y
+# CONFIG_UBSAN_UNREACHABLE is not set
+# CONFIG_UCB1400_CORE is not set
+# CONFIG_UCSI is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDMABUF is not set
+CONFIG_UEVENT_HELPER=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_UFS_FS is not set
+# CONFIG_UHID is not set
+CONFIG_UID16=y
+# CONFIG_UIO is not set
+# CONFIG_ULTRA is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_UNICODE is not set
+# CONFIG_UNISYSSPAR is not set
+# CONFIG_UNISYS_VISORBUS is not set
+CONFIG_UNIX=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_UNIX_DIAG is not set
+CONFIG_UNIX_SCM=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_UNWINDER_FRAME_POINTER is not set
+# CONFIG_UPROBES is not set
+# CONFIG_UPROBE_EVENTS is not set
+# CONFIG_US5182D is not set
+# CONFIG_USB is not set
+# CONFIG_USB4 is not set
+# CONFIG_USBIP_CORE is not set
+CONFIG_USBIP_VHCI_HC_PORTS=8
+CONFIG_USBIP_VHCI_NR_HCS=1
+# CONFIG_USBIP_VUDC is not set
+# CONFIG_USBPCWATCHDOG is not set
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_AIRSPY is not set
+CONFIG_USB_ALI_M5632=y
+# CONFIG_USB_AMD5536UDC is not set
+CONFIG_USB_AN2720=y
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARMLINUX=y
+# CONFIG_USB_ATM is not set
+# CONFIG_USB_AUDIO is not set
+CONFIG_USB_AUTOSUSPEND_DELAY=2
+# CONFIG_USB_BDC_UDC is not set
+CONFIG_USB_BELKIN=y
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+# CONFIG_USB_CDNS3 is not set
+# CONFIG_USB_CDNS_SUPPORT is not set
+# CONFIG_USB_CHAOSKEY is not set
+# CONFIG_USB_CHIPIDEA is not set
+# CONFIG_USB_CHIPIDEA_GENERIC is not set
+# CONFIG_USB_CHIPIDEA_IMX is not set
+# CONFIG_USB_CHIPIDEA_MSM is not set
+# CONFIG_USB_CHIPIDEA_PCI is not set
+# CONFIG_USB_CHIPIDEA_TEGRA is not set
+# CONFIG_USB_CONFIGFS is not set
+# CONFIG_USB_CONN_GPIO is not set
+# CONFIG_USB_CXACRU is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+CONFIG_USB_DEFAULT_PERSIST=y
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_DUMMY_HCD is not set
+# CONFIG_USB_DWC2 is not set
+# CONFIG_USB_DWC2_DEBUG is not set
+# CONFIG_USB_DWC2_DUAL_ROLE is not set
+# CONFIG_USB_DWC2_HOST is not set
+# CONFIG_USB_DWC2_PERIPHERAL is not set
+# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set
+# CONFIG_USB_DWC3 is not set
+# CONFIG_USB_DWC3_EXYNOS is not set
+# CONFIG_USB_DWC3_HAPS is not set
+# CONFIG_USB_DWC3_KEYSTONE is not set
+# CONFIG_USB_DWC3_OF_SIMPLE is not set
+# CONFIG_USB_DWC3_PCI is not set
+# CONFIG_USB_DWC3_QCOM is not set
+# CONFIG_USB_DWC3_ULPI is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_EG20T is not set
+# CONFIG_USB_EHCI_ATH79 is not set
+# CONFIG_USB_EHCI_FSL is not set
+# CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_EHCI_HCD_AT91 is not set
+# CONFIG_USB_EHCI_HCD_OMAP is not set
+# CONFIG_USB_EHCI_HCD_PPC_OF is not set
+# CONFIG_USB_EHCI_MSM is not set
+# CONFIG_USB_EHCI_MV is not set
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_TT_NEWSCHED=y
+# CONFIG_USB_EHSET_TEST_FIXTURE is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EPSON2888 is not set
+# CONFIG_USB_ETH is not set
+# CONFIG_USB_EZUSB_FX2 is not set
+# CONFIG_USB_FEW_INIT_RETRIES is not set
+# CONFIG_USB_FOTG210_HCD is not set
+# CONFIG_USB_FOTG210_UDC is not set
+# CONFIG_USB_FSL_USB2 is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_FUNCTIONFS is not set
+# CONFIG_USB_FUSB300 is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+# CONFIG_USB_GADGET_DEBUG_FS is not set
+CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
+CONFIG_USB_GADGET_VBUS_DRAW=2
+# CONFIG_USB_GADGET_XILINX is not set
+# CONFIG_USB_GL860 is not set
+# CONFIG_USB_GOKU is not set
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_USB_GR_UDC is not set
+# CONFIG_USB_GSPCA is not set
+# CONFIG_USB_GSPCA_BENQ is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_CPIA1 is not set
+# CONFIG_USB_GSPCA_DTCS033 is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_JEILINJ is not set
+# CONFIG_USB_GSPCA_JL2005BCD is not set
+# CONFIG_USB_GSPCA_KINECT is not set
+# CONFIG_USB_GSPCA_KONICA is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_MR97310A is not set
+# CONFIG_USB_GSPCA_NW80X is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_OV534 is not set
+# CONFIG_USB_GSPCA_OV534_9 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7302 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SE401 is not set
+# CONFIG_USB_GSPCA_SN9C2028 is not set
+# CONFIG_USB_GSPCA_SN9C20X is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA1528 is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_SQ905 is not set
+# CONFIG_USB_GSPCA_SQ905C is not set
+# CONFIG_USB_GSPCA_SQ930X is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_STK1135 is not set
+# CONFIG_USB_GSPCA_STV0680 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TOPRO is not set
+# CONFIG_USB_GSPCA_TOUPTEK is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_VICAM is not set
+# CONFIG_USB_GSPCA_XIRLINK_CIT is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_USB_G_ACM_MS is not set
+# CONFIG_USB_G_DBGP is not set
+# CONFIG_USB_G_HID is not set
+# CONFIG_USB_G_MULTI is not set
+# CONFIG_USB_G_NCM is not set
+# CONFIG_USB_G_NOKIA is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_G_WEBCAM is not set
+# CONFIG_USB_HACKRF is not set
+# CONFIG_USB_HCD_TEST_MODE is not set
+# CONFIG_USB_HID is not set
+# CONFIG_USB_HIDDEV is not set
+# CONFIG_USB_HSIC_USB3503 is not set
+# CONFIG_USB_HSIC_USB4604 is not set
+# CONFIG_USB_HSO is not set
+# CONFIG_USB_HUB_USB251XB is not set
+# CONFIG_USB_HWA_HCD is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_IMX21_HCD is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_IPHETH is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1301 is not set
+# CONFIG_USB_ISP1362_HCD is not set
+# CONFIG_USB_ISP1760 is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_KC2190 is not set
+# CONFIG_USB_LAN78XX is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set
+# CONFIG_USB_LED_TRIG is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LGM_PHY is not set
+# CONFIG_USB_LINK_LAYER_TEST is not set
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_M66592 is not set
+# CONFIG_USB_MASS_STORAGE is not set
+# CONFIG_USB_MAX3420_UDC is not set
+# CONFIG_USB_MAX3421_HCD is not set
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_MOUSE is not set
+# CONFIG_USB_MSI2500 is not set
+# CONFIG_USB_MSM_OTG is not set
+# CONFIG_USB_MTU3 is not set
+# CONFIG_USB_MUSB_GADGET is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_MUSB_HOST is not set
+# CONFIG_USB_MV_U3D is not set
+# CONFIG_USB_MV_UDC is not set
+# CONFIG_USB_MXS_PHY is not set
+# CONFIG_USB_NET2272 is not set
+# CONFIG_USB_NET2280 is not set
+# CONFIG_USB_NET_AQC111 is not set
+# CONFIG_USB_NET_AX88179_178A is not set
+# CONFIG_USB_NET_AX8817X is not set
+# CONFIG_USB_NET_CDCETHER is not set
+# CONFIG_USB_NET_CDC_EEM is not set
+# CONFIG_USB_NET_CDC_MBIM is not set
+# CONFIG_USB_NET_CDC_NCM is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_CH9200 is not set
+# CONFIG_USB_NET_CX82310_ETH is not set
+# CONFIG_USB_NET_DM9601 is not set
+# CONFIG_USB_NET_DRIVERS is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set
+# CONFIG_USB_NET_INT51X1 is not set
+# CONFIG_USB_NET_KALMIA is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_QMI_WWAN is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_RNDIS_WLAN is not set
+# CONFIG_USB_NET_SMSC75XX is not set
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_SR9700 is not set
+# CONFIG_USB_NET_SR9800 is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_USB_OHCI_HCD is not set
+# CONFIG_USB_OHCI_HCD_PCI is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF_BE is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF_LE is not set
+# CONFIG_USB_OHCI_HCD_SSB is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set
+# CONFIG_USB_OTG_FSM is not set
+# CONFIG_USB_OTG_PRODUCTLIST is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_PCI is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_PHY is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_PWC_INPUT_EVDEV is not set
+# CONFIG_USB_PXA27X is not set
+# CONFIG_USB_R8A66597 is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_RAW_GADGET is not set
+# CONFIG_USB_RCAR_PHY is not set
+# CONFIG_USB_RENESAS_USBHS is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_ROLES_INTEL_XHCI is not set
+# CONFIG_USB_ROLE_SWITCH is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_RTL8152 is not set
+# CONFIG_USB_RTL8153_ECM is not set
+# CONFIG_USB_S2255 is not set
+# CONFIG_USB_SERIAL is not set
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+# CONFIG_USB_SERIAL_BELKIN is not set
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_CP210X is not set
+# CONFIG_USB_SERIAL_CYBERJACK is not set
+# CONFIG_USB_SERIAL_CYPRESS_M8 is not set
+# CONFIG_USB_SERIAL_DEBUG is not set
+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT_TI is not set
+# CONFIG_USB_SERIAL_EMPEG is not set
+# CONFIG_USB_SERIAL_F81232 is not set
+# CONFIG_USB_SERIAL_F8153X is not set
+# CONFIG_USB_SERIAL_FTDI_SIO is not set
+# CONFIG_USB_SERIAL_GARMIN is not set
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_IPAQ is not set
+# CONFIG_USB_SERIAL_IPW is not set
+# CONFIG_USB_SERIAL_IR is not set
+# CONFIG_USB_SERIAL_IUU is not set
+# CONFIG_USB_SERIAL_KEYSPAN is not set
+CONFIG_USB_SERIAL_KEYSPAN_MPR=y
+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
+CONFIG_USB_SERIAL_KEYSPAN_USA18X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
+# CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_METRO is not set
+# CONFIG_USB_SERIAL_MOS7715_PARPORT is not set
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_MXUPORT is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+# CONFIG_USB_SERIAL_OMNINET is not set
+# CONFIG_USB_SERIAL_OPTICON is not set
+# CONFIG_USB_SERIAL_OPTION is not set
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_PL2303 is not set
+# CONFIG_USB_SERIAL_QCAUX is not set
+# CONFIG_USB_SERIAL_QT2 is not set
+# CONFIG_USB_SERIAL_QUALCOMM is not set
+# CONFIG_USB_SERIAL_SAFE is not set
+CONFIG_USB_SERIAL_SAFE_PADDED=y
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+# CONFIG_USB_SERIAL_SIMPLE is not set
+# CONFIG_USB_SERIAL_SPCP8X5 is not set
+# CONFIG_USB_SERIAL_SSU100 is not set
+# CONFIG_USB_SERIAL_SYMBOL is not set
+# CONFIG_USB_SERIAL_TI is not set
+# CONFIG_USB_SERIAL_UPD78F0730 is not set
+# CONFIG_USB_SERIAL_VISOR is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+# CONFIG_USB_SERIAL_WISHBONE is not set
+# CONFIG_USB_SERIAL_XIRCOM is not set
+# CONFIG_USB_SERIAL_XR is not set
+# CONFIG_USB_SERIAL_XSENS_MT is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_SIERRA_NET is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_SNP_UDC_PLAT is not set
+# CONFIG_USB_SPEEDTOUCH is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_ENE_UB6250 is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_REALTEK is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STV06XX is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_USB_SWITCH_FSA9480 is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_TMC is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_UAS is not set
+# CONFIG_USB_UEAGLEATM is not set
+# CONFIG_USB_ULPI is not set
+# CONFIG_USB_ULPI_BUS is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_USB_USS720 is not set
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
+# CONFIG_USB_VL600 is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_WHCI_HCD is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+# CONFIG_USB_XHCI_DBGCAP is not set
+# CONFIG_USB_XHCI_HCD is not set
+# CONFIG_USB_XHCI_MVEBU is not set
+# CONFIG_USB_XHCI_PCI_RENESAS is not set
+# CONFIG_USB_XUSBATM is not set
+# CONFIG_USB_YUREX is not set
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USELIB is not set
+# CONFIG_USERFAULTFD is not set
+# CONFIG_USERIO is not set
+# CONFIG_USE_OF is not set
+# CONFIG_UTS_NS is not set
+# CONFIG_UWB is not set
+# CONFIG_U_SERIAL_CONSOLE is not set
+# CONFIG_V4L_MEM2MEM_DRIVERS is not set
+# CONFIG_V4L_PLATFORM_DRIVERS is not set
+# CONFIG_V4L_TEST_DRIVERS is not set
+# CONFIG_VALIDATE_FS_PARSER is not set
+# CONFIG_VBOXGUEST is not set
+# CONFIG_VCNL3020 is not set
+# CONFIG_VCNL4000 is not set
+# CONFIG_VCNL4035 is not set
+# CONFIG_VDPA is not set
+CONFIG_VDSO=y
+# CONFIG_VEML6030 is not set
+# CONFIG_VEML6070 is not set
+# CONFIG_VETH is not set
+# CONFIG_VEXPRESS_CONFIG is not set
+# CONFIG_VF610_ADC is not set
+# CONFIG_VF610_DAC is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_VFIO is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VGA_ARB is not set
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_VGA_SWITCHEROO is not set
+# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set
+CONFIG_VHOST_MENU=y
+# CONFIG_VHOST_NET is not set
+# CONFIG_VHOST_VSOCK is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_VELOCITY is not set
+# CONFIG_VIDEO_AD5398 is not set
+# CONFIG_VIDEO_AD5820 is not set
+# CONFIG_VIDEO_AD9389B is not set
+# CONFIG_VIDEO_ADP1653 is not set
+# CONFIG_VIDEO_ADV7170 is not set
+# CONFIG_VIDEO_ADV7175 is not set
+# CONFIG_VIDEO_ADV7180 is not set
+# CONFIG_VIDEO_ADV7183 is not set
+# CONFIG_VIDEO_ADV7343 is not set
+# CONFIG_VIDEO_ADV7393 is not set
+# CONFIG_VIDEO_ADV748X is not set
+# CONFIG_VIDEO_ADV7511 is not set
+# CONFIG_VIDEO_ADV7604 is not set
+# CONFIG_VIDEO_ADV7842 is not set
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_AK7375 is not set
+# CONFIG_VIDEO_AK881X is not set
+# CONFIG_VIDEO_AM437X_VPFE is not set
+# CONFIG_VIDEO_ASPEED is not set
+# CONFIG_VIDEO_ATMEL_ISC is not set
+# CONFIG_VIDEO_ATMEL_ISI is not set
+# CONFIG_VIDEO_AU0828 is not set
+# CONFIG_VIDEO_BT819 is not set
+# CONFIG_VIDEO_BT848 is not set
+# CONFIG_VIDEO_BT856 is not set
+# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_CADENCE is not set
+# CONFIG_VIDEO_CAFE_CCIC is not set
+# CONFIG_VIDEO_CCS is not set
+# CONFIG_VIDEO_CODA is not set
+# CONFIG_VIDEO_CS3308 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_CX231XX is not set
+# CONFIG_VIDEO_CX2341X is not set
+# CONFIG_VIDEO_CX25840 is not set
+# CONFIG_VIDEO_CX88 is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_DM6446_CCDC is not set
+# CONFIG_VIDEO_DT3155 is not set
+# CONFIG_VIDEO_DW9714 is not set
+# CONFIG_VIDEO_DW9768 is not set
+# CONFIG_VIDEO_DW9807_VCM is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_ET8EK8 is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+# CONFIG_VIDEO_GO7007 is not set
+# CONFIG_VIDEO_GS1662 is not set
+# CONFIG_VIDEO_HDPVR is not set
+# CONFIG_VIDEO_HEXIUM_GEMINI is not set
+# CONFIG_VIDEO_HEXIUM_ORION is not set
+# CONFIG_VIDEO_HI556 is not set
+# CONFIG_VIDEO_I2C is not set
+# CONFIG_VIDEO_IMX_PXP is not set
+# CONFIG_VIDEO_IMX208 is not set
+# CONFIG_VIDEO_IMX214 is not set
+# CONFIG_VIDEO_IMX219 is not set
+# CONFIG_VIDEO_IMX258 is not set
+# CONFIG_VIDEO_IMX274 is not set
+# CONFIG_VIDEO_IMX290 is not set
+# CONFIG_VIDEO_IMX319 is not set
+# CONFIG_VIDEO_IMX334 is not set
+# CONFIG_VIDEO_IMX335 is not set
+# CONFIG_VIDEO_IMX355 is not set
+# CONFIG_VIDEO_IMX412 is not set
+# CONFIG_VIDEO_IMX477 is not set
+# CONFIG_VIDEO_IMX8_JPEG is not set
+# CONFIG_VIDEO_IRS1125 is not set
+# CONFIG_VIDEO_IR_I2C is not set
+# CONFIG_VIDEO_IVTV is not set
+# CONFIG_VIDEO_KS0127 is not set
+# CONFIG_VIDEO_LM3560 is not set
+# CONFIG_VIDEO_LM3646 is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_M5MOLS is not set
+# CONFIG_VIDEO_MAX9286 is not set
+# CONFIG_VIDEO_MEM2MEM_DEINTERLACE is not set
+# CONFIG_VIDEO_ML86V7667 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_MT9M001 is not set
+# CONFIG_VIDEO_MT9M032 is not set
+# CONFIG_VIDEO_MT9M111 is not set
+# CONFIG_VIDEO_MT9P031 is not set
+# CONFIG_VIDEO_MT9T001 is not set
+# CONFIG_VIDEO_MT9T112 is not set
+# CONFIG_VIDEO_MT9V011 is not set
+# CONFIG_VIDEO_MT9V032 is not set
+# CONFIG_VIDEO_MT9V111 is not set
+# CONFIG_VIDEO_MUX is not set
+# CONFIG_VIDEO_MXB is not set
+# CONFIG_VIDEO_NOON010PC30 is not set
+# CONFIG_VIDEO_OMAP2_VOUT is not set
+# CONFIG_VIDEO_OV02A10 is not set
+# CONFIG_VIDEO_OV13858 is not set
+# CONFIG_VIDEO_OV2311 is not set
+# CONFIG_VIDEO_OV2640 is not set
+# CONFIG_VIDEO_OV2659 is not set
+# CONFIG_VIDEO_OV2680 is not set
+# CONFIG_VIDEO_OV2685 is not set
+# CONFIG_VIDEO_OV2740 is not set
+# CONFIG_VIDEO_OV5640 is not set
+# CONFIG_VIDEO_OV5645 is not set
+# CONFIG_VIDEO_OV5647 is not set
+# CONFIG_VIDEO_OV5648 is not set
+# CONFIG_VIDEO_OV5670 is not set
+# CONFIG_VIDEO_OV5675 is not set
+# CONFIG_VIDEO_OV5695 is not set
+# CONFIG_VIDEO_OV6650 is not set
+# CONFIG_VIDEO_OV7251 is not set
+# CONFIG_VIDEO_OV7640 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_OV772X is not set
+# CONFIG_VIDEO_OV7740 is not set
+# CONFIG_VIDEO_OV8856 is not set
+# CONFIG_VIDEO_OV8865 is not set
+# CONFIG_VIDEO_OV9281 is not set
+# CONFIG_VIDEO_OV9282 is not set
+# CONFIG_VIDEO_OV9640 is not set
+# CONFIG_VIDEO_OV9650 is not set
+# CONFIG_VIDEO_OV9734 is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_RDACM20 is not set
+# CONFIG_VIDEO_RDACM21 is not set
+# CONFIG_VIDEO_RJ54N1 is not set
+# CONFIG_VIDEO_ROCKCHIP_ISP1 is not set
+# CONFIG_VIDEO_S5C73M3 is not set
+# CONFIG_VIDEO_S5K4ECGX is not set
+# CONFIG_VIDEO_S5K5BAF is not set
+# CONFIG_VIDEO_S5K6A3 is not set
+# CONFIG_VIDEO_S5K6AA is not set
+# CONFIG_VIDEO_SAA6588 is not set
+# CONFIG_VIDEO_SAA6752HS is not set
+# CONFIG_VIDEO_SAA7110 is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA7127 is not set
+# CONFIG_VIDEO_SAA7134 is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_SAA7185 is not set
+# CONFIG_VIDEO_SH_MOBILE_CEU is not set
+# CONFIG_VIDEO_SMIAPP is not set
+# CONFIG_VIDEO_SONY_BTF_MPX is not set
+# CONFIG_VIDEO_SR030PC30 is not set
+# CONFIG_VIDEO_STK1160_COMMON is not set
+# CONFIG_VIDEO_ST_MIPID02 is not set
+# CONFIG_VIDEO_TC358743 is not set
+# CONFIG_VIDEO_TDA1997X is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_THS7303 is not set
+# CONFIG_VIDEO_THS8200 is not set
+# CONFIG_VIDEO_TIMBERDALE is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_TM6000 is not set
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TVP514X is not set
+# CONFIG_VIDEO_TVP5150 is not set
+# CONFIG_VIDEO_TVP7002 is not set
+# CONFIG_VIDEO_TW2804 is not set
+# CONFIG_VIDEO_TW9903 is not set
+# CONFIG_VIDEO_TW9906 is not set
+# CONFIG_VIDEO_TW9910 is not set
+# CONFIG_VIDEO_UDA1342 is not set
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_USBTV is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_VIDEO_V4L2 is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+# CONFIG_VIDEO_VPX3220 is not set
+# CONFIG_VIDEO_VS6624 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_XILINX is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_BLK_SCSI is not set
+# CONFIG_VIRTIO_CONSOLE is not set
+# CONFIG_VIRTIO_FS is not set
+# CONFIG_VIRTIO_INPUT is not set
+# CONFIG_VIRTIO_IOMMU is not set
+CONFIG_VIRTIO_MENU=y
+# CONFIG_VIRTIO_MMIO is not set
+# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set
+# CONFIG_VIRTIO_PCI is not set
+# CONFIG_VIRTUALIZATION is not set
+# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
+# CONFIG_VIRT_DRIVERS is not set
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_VL53L0X_I2C is not set
+# CONFIG_VL6180 is not set
+CONFIG_VLAN_8021Q=y
+# CONFIG_VLAN_8021Q_GVRP is not set
+# CONFIG_VLAN_8021Q_MVRP is not set
+# CONFIG_VME_BUS is not set
+# CONFIG_VMLINUX_MAP is not set
+# CONFIG_VMSPLIT_1G is not set
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_2G_OPT is not set
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_3G_OPT is not set
+# CONFIG_VMWARE_PVSCSI is not set
+# CONFIG_VMXNET3 is not set
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_VOP_BUS is not set
+# CONFIG_VORTEX is not set
+# CONFIG_VSOCKETS is not set
+# CONFIG_VSOCKETS_DIAG is not set
+# CONFIG_VT is not set
+# CONFIG_VT6655 is not set
+# CONFIG_VT6656 is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_VXGE is not set
+# CONFIG_VXLAN is not set
+# CONFIG_VZ89X is not set
+# CONFIG_W1 is not set
+# CONFIG_W1_CON is not set
+# CONFIG_W1_MASTER_DS1WM is not set
+# CONFIG_W1_MASTER_DS2482 is not set
+# CONFIG_W1_MASTER_DS2490 is not set
+# CONFIG_W1_MASTER_GPIO is not set
+# CONFIG_W1_MASTER_MATROX is not set
+# CONFIG_W1_MASTER_SGI is not set
+# CONFIG_W1_SLAVE_DS2405 is not set
+# CONFIG_W1_SLAVE_DS2406 is not set
+# CONFIG_W1_SLAVE_DS2408 is not set
+# CONFIG_W1_SLAVE_DS2413 is not set
+# CONFIG_W1_SLAVE_DS2423 is not set
+# CONFIG_W1_SLAVE_DS2430 is not set
+# CONFIG_W1_SLAVE_DS2431 is not set
+# CONFIG_W1_SLAVE_DS2433 is not set
+# CONFIG_W1_SLAVE_DS2438 is not set
+# CONFIG_W1_SLAVE_DS250X is not set
+# CONFIG_W1_SLAVE_DS2780 is not set
+# CONFIG_W1_SLAVE_DS2781 is not set
+# CONFIG_W1_SLAVE_DS2805 is not set
+# CONFIG_W1_SLAVE_DS28E04 is not set
+# CONFIG_W1_SLAVE_DS28E17 is not set
+# CONFIG_W1_SLAVE_SMEM is not set
+# CONFIG_W1_SLAVE_THERM is not set
+# CONFIG_W83627HF_WDT is not set
+# CONFIG_W83877F_WDT is not set
+# CONFIG_W83977F_WDT is not set
+# CONFIG_WAN is not set
+# CONFIG_WANXL is not set
+# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_CORE is not set
+CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y
+# CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT is not set
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+CONFIG_WATCHDOG_OPEN_TIMEOUT=0
+# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set
+# CONFIG_WATCHDOG_SYSFS is not set
+# CONFIG_WATCH_QUEUE is not set
+# CONFIG_WD80x3 is not set
+# CONFIG_WDAT_WDT is not set
+# CONFIG_WDTPCI is not set
+CONFIG_WERROR=y
+# CONFIG_WEXT_CORE is not set
+# CONFIG_WEXT_PRIV is not set
+# CONFIG_WEXT_PROC is not set
+# CONFIG_WEXT_SPY is not set
+CONFIG_WILINK_PLATFORM_DATA=y
+# CONFIG_WIMAX is not set
+# CONFIG_WIREGUARD is not set
+CONFIG_WIRELESS=y
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_WIRELESS_WDS is not set
+# CONFIG_WIZNET_W5100 is not set
+# CONFIG_WIZNET_W5300 is not set
+# CONFIG_WL1251 is not set
+# CONFIG_WL12XX is not set
+# CONFIG_WL18XX is not set
+CONFIG_WLAN=y
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+# CONFIG_WLAN_VENDOR_ATH is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+# CONFIG_WLAN_VENDOR_CISCO is not set
+# CONFIG_WLAN_VENDOR_INTEL is not set
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+# CONFIG_WLAN_VENDOR_MEDIATEK is not set
+# CONFIG_WLAN_VENDOR_MICROCHIP is not set
+# CONFIG_WLAN_VENDOR_QUANTENNA is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+# CONFIG_WLAN_VENDOR_REALTEK is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
+# CONFIG_WLCORE is not set
+CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y
+# CONFIG_WQ_WATCHDOG is not set
+# CONFIG_WWAN is not set
+# CONFIG_WWAN_HWSIM is not set
+# CONFIG_WW_MUTEX_SELFTEST is not set
+# CONFIG_X25 is not set
+# CONFIG_X509_CERTIFICATE_PARSER is not set
+# CONFIG_X86_PKG_TEMP_THERMAL is not set
+CONFIG_X86_SYSFB=y
+# CONFIG_XDP_SOCKETS is not set
+# CONFIG_XEN is not set
+# CONFIG_XEN_GRANT_DMA_ALLOC is not set
+# CONFIG_XEN_PVCALLS_FRONTEND is not set
+CONFIG_XEN_SCRUB_PAGES_DEFAULT=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_INTERFACE is not set
+# CONFIG_XFRM_IPCOMP is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFS_DEBUG is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_XFS_ONLINE_SCRUB is not set
+# CONFIG_XFS_POSIX_ACL is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_SUPPORT_V4 is not set
+# CONFIG_XFS_WARN is not set
+# CONFIG_XILINX_AXI_EMAC is not set
+# CONFIG_XILINX_DMA is not set
+# CONFIG_XILINX_EMACLITE is not set
+# CONFIG_XILINX_GMII2RGMII is not set
+# CONFIG_XILINX_LL_TEMAC is not set
+# CONFIG_XILINX_SDFEC is not set
+# CONFIG_XILINX_VCU is not set
+# CONFIG_XILINX_WATCHDOG is not set
+# CONFIG_XILINX_XADC is not set
+# CONFIG_XILINX_ZYNQMP_DMA is not set
+# CONFIG_XILINX_ZYNQMP_DPDMA is not set
+# CONFIG_XILLYBUS is not set
+# CONFIG_XILLYUSB is not set
+# CONFIG_XIL_AXIS_FIFO is not set
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_XMON is not set
+CONFIG_XZ_DEC=y
+# CONFIG_XZ_DEC_ARM is not set
+# CONFIG_XZ_DEC_ARMTHUMB is not set
+# CONFIG_XZ_DEC_BCJ is not set
+# CONFIG_XZ_DEC_IA64 is not set
+# CONFIG_XZ_DEC_POWERPC is not set
+# CONFIG_XZ_DEC_SPARC is not set
+# CONFIG_XZ_DEC_TEST is not set
+# CONFIG_XZ_DEC_X86 is not set
+# CONFIG_YAM is not set
+# CONFIG_YAMAHA_YAS530 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_YENTA is not set
+# CONFIG_YENTA_O2 is not set
+# CONFIG_YENTA_RICOH is not set
+# CONFIG_YENTA_TI is not set
+# CONFIG_YENTA_TOSHIBA is not set
+# CONFIG_ZBUD is not set
+# CONFIG_ZD1211RW is not set
+# CONFIG_ZD1211RW_DEBUG is not set
+# CONFIG_ZEROPLUS_FF is not set
+# CONFIG_ZERO_CALL_USED_REGS is not set
+# CONFIG_ZIIRAVE_WATCHDOG is not set
+# CONFIG_ZISOFS is not set
+# CONFIG_ZLIB_DEFLATE is not set
+# CONFIG_ZLIB_INFLATE is not set
+CONFIG_ZONE_DMA=y
+# CONFIG_ZOPT2201 is not set
+# CONFIG_ZPA2326 is not set
+# CONFIG_ZPOOL is not set
+# CONFIG_ZRAM is not set
+# CONFIG_ZRAM_DEF_COMP_842 is not set
+# CONFIG_ZRAM_DEF_COMP_LZ4 is not set
+# CONFIG_ZRAM_DEF_COMP_LZ4HC is not set
+# CONFIG_ZRAM_DEF_COMP_LZO is not set
+# CONFIG_ZRAM_DEF_COMP_LZORLE is not set
+# CONFIG_ZRAM_DEF_COMP_ZSTD is not set
+# CONFIG_ZRAM_MEMORY_TRACKING is not set
+# CONFIG_ZSMALLOC is not set
+# CONFIG_ZX_TDM is not set
diff --git a/target/linux/generic/hack-6.1/204-module_strip.patch b/target/linux/generic/hack-6.1/204-module_strip.patch
new file mode 100644
index 0000000000..dd5d93e226
--- /dev/null
+++ b/target/linux/generic/hack-6.1/204-module_strip.patch
@@ -0,0 +1,212 @@
+From a779a482fb9b9f8fcdf8b2519c789b4b9bb5dd05 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 16:56:48 +0200
+Subject: build: add a hack for removing non-essential module info
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/module.h | 13 ++++++++-----
+ include/linux/moduleparam.h | 15 ++++++++++++---
+ init/Kconfig | 7 +++++++
+ kernel/module.c | 5 ++++-
+ scripts/mod/modpost.c | 12 ++++++++++++
+ 5 files changed, 43 insertions(+), 9 deletions(-)
+
+--- a/include/linux/module.h
++++ b/include/linux/module.h
+@@ -164,6 +164,7 @@ extern void cleanup_module(void);
+
+ /* Generic info of form tag = "info" */
+ #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
++#define MODULE_INFO_STRIP(tag, info) __MODULE_INFO_STRIP(tag, tag, info)
+
+ /* For userspace: you can also call me... */
+ #define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)
+@@ -233,12 +234,12 @@ extern void cleanup_module(void);
+ * Author(s), use "Name <email>" or just "Name", for multiple
+ * authors use multiple MODULE_AUTHOR() statements/lines.
+ */
+-#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
++#define MODULE_AUTHOR(_author) MODULE_INFO_STRIP(author, _author)
+
+ /* What your module does. */
+-#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
++#define MODULE_DESCRIPTION(_description) MODULE_INFO_STRIP(description, _description)
+
+-#ifdef MODULE
++#if defined(MODULE) && !defined(CONFIG_MODULE_STRIPPED)
+ /* Creates an alias so file2alias.c can find device table. */
+ #define MODULE_DEVICE_TABLE(type, name) \
+ extern typeof(name) __mod_##type##__##name##_device_table \
+@@ -265,7 +266,9 @@ extern typeof(name) __mod_##type##__##na
+ */
+
+ #if defined(MODULE) || !defined(CONFIG_SYSFS)
+-#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
++#define MODULE_VERSION(_version) MODULE_INFO_STRIP(version, _version)
++#elif defined(CONFIG_MODULE_STRIPPED)
++#define MODULE_VERSION(_version) __MODULE_INFO_DISABLED(version)
+ #else
+ #define MODULE_VERSION(_version) \
+ MODULE_INFO(version, _version); \
+@@ -288,7 +291,7 @@ extern typeof(name) __mod_##type##__##na
+ /* Optional firmware file (or files) needed by the module
+ * format is simply firmware file name. Multiple firmware
+ * files require multiple MODULE_FIRMWARE() specifiers */
+-#define MODULE_FIRMWARE(_firmware) MODULE_INFO(firmware, _firmware)
++#define MODULE_FIRMWARE(_firmware) MODULE_INFO_STRIP(firmware, _firmware)
+
+ #define MODULE_IMPORT_NS(ns) MODULE_INFO(import_ns, #ns)
+
+--- a/include/linux/moduleparam.h
++++ b/include/linux/moduleparam.h
+@@ -20,6 +20,16 @@
+ /* Chosen so that structs with an unsigned long line up. */
+ #define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long))
+
++/* This struct is here for syntactic coherency, it is not used */
++#define __MODULE_INFO_DISABLED(name) \
++ struct __UNIQUE_ID(name) {}
++
++#ifdef CONFIG_MODULE_STRIPPED
++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO_DISABLED(name)
++#else
++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO(tag, name, info)
++#endif
++
+ #define __MODULE_INFO(tag, name, info) \
+ static const char __UNIQUE_ID(name)[] \
+ __used __section(".modinfo") __aligned(1) \
+@@ -31,7 +41,7 @@
+ /* One for each parameter, describing how to use it. Some files do
+ multiple of these per line, so can't just use MODULE_INFO. */
+ #define MODULE_PARM_DESC(_parm, desc) \
+- __MODULE_INFO(parm, _parm, #_parm ":" desc)
++ __MODULE_INFO_STRIP(parm, _parm, #_parm ":" desc)
+
+ struct kernel_param;
+
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -2356,6 +2356,13 @@ config UNUSED_KSYMS_WHITELIST
+ one per line. The path can be absolute, or relative to the kernel
+ source tree.
+
++config MODULE_STRIPPED
++ bool "Reduce module size"
++ depends on MODULES
++ help
++ Remove module parameter descriptions, author info, version, aliases,
++ device tables, etc.
++
+ endif # MODULES
+
+ config MODULES_TREE_LOOKUP
+--- a/kernel/module.c
++++ b/kernel/module.c
+@@ -1218,6 +1218,7 @@ static struct module_attribute *modinfo_
+
+ static const char vermagic[] = VERMAGIC_STRING;
+
++#if defined(CONFIG_MODVERSIONS) || !defined(CONFIG_MODULE_STRIPPED)
+ static int try_to_force_load(struct module *mod, const char *reason)
+ {
+ #ifdef CONFIG_MODULE_FORCE_LOAD
+@@ -1229,6 +1230,7 @@ static int try_to_force_load(struct modu
+ return -ENOEXEC;
+ #endif
+ }
++#endif
+
+ #ifdef CONFIG_MODVERSIONS
+
+@@ -3266,9 +3268,11 @@ static int setup_load_info(struct load_i
+
+ static int check_modinfo(struct module *mod, struct load_info *info, int flags)
+ {
+- const char *modmagic = get_modinfo(info, "vermagic");
+ int err;
+
++#ifndef CONFIG_MODULE_STRIPPED
++ const char *modmagic = get_modinfo(info, "vermagic");
++
+ if (flags & MODULE_INIT_IGNORE_VERMAGIC)
+ modmagic = NULL;
+
+@@ -3289,6 +3293,7 @@ static int check_modinfo(struct module *
+ mod->name);
+ add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK);
+ }
++#endif
+
+ check_modinfo_retpoline(mod, info);
+
+--- a/scripts/mod/modpost.c
++++ b/scripts/mod/modpost.c
+@@ -2036,7 +2036,9 @@ static void read_symbols(const char *mod
+ symname = remove_dot(info.strtab + sym->st_name);
+
+ handle_symbol(mod, &info, sym, symname);
++#ifndef CONFIG_MODULE_STRIPPED
+ handle_moddevtable(mod, &info, sym, symname);
++#endif
+ }
+
+ for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
+@@ -2215,8 +2217,10 @@ static void add_header(struct buffer *b,
+ buf_printf(b, "BUILD_SALT;\n");
+ buf_printf(b, "BUILD_LTO_INFO;\n");
+ buf_printf(b, "\n");
++#ifndef CONFIG_MODULE_STRIPPED
+ buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
+ buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n");
++#endif
+ buf_printf(b, "\n");
+ buf_printf(b, "__visible struct module __this_module\n");
+ buf_printf(b, "__section(\".gnu.linkonce.this_module\") = {\n");
+@@ -2233,8 +2237,10 @@ static void add_header(struct buffer *b,
+
+ static void add_intree_flag(struct buffer *b, int is_intree)
+ {
++#ifndef CONFIG_MODULE_STRIPPED
+ if (is_intree)
+ buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
++#endif
+ }
+
+ /* Cannot check for assembler */
+@@ -2247,8 +2253,10 @@ static void add_retpoline(struct buffer
+
+ static void add_staging_flag(struct buffer *b, const char *name)
+ {
++#ifndef CONFIG_MODULE_STRIPPED
+ if (strstarts(name, "drivers/staging"))
+ buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
++#endif
+ }
+
+ /**
+@@ -2328,11 +2336,13 @@ static void add_depends(struct buffer *b
+
+ static void add_srcversion(struct buffer *b, struct module *mod)
+ {
++#ifndef CONFIG_MODULE_STRIPPED
+ if (mod->srcversion[0]) {
+ buf_printf(b, "\n");
+ buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n",
+ mod->srcversion);
+ }
++#endif
+ }
+
+ static void write_buf(struct buffer *b, const char *fname)
+@@ -2581,7 +2591,9 @@ int main(int argc, char **argv)
+ add_staging_flag(&buf, mod->name);
+ add_versions(&buf, mod);
+ add_depends(&buf, mod);
++#ifndef CONFIG_MODULE_STRIPPED
+ add_moddevtable(&buf, mod);
++#endif
+ add_srcversion(&buf, mod);
+
+ sprintf(fname, "%s.mod.c", mod->name);
diff --git a/target/linux/generic/hack-6.1/205-kconfig-abort-configuration-on-unset-symbol.patch b/target/linux/generic/hack-6.1/205-kconfig-abort-configuration-on-unset-symbol.patch
new file mode 100644
index 0000000000..a87c7a5116
--- /dev/null
+++ b/target/linux/generic/hack-6.1/205-kconfig-abort-configuration-on-unset-symbol.patch
@@ -0,0 +1,41 @@
+From 310e8e04a05d9eb43fa9dd7f00143300afcaa37a Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Fri, 11 Nov 2022 13:33:44 +0100
+Subject: [PATCH] kconfig: abort configuration on unset symbol
+
+When a target configuration has unset Kconfig symbols, the build will
+fail when OpenWrt is compiled with V=s and stdin is connected to a tty.
+
+In case OpenWrt is compiled without either of these preconditions, the
+build will succeed with the symbols in question being unset.
+
+Modify the kernel configuration in a way it fails on unset symbols
+regardless of the aforementioned preconditions.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ scripts/kconfig/conf.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/scripts/kconfig/conf.c
++++ b/scripts/kconfig/conf.c
+@@ -341,6 +341,9 @@ static int conf_askvalue(struct symbol *
+ }
+ /* fall through */
+ default:
++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) {
++ exit(1);
++ }
+ fflush(stdout);
+ xfgets(line, sizeof(line), stdin);
+ break;
+@@ -523,6 +526,9 @@ static int conf_choice(struct menu *menu
+ }
+ /* fall through */
+ case oldaskconfig:
++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) {
++ exit(1);
++ }
+ fflush(stdout);
+ xfgets(line, sizeof(line), stdin);
+ strip(line);
diff --git a/target/linux/generic/hack-6.1/210-darwin_scripts_include.patch b/target/linux/generic/hack-6.1/210-darwin_scripts_include.patch
new file mode 100644
index 0000000000..be6adc0d11
--- /dev/null
+++ b/target/linux/generic/hack-6.1/210-darwin_scripts_include.patch
@@ -0,0 +1,3053 @@
+From db7c30dcd9a0391bf13b62c9f91e144d762ef43a Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Fri, 7 Jul 2017 17:00:49 +0200
+Subject: Add an OSX specific patch to make the kernel be compiled
+
+lede-commit: 3fc2a24f0422b2f55f9ed43f116db3111f700526
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ scripts/kconfig/Makefile | 3 +
+ scripts/mod/elf.h | 3007 ++++++++++++++++++++++++++++++++++++++++++++
+ scripts/mod/mk_elfconfig.c | 4 +
+ scripts/mod/modpost.h | 4 +
+ 4 files changed, 3018 insertions(+)
+ create mode 100644 scripts/mod/elf.h
+
+--- /dev/null
++++ b/scripts/mod/elf.h
+@@ -0,0 +1,3007 @@
++/* This file defines standard ELF types, structures, and macros.
++ Copyright (C) 1995-2012 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, see
++ <http://www.gnu.org/licenses/>. */
++
++#ifndef _ELF_H
++#define _ELF_H 1
++
++/* Standard ELF types. */
++
++#include <stdint.h>
++
++/* Type for a 16-bit quantity. */
++typedef uint16_t Elf32_Half;
++typedef uint16_t Elf64_Half;
++
++/* Types for signed and unsigned 32-bit quantities. */
++typedef uint32_t Elf32_Word;
++typedef int32_t Elf32_Sword;
++typedef uint32_t Elf64_Word;
++typedef int32_t Elf64_Sword;
++
++/* Types for signed and unsigned 64-bit quantities. */
++typedef uint64_t Elf32_Xword;
++typedef int64_t Elf32_Sxword;
++typedef uint64_t Elf64_Xword;
++typedef int64_t Elf64_Sxword;
++
++/* Type of addresses. */
++typedef uint32_t Elf32_Addr;
++typedef uint64_t Elf64_Addr;
++
++/* Type of file offsets. */
++typedef uint32_t Elf32_Off;
++typedef uint64_t Elf64_Off;
++
++/* Type for section indices, which are 16-bit quantities. */
++typedef uint16_t Elf32_Section;
++typedef uint16_t Elf64_Section;
++
++/* Type for version symbol information. */
++typedef Elf32_Half Elf32_Versym;
++typedef Elf64_Half Elf64_Versym;
++
++
++/* The ELF file header. This appears at the start of every ELF file. */
++
++#define EI_NIDENT (16)
++
++typedef struct
++{
++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
++ Elf32_Half e_type; /* Object file type */
++ Elf32_Half e_machine; /* Architecture */
++ Elf32_Word e_version; /* Object file version */
++ Elf32_Addr e_entry; /* Entry point virtual address */
++ Elf32_Off e_phoff; /* Program header table file offset */
++ Elf32_Off e_shoff; /* Section header table file offset */
++ Elf32_Word e_flags; /* Processor-specific flags */
++ Elf32_Half e_ehsize; /* ELF header size in bytes */
++ Elf32_Half e_phentsize; /* Program header table entry size */
++ Elf32_Half e_phnum; /* Program header table entry count */
++ Elf32_Half e_shentsize; /* Section header table entry size */
++ Elf32_Half e_shnum; /* Section header table entry count */
++ Elf32_Half e_shstrndx; /* Section header string table index */
++} Elf32_Ehdr;
++
++typedef struct
++{
++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
++ Elf64_Half e_type; /* Object file type */
++ Elf64_Half e_machine; /* Architecture */
++ Elf64_Word e_version; /* Object file version */
++ Elf64_Addr e_entry; /* Entry point virtual address */
++ Elf64_Off e_phoff; /* Program header table file offset */
++ Elf64_Off e_shoff; /* Section header table file offset */
++ Elf64_Word e_flags; /* Processor-specific flags */
++ Elf64_Half e_ehsize; /* ELF header size in bytes */
++ Elf64_Half e_phentsize; /* Program header table entry size */
++ Elf64_Half e_phnum; /* Program header table entry count */
++ Elf64_Half e_shentsize; /* Section header table entry size */
++ Elf64_Half e_shnum; /* Section header table entry count */
++ Elf64_Half e_shstrndx; /* Section header string table index */
++} Elf64_Ehdr;
++
++/* Fields in the e_ident array. The EI_* macros are indices into the
++ array. The macros under each EI_* macro are the values the byte
++ may have. */
++
++#define EI_MAG0 0 /* File identification byte 0 index */
++#define ELFMAG0 0x7f /* Magic number byte 0 */
++
++#define EI_MAG1 1 /* File identification byte 1 index */
++#define ELFMAG1 'E' /* Magic number byte 1 */
++
++#define EI_MAG2 2 /* File identification byte 2 index */
++#define ELFMAG2 'L' /* Magic number byte 2 */
++
++#define EI_MAG3 3 /* File identification byte 3 index */
++#define ELFMAG3 'F' /* Magic number byte 3 */
++
++/* Conglomeration of the identification bytes, for easy testing as a word. */
++#define ELFMAG "\177ELF"
++#define SELFMAG 4
++
++#define EI_CLASS 4 /* File class byte index */
++#define ELFCLASSNONE 0 /* Invalid class */
++#define ELFCLASS32 1 /* 32-bit objects */
++#define ELFCLASS64 2 /* 64-bit objects */
++#define ELFCLASSNUM 3
++
++#define EI_DATA 5 /* Data encoding byte index */
++#define ELFDATANONE 0 /* Invalid data encoding */
++#define ELFDATA2LSB 1 /* 2's complement, little endian */
++#define ELFDATA2MSB 2 /* 2's complement, big endian */
++#define ELFDATANUM 3
++
++#define EI_VERSION 6 /* File version byte index */
++ /* Value must be EV_CURRENT */
++
++#define EI_OSABI 7 /* OS ABI identification */
++#define ELFOSABI_NONE 0 /* UNIX System V ABI */
++#define ELFOSABI_SYSV 0 /* Alias. */
++#define ELFOSABI_HPUX 1 /* HP-UX */
++#define ELFOSABI_NETBSD 2 /* NetBSD. */
++#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */
++#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */
++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */
++#define ELFOSABI_AIX 7 /* IBM AIX. */
++#define ELFOSABI_IRIX 8 /* SGI Irix. */
++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */
++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */
++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */
++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */
++#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */
++#define ELFOSABI_ARM 97 /* ARM */
++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
++
++#define EI_ABIVERSION 8 /* ABI version */
++
++#define EI_PAD 9 /* Byte index of padding bytes */
++
++/* Legal values for e_type (object file type). */
++
++#define ET_NONE 0 /* No file type */
++#define ET_REL 1 /* Relocatable file */
++#define ET_EXEC 2 /* Executable file */
++#define ET_DYN 3 /* Shared object file */
++#define ET_CORE 4 /* Core file */
++#define ET_NUM 5 /* Number of defined types */
++#define ET_LOOS 0xfe00 /* OS-specific range start */
++#define ET_HIOS 0xfeff /* OS-specific range end */
++#define ET_LOPROC 0xff00 /* Processor-specific range start */
++#define ET_HIPROC 0xffff /* Processor-specific range end */
++
++/* Legal values for e_machine (architecture). */
++
++#define EM_NONE 0 /* No machine */
++#define EM_M32 1 /* AT&T WE 32100 */
++#define EM_SPARC 2 /* SUN SPARC */
++#define EM_386 3 /* Intel 80386 */
++#define EM_68K 4 /* Motorola m68k family */
++#define EM_88K 5 /* Motorola m88k family */
++#define EM_860 7 /* Intel 80860 */
++#define EM_MIPS 8 /* MIPS R3000 big-endian */
++#define EM_S370 9 /* IBM System/370 */
++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
++
++#define EM_PARISC 15 /* HPPA */
++#define EM_VPP500 17 /* Fujitsu VPP500 */
++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
++#define EM_960 19 /* Intel 80960 */
++#define EM_PPC 20 /* PowerPC */
++#define EM_PPC64 21 /* PowerPC 64-bit */
++#define EM_S390 22 /* IBM S390 */
++
++#define EM_V800 36 /* NEC V800 series */
++#define EM_FR20 37 /* Fujitsu FR20 */
++#define EM_RH32 38 /* TRW RH-32 */
++#define EM_RCE 39 /* Motorola RCE */
++#define EM_ARM 40 /* ARM */
++#define EM_FAKE_ALPHA 41 /* Digital Alpha */
++#define EM_SH 42 /* Hitachi SH */
++#define EM_SPARCV9 43 /* SPARC v9 64-bit */
++#define EM_TRICORE 44 /* Siemens Tricore */
++#define EM_ARC 45 /* Argonaut RISC Core */
++#define EM_H8_300 46 /* Hitachi H8/300 */
++#define EM_H8_300H 47 /* Hitachi H8/300H */
++#define EM_H8S 48 /* Hitachi H8S */
++#define EM_H8_500 49 /* Hitachi H8/500 */
++#define EM_IA_64 50 /* Intel Merced */
++#define EM_MIPS_X 51 /* Stanford MIPS-X */
++#define EM_COLDFIRE 52 /* Motorola Coldfire */
++#define EM_68HC12 53 /* Motorola M68HC12 */
++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/
++#define EM_PCP 55 /* Siemens PCP */
++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */
++#define EM_NDR1 57 /* Denso NDR1 microprocessor */
++#define EM_STARCORE 58 /* Motorola Start*Core processor */
++#define EM_ME16 59 /* Toyota ME16 processor */
++#define EM_ST100 60 /* STMicroelectronic ST100 processor */
++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/
++#define EM_X86_64 62 /* AMD x86-64 architecture */
++#define EM_PDSP 63 /* Sony DSP Processor */
++
++#define EM_FX66 66 /* Siemens FX66 microcontroller */
++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */
++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */
++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */
++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */
++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */
++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */
++#define EM_SVX 73 /* Silicon Graphics SVx */
++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */
++#define EM_VAX 75 /* Digital VAX */
++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */
++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */
++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */
++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */
++#define EM_HUANY 81 /* Harvard University machine-independent object files */
++#define EM_PRISM 82 /* SiTera Prism */
++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */
++#define EM_FR30 84 /* Fujitsu FR30 */
++#define EM_D10V 85 /* Mitsubishi D10V */
++#define EM_D30V 86 /* Mitsubishi D30V */
++#define EM_V850 87 /* NEC v850 */
++#define EM_M32R 88 /* Mitsubishi M32R */
++#define EM_MN10300 89 /* Matsushita MN10300 */
++#define EM_MN10200 90 /* Matsushita MN10200 */
++#define EM_PJ 91 /* picoJava */
++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */
++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */
++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */
++#define EM_TILEPRO 188 /* Tilera TILEPro */
++#define EM_TILEGX 191 /* Tilera TILE-Gx */
++#define EM_NUM 192
++
++/* If it is necessary to assign new unofficial EM_* values, please
++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the
++ chances of collision with official or non-GNU unofficial values. */
++
++#define EM_ALPHA 0x9026
++
++/* Legal values for e_version (version). */
++
++#define EV_NONE 0 /* Invalid ELF version */
++#define EV_CURRENT 1 /* Current version */
++#define EV_NUM 2
++
++/* Section header. */
++
++typedef struct
++{
++ Elf32_Word sh_name; /* Section name (string tbl index) */
++ Elf32_Word sh_type; /* Section type */
++ Elf32_Word sh_flags; /* Section flags */
++ Elf32_Addr sh_addr; /* Section virtual addr at execution */
++ Elf32_Off sh_offset; /* Section file offset */
++ Elf32_Word sh_size; /* Section size in bytes */
++ Elf32_Word sh_link; /* Link to another section */
++ Elf32_Word sh_info; /* Additional section information */
++ Elf32_Word sh_addralign; /* Section alignment */
++ Elf32_Word sh_entsize; /* Entry size if section holds table */
++} Elf32_Shdr;
++
++typedef struct
++{
++ Elf64_Word sh_name; /* Section name (string tbl index) */
++ Elf64_Word sh_type; /* Section type */
++ Elf64_Xword sh_flags; /* Section flags */
++ Elf64_Addr sh_addr; /* Section virtual addr at execution */
++ Elf64_Off sh_offset; /* Section file offset */
++ Elf64_Xword sh_size; /* Section size in bytes */
++ Elf64_Word sh_link; /* Link to another section */
++ Elf64_Word sh_info; /* Additional section information */
++ Elf64_Xword sh_addralign; /* Section alignment */
++ Elf64_Xword sh_entsize; /* Entry size if section holds table */
++} Elf64_Shdr;
++
++/* Special section indices. */
++
++#define SHN_UNDEF 0 /* Undefined section */
++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */
++#define SHN_LOPROC 0xff00 /* Start of processor-specific */
++#define SHN_BEFORE 0xff00 /* Order section before all others
++ (Solaris). */
++#define SHN_AFTER 0xff01 /* Order section after all others
++ (Solaris). */
++#define SHN_HIPROC 0xff1f /* End of processor-specific */
++#define SHN_LOOS 0xff20 /* Start of OS-specific */
++#define SHN_HIOS 0xff3f /* End of OS-specific */
++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */
++#define SHN_COMMON 0xfff2 /* Associated symbol is common */
++#define SHN_XINDEX 0xffff /* Index is in extra table. */
++#define SHN_HIRESERVE 0xffff /* End of reserved indices */
++
++/* Legal values for sh_type (section type). */
++
++#define SHT_NULL 0 /* Section header table entry unused */
++#define SHT_PROGBITS 1 /* Program data */
++#define SHT_SYMTAB 2 /* Symbol table */
++#define SHT_STRTAB 3 /* String table */
++#define SHT_RELA 4 /* Relocation entries with addends */
++#define SHT_HASH 5 /* Symbol hash table */
++#define SHT_DYNAMIC 6 /* Dynamic linking information */
++#define SHT_NOTE 7 /* Notes */
++#define SHT_NOBITS 8 /* Program space with no data (bss) */
++#define SHT_REL 9 /* Relocation entries, no addends */
++#define SHT_SHLIB 10 /* Reserved */
++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
++#define SHT_INIT_ARRAY 14 /* Array of constructors */
++#define SHT_FINI_ARRAY 15 /* Array of destructors */
++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
++#define SHT_GROUP 17 /* Section group */
++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
++#define SHT_NUM 19 /* Number of defined types. */
++#define SHT_LOOS 0x60000000 /* Start OS-specific. */
++#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */
++#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
++#define SHT_SUNW_move 0x6ffffffa
++#define SHT_SUNW_COMDAT 0x6ffffffb
++#define SHT_SUNW_syminfo 0x6ffffffc
++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
++#define SHT_HIOS 0x6fffffff /* End OS-specific type */
++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
++#define SHT_LOUSER 0x80000000 /* Start of application-specific */
++#define SHT_HIUSER 0x8fffffff /* End of application-specific */
++
++/* Legal values for sh_flags (section flags). */
++
++#define SHF_WRITE (1 << 0) /* Writable */
++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
++#define SHF_EXECINSTR (1 << 2) /* Executable */
++#define SHF_MERGE (1 << 4) /* Might be merged */
++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */
++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */
++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */
++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling
++ required */
++#define SHF_GROUP (1 << 9) /* Section is member of a group. */
++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */
++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */
++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */
++#define SHF_ORDERED (1 << 30) /* Special ordering requirement
++ (Solaris). */
++#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless
++ referenced or allocated (Solaris).*/
++
++/* Section group handling. */
++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */
++
++/* Symbol table entry. */
++
++typedef struct
++{
++ Elf32_Word st_name; /* Symbol name (string tbl index) */
++ Elf32_Addr st_value; /* Symbol value */
++ Elf32_Word st_size; /* Symbol size */
++ unsigned char st_info; /* Symbol type and binding */
++ unsigned char st_other; /* Symbol visibility */
++ Elf32_Section st_shndx; /* Section index */
++} Elf32_Sym;
++
++typedef struct
++{
++ Elf64_Word st_name; /* Symbol name (string tbl index) */
++ unsigned char st_info; /* Symbol type and binding */
++ unsigned char st_other; /* Symbol visibility */
++ Elf64_Section st_shndx; /* Section index */
++ Elf64_Addr st_value; /* Symbol value */
++ Elf64_Xword st_size; /* Symbol size */
++} Elf64_Sym;
++
++/* The syminfo section if available contains additional information about
++ every dynamic symbol. */
++
++typedef struct
++{
++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */
++ Elf32_Half si_flags; /* Per symbol flags */
++} Elf32_Syminfo;
++
++typedef struct
++{
++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */
++ Elf64_Half si_flags; /* Per symbol flags */
++} Elf64_Syminfo;
++
++/* Possible values for si_boundto. */
++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */
++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */
++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */
++
++/* Possible bitmasks for si_flags. */
++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */
++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */
++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */
++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy
++ loaded */
++/* Syminfo version values. */
++#define SYMINFO_NONE 0
++#define SYMINFO_CURRENT 1
++#define SYMINFO_NUM 2
++
++
++/* How to extract and insert information held in the st_info field. */
++
++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
++#define ELF32_ST_TYPE(val) ((val) & 0xf)
++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
++
++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */
++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
++
++/* Legal values for ST_BIND subfield of st_info (symbol binding). */
++
++#define STB_LOCAL 0 /* Local symbol */
++#define STB_GLOBAL 1 /* Global symbol */
++#define STB_WEAK 2 /* Weak symbol */
++#define STB_NUM 3 /* Number of defined types. */
++#define STB_LOOS 10 /* Start of OS-specific */
++#define STB_GNU_UNIQUE 10 /* Unique symbol. */
++#define STB_HIOS 12 /* End of OS-specific */
++#define STB_LOPROC 13 /* Start of processor-specific */
++#define STB_HIPROC 15 /* End of processor-specific */
++
++/* Legal values for ST_TYPE subfield of st_info (symbol type). */
++
++#define STT_NOTYPE 0 /* Symbol type is unspecified */
++#define STT_OBJECT 1 /* Symbol is a data object */
++#define STT_FUNC 2 /* Symbol is a code object */
++#define STT_SECTION 3 /* Symbol associated with a section */
++#define STT_FILE 4 /* Symbol's name is file name */
++#define STT_COMMON 5 /* Symbol is a common data object */
++#define STT_TLS 6 /* Symbol is thread-local data object*/
++#define STT_NUM 7 /* Number of defined types. */
++#define STT_LOOS 10 /* Start of OS-specific */
++#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */
++#define STT_HIOS 12 /* End of OS-specific */
++#define STT_LOPROC 13 /* Start of processor-specific */
++#define STT_HIPROC 15 /* End of processor-specific */
++
++
++/* Symbol table indices are found in the hash buckets and chain table
++ of a symbol hash table section. This special index value indicates
++ the end of a chain, meaning no further symbols are found in that bucket. */
++
++#define STN_UNDEF 0 /* End of a chain. */
++
++
++/* How to extract and insert information held in the st_other field. */
++
++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
++
++/* For ELF64 the definitions are the same. */
++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
++
++/* Symbol visibility specification encoded in the st_other field. */
++#define STV_DEFAULT 0 /* Default symbol visibility rules */
++#define STV_INTERNAL 1 /* Processor specific hidden class */
++#define STV_HIDDEN 2 /* Sym unavailable in other modules */
++#define STV_PROTECTED 3 /* Not preemptible, not exported */
++
++
++/* Relocation table entry without addend (in section of type SHT_REL). */
++
++typedef struct
++{
++ Elf32_Addr r_offset; /* Address */
++ Elf32_Word r_info; /* Relocation type and symbol index */
++} Elf32_Rel;
++
++/* I have seen two different definitions of the Elf64_Rel and
++ Elf64_Rela structures, so we'll leave them out until Novell (or
++ whoever) gets their act together. */
++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */
++
++typedef struct
++{
++ Elf64_Addr r_offset; /* Address */
++ Elf64_Xword r_info; /* Relocation type and symbol index */
++} Elf64_Rel;
++
++/* Relocation table entry with addend (in section of type SHT_RELA). */
++
++typedef struct
++{
++ Elf32_Addr r_offset; /* Address */
++ Elf32_Word r_info; /* Relocation type and symbol index */
++ Elf32_Sword r_addend; /* Addend */
++} Elf32_Rela;
++
++typedef struct
++{
++ Elf64_Addr r_offset; /* Address */
++ Elf64_Xword r_info; /* Relocation type and symbol index */
++ Elf64_Sxword r_addend; /* Addend */
++} Elf64_Rela;
++
++/* How to extract and insert information held in the r_info field. */
++
++#define ELF32_R_SYM(val) ((val) >> 8)
++#define ELF32_R_TYPE(val) ((val) & 0xff)
++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
++
++#define ELF64_R_SYM(i) ((i) >> 32)
++#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
++
++/* Program segment header. */
++
++typedef struct
++{
++ Elf32_Word p_type; /* Segment type */
++ Elf32_Off p_offset; /* Segment file offset */
++ Elf32_Addr p_vaddr; /* Segment virtual address */
++ Elf32_Addr p_paddr; /* Segment physical address */
++ Elf32_Word p_filesz; /* Segment size in file */
++ Elf32_Word p_memsz; /* Segment size in memory */
++ Elf32_Word p_flags; /* Segment flags */
++ Elf32_Word p_align; /* Segment alignment */
++} Elf32_Phdr;
++
++typedef struct
++{
++ Elf64_Word p_type; /* Segment type */
++ Elf64_Word p_flags; /* Segment flags */
++ Elf64_Off p_offset; /* Segment file offset */
++ Elf64_Addr p_vaddr; /* Segment virtual address */
++ Elf64_Addr p_paddr; /* Segment physical address */
++ Elf64_Xword p_filesz; /* Segment size in file */
++ Elf64_Xword p_memsz; /* Segment size in memory */
++ Elf64_Xword p_align; /* Segment alignment */
++} Elf64_Phdr;
++
++/* Special value for e_phnum. This indicates that the real number of
++ program headers is too large to fit into e_phnum. Instead the real
++ value is in the field sh_info of section 0. */
++
++#define PN_XNUM 0xffff
++
++/* Legal values for p_type (segment type). */
++
++#define PT_NULL 0 /* Program header table entry unused */
++#define PT_LOAD 1 /* Loadable program segment */
++#define PT_DYNAMIC 2 /* Dynamic linking information */
++#define PT_INTERP 3 /* Program interpreter */
++#define PT_NOTE 4 /* Auxiliary information */
++#define PT_SHLIB 5 /* Reserved */
++#define PT_PHDR 6 /* Entry for header table itself */
++#define PT_TLS 7 /* Thread-local storage segment */
++#define PT_NUM 8 /* Number of defined types */
++#define PT_LOOS 0x60000000 /* Start of OS-specific */
++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */
++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */
++#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
++#define PT_LOSUNW 0x6ffffffa
++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */
++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */
++#define PT_HISUNW 0x6fffffff
++#define PT_HIOS 0x6fffffff /* End of OS-specific */
++#define PT_LOPROC 0x70000000 /* Start of processor-specific */
++#define PT_HIPROC 0x7fffffff /* End of processor-specific */
++
++/* Legal values for p_flags (segment flags). */
++
++#define PF_X (1 << 0) /* Segment is executable */
++#define PF_W (1 << 1) /* Segment is writable */
++#define PF_R (1 << 2) /* Segment is readable */
++#define PF_MASKOS 0x0ff00000 /* OS-specific */
++#define PF_MASKPROC 0xf0000000 /* Processor-specific */
++
++/* Legal values for note segment descriptor types for core files. */
++
++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */
++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */
++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */
++#define NT_PRXREG 4 /* Contains copy of prxregset struct */
++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */
++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */
++#define NT_AUXV 6 /* Contains copy of auxv array */
++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */
++#define NT_ASRS 8 /* Contains copy of asrset struct */
++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */
++#define NT_PSINFO 13 /* Contains copy of psinfo struct */
++#define NT_PRCRED 14 /* Contains copy of prcred struct */
++#define NT_UTSNAME 15 /* Contains copy of utsname struct */
++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */
++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */
++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */
++#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */
++#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
++#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
++#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
++#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
++#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
++#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
++
++/* Legal values for the note segment descriptor types for object files. */
++
++#define NT_VERSION 1 /* Contains a version string. */
++
++
++/* Dynamic section entry. */
++
++typedef struct
++{
++ Elf32_Sword d_tag; /* Dynamic entry type */
++ union
++ {
++ Elf32_Word d_val; /* Integer value */
++ Elf32_Addr d_ptr; /* Address value */
++ } d_un;
++} Elf32_Dyn;
++
++typedef struct
++{
++ Elf64_Sxword d_tag; /* Dynamic entry type */
++ union
++ {
++ Elf64_Xword d_val; /* Integer value */
++ Elf64_Addr d_ptr; /* Address value */
++ } d_un;
++} Elf64_Dyn;
++
++/* Legal values for d_tag (dynamic entry type). */
++
++#define DT_NULL 0 /* Marks end of dynamic section */
++#define DT_NEEDED 1 /* Name of needed library */
++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */
++#define DT_PLTGOT 3 /* Processor defined value */
++#define DT_HASH 4 /* Address of symbol hash table */
++#define DT_STRTAB 5 /* Address of string table */
++#define DT_SYMTAB 6 /* Address of symbol table */
++#define DT_RELA 7 /* Address of Rela relocs */
++#define DT_RELASZ 8 /* Total size of Rela relocs */
++#define DT_RELAENT 9 /* Size of one Rela reloc */
++#define DT_STRSZ 10 /* Size of string table */
++#define DT_SYMENT 11 /* Size of one symbol table entry */
++#define DT_INIT 12 /* Address of init function */
++#define DT_FINI 13 /* Address of termination function */
++#define DT_SONAME 14 /* Name of shared object */
++#define DT_RPATH 15 /* Library search path (deprecated) */
++#define DT_SYMBOLIC 16 /* Start symbol search here */
++#define DT_REL 17 /* Address of Rel relocs */
++#define DT_RELSZ 18 /* Total size of Rel relocs */
++#define DT_RELENT 19 /* Size of one Rel reloc */
++#define DT_PLTREL 20 /* Type of reloc in PLT */
++#define DT_DEBUG 21 /* For debugging; unspecified */
++#define DT_TEXTREL 22 /* Reloc might modify .text */
++#define DT_JMPREL 23 /* Address of PLT relocs */
++#define DT_BIND_NOW 24 /* Process relocations of object */
++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */
++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */
++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
++#define DT_RUNPATH 29 /* Library search path */
++#define DT_FLAGS 30 /* Flags for the object being loaded */
++#define DT_ENCODING 32 /* Start of encoded range */
++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/
++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */
++#define DT_NUM 34 /* Number used */
++#define DT_LOOS 0x6000000d /* Start of OS-specific */
++#define DT_HIOS 0x6ffff000 /* End of OS-specific */
++#define DT_LOPROC 0x70000000 /* Start of processor-specific */
++#define DT_HIPROC 0x7fffffff /* End of processor-specific */
++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */
++
++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's
++ approach. */
++#define DT_VALRNGLO 0x6ffffd00
++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */
++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */
++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */
++#define DT_CHECKSUM 0x6ffffdf8
++#define DT_PLTPADSZ 0x6ffffdf9
++#define DT_MOVEENT 0x6ffffdfa
++#define DT_MOVESZ 0x6ffffdfb
++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */
++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting
++ the following DT_* entry. */
++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */
++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */
++#define DT_VALRNGHI 0x6ffffdff
++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */
++#define DT_VALNUM 12
++
++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
++
++ If any adjustment is made to the ELF object after it has been
++ built these entries will need to be adjusted. */
++#define DT_ADDRRNGLO 0x6ffffe00
++#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */
++#define DT_TLSDESC_PLT 0x6ffffef6
++#define DT_TLSDESC_GOT 0x6ffffef7
++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */
++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */
++#define DT_CONFIG 0x6ffffefa /* Configuration information. */
++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */
++#define DT_AUDIT 0x6ffffefc /* Object auditing. */
++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */
++#define DT_MOVETAB 0x6ffffefe /* Move table. */
++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */
++#define DT_ADDRRNGHI 0x6ffffeff
++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */
++#define DT_ADDRNUM 11
++
++/* The versioning entry types. The next are defined as part of the
++ GNU extension. */
++#define DT_VERSYM 0x6ffffff0
++
++#define DT_RELACOUNT 0x6ffffff9
++#define DT_RELCOUNT 0x6ffffffa
++
++/* These were chosen by Sun. */
++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */
++#define DT_VERDEF 0x6ffffffc /* Address of version definition
++ table */
++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */
++#define DT_VERNEED 0x6ffffffe /* Address of table with needed
++ versions */
++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */
++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */
++#define DT_VERSIONTAGNUM 16
++
++/* Sun added these machine-independent extensions in the "processor-specific"
++ range. Be compatible. */
++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */
++#define DT_FILTER 0x7fffffff /* Shared object to get values from */
++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
++#define DT_EXTRANUM 3
++
++/* Values of `d_un.d_val' in the DT_FLAGS entry. */
++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */
++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */
++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */
++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */
++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */
++
++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1
++ entry in the dynamic section. */
++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */
++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */
++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */
++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/
++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/
++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/
++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */
++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */
++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */
++#define DF_1_TRANS 0x00000200
++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */
++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */
++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */
++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/
++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */
++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */
++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */
++
++/* Flags for the feature selection in DT_FEATURE_1. */
++#define DTF_1_PARINIT 0x00000001
++#define DTF_1_CONFEXP 0x00000002
++
++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */
++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */
++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not
++ generally available. */
++
++/* Version definition sections. */
++
++typedef struct
++{
++ Elf32_Half vd_version; /* Version revision */
++ Elf32_Half vd_flags; /* Version information */
++ Elf32_Half vd_ndx; /* Version Index */
++ Elf32_Half vd_cnt; /* Number of associated aux entries */
++ Elf32_Word vd_hash; /* Version name hash value */
++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */
++ Elf32_Word vd_next; /* Offset in bytes to next verdef
++ entry */
++} Elf32_Verdef;
++
++typedef struct
++{
++ Elf64_Half vd_version; /* Version revision */
++ Elf64_Half vd_flags; /* Version information */
++ Elf64_Half vd_ndx; /* Version Index */
++ Elf64_Half vd_cnt; /* Number of associated aux entries */
++ Elf64_Word vd_hash; /* Version name hash value */
++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */
++ Elf64_Word vd_next; /* Offset in bytes to next verdef
++ entry */
++} Elf64_Verdef;
++
++
++/* Legal values for vd_version (version revision). */
++#define VER_DEF_NONE 0 /* No version */
++#define VER_DEF_CURRENT 1 /* Current version */
++#define VER_DEF_NUM 2 /* Given version number */
++
++/* Legal values for vd_flags (version information flags). */
++#define VER_FLG_BASE 0x1 /* Version definition of file itself */
++#define VER_FLG_WEAK 0x2 /* Weak version identifier */
++
++/* Versym symbol index values. */
++#define VER_NDX_LOCAL 0 /* Symbol is local. */
++#define VER_NDX_GLOBAL 1 /* Symbol is global. */
++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */
++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */
++
++/* Auxialiary version information. */
++
++typedef struct
++{
++ Elf32_Word vda_name; /* Version or dependency names */
++ Elf32_Word vda_next; /* Offset in bytes to next verdaux
++ entry */
++} Elf32_Verdaux;
++
++typedef struct
++{
++ Elf64_Word vda_name; /* Version or dependency names */
++ Elf64_Word vda_next; /* Offset in bytes to next verdaux
++ entry */
++} Elf64_Verdaux;
++
++
++/* Version dependency section. */
++
++typedef struct
++{
++ Elf32_Half vn_version; /* Version of structure */
++ Elf32_Half vn_cnt; /* Number of associated aux entries */
++ Elf32_Word vn_file; /* Offset of filename for this
++ dependency */
++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */
++ Elf32_Word vn_next; /* Offset in bytes to next verneed
++ entry */
++} Elf32_Verneed;
++
++typedef struct
++{
++ Elf64_Half vn_version; /* Version of structure */
++ Elf64_Half vn_cnt; /* Number of associated aux entries */
++ Elf64_Word vn_file; /* Offset of filename for this
++ dependency */
++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */
++ Elf64_Word vn_next; /* Offset in bytes to next verneed
++ entry */
++} Elf64_Verneed;
++
++
++/* Legal values for vn_version (version revision). */
++#define VER_NEED_NONE 0 /* No version */
++#define VER_NEED_CURRENT 1 /* Current version */
++#define VER_NEED_NUM 2 /* Given version number */
++
++/* Auxiliary needed version information. */
++
++typedef struct
++{
++ Elf32_Word vna_hash; /* Hash value of dependency name */
++ Elf32_Half vna_flags; /* Dependency specific information */
++ Elf32_Half vna_other; /* Unused */
++ Elf32_Word vna_name; /* Dependency name string offset */
++ Elf32_Word vna_next; /* Offset in bytes to next vernaux
++ entry */
++} Elf32_Vernaux;
++
++typedef struct
++{
++ Elf64_Word vna_hash; /* Hash value of dependency name */
++ Elf64_Half vna_flags; /* Dependency specific information */
++ Elf64_Half vna_other; /* Unused */
++ Elf64_Word vna_name; /* Dependency name string offset */
++ Elf64_Word vna_next; /* Offset in bytes to next vernaux
++ entry */
++} Elf64_Vernaux;
++
++
++/* Legal values for vna_flags. */
++#define VER_FLG_WEAK 0x2 /* Weak version identifier */
++
++
++/* Auxiliary vector. */
++
++/* This vector is normally only used by the program interpreter. The
++ usual definition in an ABI supplement uses the name auxv_t. The
++ vector is not usually defined in a standard <elf.h> file, but it
++ can't hurt. We rename it to avoid conflicts. The sizes of these
++ types are an arrangement between the exec server and the program
++ interpreter, so we don't fully specify them here. */
++
++typedef struct
++{
++ uint32_t a_type; /* Entry type */
++ union
++ {
++ uint32_t a_val; /* Integer value */
++ /* We use to have pointer elements added here. We cannot do that,
++ though, since it does not work when using 32-bit definitions
++ on 64-bit platforms and vice versa. */
++ } a_un;
++} Elf32_auxv_t;
++
++typedef struct
++{
++ uint64_t a_type; /* Entry type */
++ union
++ {
++ uint64_t a_val; /* Integer value */
++ /* We use to have pointer elements added here. We cannot do that,
++ though, since it does not work when using 32-bit definitions
++ on 64-bit platforms and vice versa. */
++ } a_un;
++} Elf64_auxv_t;
++
++/* Legal values for a_type (entry type). */
++
++#define AT_NULL 0 /* End of vector */
++#define AT_IGNORE 1 /* Entry should be ignored */
++#define AT_EXECFD 2 /* File descriptor of program */
++#define AT_PHDR 3 /* Program headers for program */
++#define AT_PHENT 4 /* Size of program header entry */
++#define AT_PHNUM 5 /* Number of program headers */
++#define AT_PAGESZ 6 /* System page size */
++#define AT_BASE 7 /* Base address of interpreter */
++#define AT_FLAGS 8 /* Flags */
++#define AT_ENTRY 9 /* Entry point of program */
++#define AT_NOTELF 10 /* Program is not ELF */
++#define AT_UID 11 /* Real uid */
++#define AT_EUID 12 /* Effective uid */
++#define AT_GID 13 /* Real gid */
++#define AT_EGID 14 /* Effective gid */
++#define AT_CLKTCK 17 /* Frequency of times() */
++
++/* Some more special a_type values describing the hardware. */
++#define AT_PLATFORM 15 /* String identifying platform. */
++#define AT_HWCAP 16 /* Machine dependent hints about
++ processor capabilities. */
++
++/* This entry gives some information about the FPU initialization
++ performed by the kernel. */
++#define AT_FPUCW 18 /* Used FPU control word. */
++
++/* Cache block sizes. */
++#define AT_DCACHEBSIZE 19 /* Data cache block size. */
++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */
++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */
++
++/* A special ignored value for PPC, used by the kernel to control the
++ interpretation of the AUXV. Must be > 16. */
++#define AT_IGNOREPPC 22 /* Entry should be ignored. */
++
++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */
++
++#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/
++
++#define AT_RANDOM 25 /* Address of 16 random bytes. */
++
++#define AT_EXECFN 31 /* Filename of executable. */
++
++/* Pointer to the global system page used for system calls and other
++ nice things. */
++#define AT_SYSINFO 32
++#define AT_SYSINFO_EHDR 33
++
++/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains
++ log2 of line size; mask those to get cache size. */
++#define AT_L1I_CACHESHAPE 34
++#define AT_L1D_CACHESHAPE 35
++#define AT_L2_CACHESHAPE 36
++#define AT_L3_CACHESHAPE 37
++
++/* Note section contents. Each entry in the note section begins with
++ a header of a fixed form. */
++
++typedef struct
++{
++ Elf32_Word n_namesz; /* Length of the note's name. */
++ Elf32_Word n_descsz; /* Length of the note's descriptor. */
++ Elf32_Word n_type; /* Type of the note. */
++} Elf32_Nhdr;
++
++typedef struct
++{
++ Elf64_Word n_namesz; /* Length of the note's name. */
++ Elf64_Word n_descsz; /* Length of the note's descriptor. */
++ Elf64_Word n_type; /* Type of the note. */
++} Elf64_Nhdr;
++
++/* Known names of notes. */
++
++/* Solaris entries in the note section have this name. */
++#define ELF_NOTE_SOLARIS "SUNW Solaris"
++
++/* Note entries for GNU systems have this name. */
++#define ELF_NOTE_GNU "GNU"
++
++
++/* Defined types of notes for Solaris. */
++
++/* Value of descriptor (one word) is desired pagesize for the binary. */
++#define ELF_NOTE_PAGESIZE_HINT 1
++
++
++/* Defined note types for GNU systems. */
++
++/* ABI information. The descriptor consists of words:
++ word 0: OS descriptor
++ word 1: major version of the ABI
++ word 2: minor version of the ABI
++ word 3: subminor version of the ABI
++*/
++#define NT_GNU_ABI_TAG 1
++#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */
++
++/* Known OSes. These values can appear in word 0 of an
++ NT_GNU_ABI_TAG note section entry. */
++#define ELF_NOTE_OS_LINUX 0
++#define ELF_NOTE_OS_GNU 1
++#define ELF_NOTE_OS_SOLARIS2 2
++#define ELF_NOTE_OS_FREEBSD 3
++
++/* Synthetic hwcap information. The descriptor begins with two words:
++ word 0: number of entries
++ word 1: bitmask of enabled entries
++ Then follow variable-length entries, one byte followed by a
++ '\0'-terminated hwcap name string. The byte gives the bit
++ number to test if enabled, (1U << bit) & bitmask. */
++#define NT_GNU_HWCAP 2
++
++/* Build ID bits as generated by ld --build-id.
++ The descriptor consists of any nonzero number of bytes. */
++#define NT_GNU_BUILD_ID 3
++
++/* Version note generated by GNU gold containing a version string. */
++#define NT_GNU_GOLD_VERSION 4
++
++
++/* Move records. */
++typedef struct
++{
++ Elf32_Xword m_value; /* Symbol value. */
++ Elf32_Word m_info; /* Size and index. */
++ Elf32_Word m_poffset; /* Symbol offset. */
++ Elf32_Half m_repeat; /* Repeat count. */
++ Elf32_Half m_stride; /* Stride info. */
++} Elf32_Move;
++
++typedef struct
++{
++ Elf64_Xword m_value; /* Symbol value. */
++ Elf64_Xword m_info; /* Size and index. */
++ Elf64_Xword m_poffset; /* Symbol offset. */
++ Elf64_Half m_repeat; /* Repeat count. */
++ Elf64_Half m_stride; /* Stride info. */
++} Elf64_Move;
++
++/* Macro to construct move records. */
++#define ELF32_M_SYM(info) ((info) >> 8)
++#define ELF32_M_SIZE(info) ((unsigned char) (info))
++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size))
++
++#define ELF64_M_SYM(info) ELF32_M_SYM (info)
++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info)
++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size)
++
++
++/* Motorola 68k specific definitions. */
++
++/* Values for Elf32_Ehdr.e_flags. */
++#define EF_CPU32 0x00810000
++
++/* m68k relocs. */
++
++#define R_68K_NONE 0 /* No reloc */
++#define R_68K_32 1 /* Direct 32 bit */
++#define R_68K_16 2 /* Direct 16 bit */
++#define R_68K_8 3 /* Direct 8 bit */
++#define R_68K_PC32 4 /* PC relative 32 bit */
++#define R_68K_PC16 5 /* PC relative 16 bit */
++#define R_68K_PC8 6 /* PC relative 8 bit */
++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */
++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */
++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */
++#define R_68K_GOT32O 10 /* 32 bit GOT offset */
++#define R_68K_GOT16O 11 /* 16 bit GOT offset */
++#define R_68K_GOT8O 12 /* 8 bit GOT offset */
++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */
++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */
++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */
++#define R_68K_PLT32O 16 /* 32 bit PLT offset */
++#define R_68K_PLT16O 17 /* 16 bit PLT offset */
++#define R_68K_PLT8O 18 /* 8 bit PLT offset */
++#define R_68K_COPY 19 /* Copy symbol at runtime */
++#define R_68K_GLOB_DAT 20 /* Create GOT entry */
++#define R_68K_JMP_SLOT 21 /* Create PLT entry */
++#define R_68K_RELATIVE 22 /* Adjust by program base */
++#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */
++#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */
++#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */
++#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */
++#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */
++#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */
++#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */
++#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */
++#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */
++#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */
++#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */
++#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */
++#define R_68K_TLS_LE32 37 /* 32 bit offset relative to
++ static TLS block */
++#define R_68K_TLS_LE16 38 /* 16 bit offset relative to
++ static TLS block */
++#define R_68K_TLS_LE8 39 /* 8 bit offset relative to
++ static TLS block */
++#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */
++#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */
++#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */
++/* Keep this the last entry. */
++#define R_68K_NUM 43
++
++/* Intel 80386 specific definitions. */
++
++/* i386 relocs. */
++
++#define R_386_NONE 0 /* No reloc */
++#define R_386_32 1 /* Direct 32 bit */
++#define R_386_PC32 2 /* PC relative 32 bit */
++#define R_386_GOT32 3 /* 32 bit GOT entry */
++#define R_386_PLT32 4 /* 32 bit PLT address */
++#define R_386_COPY 5 /* Copy symbol at runtime */
++#define R_386_GLOB_DAT 6 /* Create GOT entry */
++#define R_386_JMP_SLOT 7 /* Create PLT entry */
++#define R_386_RELATIVE 8 /* Adjust by program base */
++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */
++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */
++#define R_386_32PLT 11
++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */
++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS
++ block offset */
++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block
++ offset */
++#define R_386_TLS_LE 17 /* Offset relative to static TLS
++ block */
++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of
++ general dynamic thread local data */
++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of
++ local dynamic thread local data
++ in LE code */
++#define R_386_16 20
++#define R_386_PC16 21
++#define R_386_8 22
++#define R_386_PC8 23
++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic
++ thread local data */
++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */
++#define R_386_TLS_GD_CALL 26 /* Relocation for call to
++ __tls_get_addr() */
++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */
++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic
++ thread local data in LE code */
++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */
++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to
++ __tls_get_addr() in LDM code */
++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */
++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */
++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS
++ block offset */
++#define R_386_TLS_LE_32 34 /* Negated offset relative to static
++ TLS block */
++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */
++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */
++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */
++/* 38? */
++#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */
++#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS
++ descriptor for
++ relaxation. */
++#define R_386_TLS_DESC 41 /* TLS descriptor containing
++ pointer to code and to
++ argument, returning the TLS
++ offset for the symbol. */
++#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */
++/* Keep this the last entry. */
++#define R_386_NUM 43
++
++/* SUN SPARC specific definitions. */
++
++/* Legal values for ST_TYPE subfield of st_info (symbol type). */
++
++#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */
++
++/* Values for Elf64_Ehdr.e_flags. */
++
++#define EF_SPARCV9_MM 3
++#define EF_SPARCV9_TSO 0
++#define EF_SPARCV9_PSO 1
++#define EF_SPARCV9_RMO 2
++#define EF_SPARC_LEDATA 0x800000 /* little endian data */
++#define EF_SPARC_EXT_MASK 0xFFFF00
++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */
++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */
++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */
++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */
++
++/* SPARC relocs. */
++
++#define R_SPARC_NONE 0 /* No reloc */
++#define R_SPARC_8 1 /* Direct 8 bit */
++#define R_SPARC_16 2 /* Direct 16 bit */
++#define R_SPARC_32 3 /* Direct 32 bit */
++#define R_SPARC_DISP8 4 /* PC relative 8 bit */
++#define R_SPARC_DISP16 5 /* PC relative 16 bit */
++#define R_SPARC_DISP32 6 /* PC relative 32 bit */
++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */
++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */
++#define R_SPARC_HI22 9 /* High 22 bit */
++#define R_SPARC_22 10 /* Direct 22 bit */
++#define R_SPARC_13 11 /* Direct 13 bit */
++#define R_SPARC_LO10 12 /* Truncated 10 bit */
++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */
++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */
++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */
++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */
++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */
++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */
++#define R_SPARC_COPY 19 /* Copy symbol at runtime */
++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */
++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */
++#define R_SPARC_RELATIVE 22 /* Adjust by program base */
++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */
++
++/* Additional Sparc64 relocs. */
++
++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */
++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */
++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */
++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */
++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */
++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */
++#define R_SPARC_10 30 /* Direct 10 bit */
++#define R_SPARC_11 31 /* Direct 11 bit */
++#define R_SPARC_64 32 /* Direct 64 bit */
++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */
++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */
++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */
++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */
++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */
++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */
++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */
++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */
++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */
++#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */
++#define R_SPARC_7 43 /* Direct 7 bit */
++#define R_SPARC_5 44 /* Direct 5 bit */
++#define R_SPARC_6 45 /* Direct 6 bit */
++#define R_SPARC_DISP64 46 /* PC relative 64 bit */
++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */
++#define R_SPARC_HIX22 48 /* High 22 bit complemented */
++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */
++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */
++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */
++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */
++#define R_SPARC_REGISTER 53 /* Global register usage */
++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */
++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */
++#define R_SPARC_TLS_GD_HI22 56
++#define R_SPARC_TLS_GD_LO10 57
++#define R_SPARC_TLS_GD_ADD 58
++#define R_SPARC_TLS_GD_CALL 59
++#define R_SPARC_TLS_LDM_HI22 60
++#define R_SPARC_TLS_LDM_LO10 61
++#define R_SPARC_TLS_LDM_ADD 62
++#define R_SPARC_TLS_LDM_CALL 63
++#define R_SPARC_TLS_LDO_HIX22 64
++#define R_SPARC_TLS_LDO_LOX10 65
++#define R_SPARC_TLS_LDO_ADD 66
++#define R_SPARC_TLS_IE_HI22 67
++#define R_SPARC_TLS_IE_LO10 68
++#define R_SPARC_TLS_IE_LD 69
++#define R_SPARC_TLS_IE_LDX 70
++#define R_SPARC_TLS_IE_ADD 71
++#define R_SPARC_TLS_LE_HIX22 72
++#define R_SPARC_TLS_LE_LOX10 73
++#define R_SPARC_TLS_DTPMOD32 74
++#define R_SPARC_TLS_DTPMOD64 75
++#define R_SPARC_TLS_DTPOFF32 76
++#define R_SPARC_TLS_DTPOFF64 77
++#define R_SPARC_TLS_TPOFF32 78
++#define R_SPARC_TLS_TPOFF64 79
++#define R_SPARC_GOTDATA_HIX22 80
++#define R_SPARC_GOTDATA_LOX10 81
++#define R_SPARC_GOTDATA_OP_HIX22 82
++#define R_SPARC_GOTDATA_OP_LOX10 83
++#define R_SPARC_GOTDATA_OP 84
++#define R_SPARC_H34 85
++#define R_SPARC_SIZE32 86
++#define R_SPARC_SIZE64 87
++#define R_SPARC_WDISP10 88
++#define R_SPARC_JMP_IREL 248
++#define R_SPARC_IRELATIVE 249
++#define R_SPARC_GNU_VTINHERIT 250
++#define R_SPARC_GNU_VTENTRY 251
++#define R_SPARC_REV32 252
++/* Keep this the last entry. */
++#define R_SPARC_NUM 253
++
++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */
++
++#define DT_SPARC_REGISTER 0x70000001
++#define DT_SPARC_NUM 2
++
++/* MIPS R3000 specific definitions. */
++
++/* Legal values for e_flags field of Elf32_Ehdr. */
++
++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */
++#define EF_MIPS_PIC 2 /* Contains PIC code */
++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */
++#define EF_MIPS_XGOT 8
++#define EF_MIPS_64BIT_WHIRL 16
++#define EF_MIPS_ABI2 32
++#define EF_MIPS_ABI_ON32 64
++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */
++
++/* Legal values for MIPS architecture level. */
++
++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */
++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */
++
++/* The following are non-official names and should not be used. */
++
++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */
++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */
++
++/* Special section indices. */
++
++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */
++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */
++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */
++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */
++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */
++
++/* Legal values for sh_type field of Elf32_Shdr. */
++
++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */
++#define SHT_MIPS_MSYM 0x70000001
++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */
++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */
++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */
++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/
++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */
++#define SHT_MIPS_PACKAGE 0x70000007
++#define SHT_MIPS_PACKSYM 0x70000008
++#define SHT_MIPS_RELD 0x70000009
++#define SHT_MIPS_IFACE 0x7000000b
++#define SHT_MIPS_CONTENT 0x7000000c
++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */
++#define SHT_MIPS_SHDR 0x70000010
++#define SHT_MIPS_FDESC 0x70000011
++#define SHT_MIPS_EXTSYM 0x70000012
++#define SHT_MIPS_DENSE 0x70000013
++#define SHT_MIPS_PDESC 0x70000014
++#define SHT_MIPS_LOCSYM 0x70000015
++#define SHT_MIPS_AUXSYM 0x70000016
++#define SHT_MIPS_OPTSYM 0x70000017
++#define SHT_MIPS_LOCSTR 0x70000018
++#define SHT_MIPS_LINE 0x70000019
++#define SHT_MIPS_RFDESC 0x7000001a
++#define SHT_MIPS_DELTASYM 0x7000001b
++#define SHT_MIPS_DELTAINST 0x7000001c
++#define SHT_MIPS_DELTACLASS 0x7000001d
++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */
++#define SHT_MIPS_DELTADECL 0x7000001f
++#define SHT_MIPS_SYMBOL_LIB 0x70000020
++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */
++#define SHT_MIPS_TRANSLATE 0x70000022
++#define SHT_MIPS_PIXIE 0x70000023
++#define SHT_MIPS_XLATE 0x70000024
++#define SHT_MIPS_XLATE_DEBUG 0x70000025
++#define SHT_MIPS_WHIRL 0x70000026
++#define SHT_MIPS_EH_REGION 0x70000027
++#define SHT_MIPS_XLATE_OLD 0x70000028
++#define SHT_MIPS_PDR_EXCEPTION 0x70000029
++
++/* Legal values for sh_flags field of Elf32_Shdr. */
++
++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */
++#define SHF_MIPS_MERGE 0x20000000
++#define SHF_MIPS_ADDR 0x40000000
++#define SHF_MIPS_STRINGS 0x80000000
++#define SHF_MIPS_NOSTRIP 0x08000000
++#define SHF_MIPS_LOCAL 0x04000000
++#define SHF_MIPS_NAMES 0x02000000
++#define SHF_MIPS_NODUPE 0x01000000
++
++
++/* Symbol tables. */
++
++/* MIPS specific values for `st_other'. */
++#define STO_MIPS_DEFAULT 0x0
++#define STO_MIPS_INTERNAL 0x1
++#define STO_MIPS_HIDDEN 0x2
++#define STO_MIPS_PROTECTED 0x3
++#define STO_MIPS_PLT 0x8
++#define STO_MIPS_SC_ALIGN_UNUSED 0xff
++
++/* MIPS specific values for `st_info'. */
++#define STB_MIPS_SPLIT_COMMON 13
++
++/* Entries found in sections of type SHT_MIPS_GPTAB. */
++
++typedef union
++{
++ struct
++ {
++ Elf32_Word gt_current_g_value; /* -G value used for compilation */
++ Elf32_Word gt_unused; /* Not used */
++ } gt_header; /* First entry in section */
++ struct
++ {
++ Elf32_Word gt_g_value; /* If this value were used for -G */
++ Elf32_Word gt_bytes; /* This many bytes would be used */
++ } gt_entry; /* Subsequent entries in section */
++} Elf32_gptab;
++
++/* Entry found in sections of type SHT_MIPS_REGINFO. */
++
++typedef struct
++{
++ Elf32_Word ri_gprmask; /* General registers used */
++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */
++ Elf32_Sword ri_gp_value; /* $gp register value */
++} Elf32_RegInfo;
++
++/* Entries found in sections of type SHT_MIPS_OPTIONS. */
++
++typedef struct
++{
++ unsigned char kind; /* Determines interpretation of the
++ variable part of descriptor. */
++ unsigned char size; /* Size of descriptor, including header. */
++ Elf32_Section section; /* Section header index of section affected,
++ 0 for global options. */
++ Elf32_Word info; /* Kind-specific information. */
++} Elf_Options;
++
++/* Values for `kind' field in Elf_Options. */
++
++#define ODK_NULL 0 /* Undefined. */
++#define ODK_REGINFO 1 /* Register usage information. */
++#define ODK_EXCEPTIONS 2 /* Exception processing options. */
++#define ODK_PAD 3 /* Section padding options. */
++#define ODK_HWPATCH 4 /* Hardware workarounds performed */
++#define ODK_FILL 5 /* record the fill value used by the linker. */
++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */
++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */
++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */
++
++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */
++
++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */
++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */
++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */
++#define OEX_SMM 0x20000 /* Force sequential memory mode? */
++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */
++#define OEX_PRECISEFP OEX_FPDBUG
++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */
++
++#define OEX_FPU_INVAL 0x10
++#define OEX_FPU_DIV0 0x08
++#define OEX_FPU_OFLO 0x04
++#define OEX_FPU_UFLO 0x02
++#define OEX_FPU_INEX 0x01
++
++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */
++
++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */
++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */
++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */
++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */
++
++#define OPAD_PREFIX 0x1
++#define OPAD_POSTFIX 0x2
++#define OPAD_SYMBOL 0x4
++
++/* Entry found in `.options' section. */
++
++typedef struct
++{
++ Elf32_Word hwp_flags1; /* Extra flags. */
++ Elf32_Word hwp_flags2; /* Extra flags. */
++} Elf_Options_Hw;
++
++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */
++
++#define OHWA0_R4KEOP_CHECKED 0x00000001
++#define OHWA1_R4KEOP_CLEAN 0x00000002
++
++/* MIPS relocs. */
++
++#define R_MIPS_NONE 0 /* No reloc */
++#define R_MIPS_16 1 /* Direct 16 bit */
++#define R_MIPS_32 2 /* Direct 32 bit */
++#define R_MIPS_REL32 3 /* PC relative 32 bit */
++#define R_MIPS_26 4 /* Direct 26 bit shifted */
++#define R_MIPS_HI16 5 /* High 16 bit */
++#define R_MIPS_LO16 6 /* Low 16 bit */
++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */
++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */
++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */
++#define R_MIPS_PC16 10 /* PC relative 16 bit */
++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */
++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */
++
++#define R_MIPS_SHIFT5 16
++#define R_MIPS_SHIFT6 17
++#define R_MIPS_64 18
++#define R_MIPS_GOT_DISP 19
++#define R_MIPS_GOT_PAGE 20
++#define R_MIPS_GOT_OFST 21
++#define R_MIPS_GOT_HI16 22
++#define R_MIPS_GOT_LO16 23
++#define R_MIPS_SUB 24
++#define R_MIPS_INSERT_A 25
++#define R_MIPS_INSERT_B 26
++#define R_MIPS_DELETE 27
++#define R_MIPS_HIGHER 28
++#define R_MIPS_HIGHEST 29
++#define R_MIPS_CALL_HI16 30
++#define R_MIPS_CALL_LO16 31
++#define R_MIPS_SCN_DISP 32
++#define R_MIPS_REL16 33
++#define R_MIPS_ADD_IMMEDIATE 34
++#define R_MIPS_PJUMP 35
++#define R_MIPS_RELGOT 36
++#define R_MIPS_JALR 37
++#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */
++#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */
++#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */
++#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */
++#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */
++#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */
++#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */
++#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */
++#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */
++#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */
++#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */
++#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */
++#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */
++#define R_MIPS_GLOB_DAT 51
++#define R_MIPS_COPY 126
++#define R_MIPS_JUMP_SLOT 127
++/* Keep this the last entry. */
++#define R_MIPS_NUM 128
++
++/* Legal values for p_type field of Elf32_Phdr. */
++
++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */
++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */
++#define PT_MIPS_OPTIONS 0x70000002
++
++/* Special program header types. */
++
++#define PF_MIPS_LOCAL 0x10000000
++
++/* Legal values for d_tag field of Elf32_Dyn. */
++
++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */
++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */
++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */
++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */
++#define DT_MIPS_FLAGS 0x70000005 /* Flags */
++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */
++#define DT_MIPS_MSYM 0x70000007
++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */
++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */
++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */
++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */
++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */
++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */
++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */
++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */
++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */
++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */
++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */
++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in
++ DT_MIPS_DELTA_CLASS. */
++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */
++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in
++ DT_MIPS_DELTA_INSTANCE. */
++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */
++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in
++ DT_MIPS_DELTA_RELOC. */
++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta
++ relocations refer to. */
++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in
++ DT_MIPS_DELTA_SYM. */
++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the
++ class declaration. */
++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in
++ DT_MIPS_DELTA_CLASSSYM. */
++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */
++#define DT_MIPS_PIXIE_INIT 0x70000023
++#define DT_MIPS_SYMBOL_LIB 0x70000024
++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
++#define DT_MIPS_LOCAL_GOTIDX 0x70000026
++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */
++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */
++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */
++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve
++ function stored in GOT. */
++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added
++ by rld on dlopen() calls. */
++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */
++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */
++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */
++/* The address of .got.plt in an executable using the new non-PIC ABI. */
++#define DT_MIPS_PLTGOT 0x70000032
++/* The base of the PLT in an executable using the new non-PIC ABI if that
++ PLT is writable. For a non-writable PLT, this is omitted or has a zero
++ value. */
++#define DT_MIPS_RWPLT 0x70000034
++#define DT_MIPS_NUM 0x35
++
++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */
++
++#define RHF_NONE 0 /* No flags */
++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */
++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */
++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */
++#define RHF_NO_MOVE (1 << 3)
++#define RHF_SGI_ONLY (1 << 4)
++#define RHF_GUARANTEE_INIT (1 << 5)
++#define RHF_DELTA_C_PLUS_PLUS (1 << 6)
++#define RHF_GUARANTEE_START_INIT (1 << 7)
++#define RHF_PIXIE (1 << 8)
++#define RHF_DEFAULT_DELAY_LOAD (1 << 9)
++#define RHF_REQUICKSTART (1 << 10)
++#define RHF_REQUICKSTARTED (1 << 11)
++#define RHF_CORD (1 << 12)
++#define RHF_NO_UNRES_UNDEF (1 << 13)
++#define RHF_RLD_ORDER_SAFE (1 << 14)
++
++/* Entries found in sections of type SHT_MIPS_LIBLIST. */
++
++typedef struct
++{
++ Elf32_Word l_name; /* Name (string table index) */
++ Elf32_Word l_time_stamp; /* Timestamp */
++ Elf32_Word l_checksum; /* Checksum */
++ Elf32_Word l_version; /* Interface version */
++ Elf32_Word l_flags; /* Flags */
++} Elf32_Lib;
++
++typedef struct
++{
++ Elf64_Word l_name; /* Name (string table index) */
++ Elf64_Word l_time_stamp; /* Timestamp */
++ Elf64_Word l_checksum; /* Checksum */
++ Elf64_Word l_version; /* Interface version */
++ Elf64_Word l_flags; /* Flags */
++} Elf64_Lib;
++
++
++/* Legal values for l_flags. */
++
++#define LL_NONE 0
++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */
++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */
++#define LL_REQUIRE_MINOR (1 << 2)
++#define LL_EXPORTS (1 << 3)
++#define LL_DELAY_LOAD (1 << 4)
++#define LL_DELTA (1 << 5)
++
++/* Entries found in sections of type SHT_MIPS_CONFLICT. */
++
++typedef Elf32_Addr Elf32_Conflict;
++
++
++/* HPPA specific definitions. */
++
++/* Legal values for e_flags field of Elf32_Ehdr. */
++
++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */
++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */
++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */
++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */
++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch
++ prediction. */
++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */
++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */
++
++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */
++
++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */
++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */
++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */
++
++/* Additional section indeces. */
++
++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared
++ symbols in ANSI C. */
++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */
++
++/* Legal values for sh_type field of Elf32_Shdr. */
++
++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */
++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */
++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */
++
++/* Legal values for sh_flags field of Elf32_Shdr. */
++
++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */
++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */
++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */
++
++/* Legal values for ST_TYPE subfield of st_info (symbol type). */
++
++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */
++
++#define STT_HP_OPAQUE (STT_LOOS + 0x1)
++#define STT_HP_STUB (STT_LOOS + 0x2)
++
++/* HPPA relocs. */
++
++#define R_PARISC_NONE 0 /* No reloc. */
++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */
++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */
++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */
++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */
++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */
++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */
++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */
++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */
++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */
++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */
++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */
++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */
++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */
++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */
++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */
++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */
++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */
++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */
++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */
++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */
++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */
++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */
++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */
++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */
++#define R_PARISC_FPTR64 64 /* 64 bits function address. */
++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */
++#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */
++#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */
++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */
++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */
++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */
++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */
++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */
++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */
++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */
++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */
++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */
++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */
++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */
++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */
++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */
++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */
++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */
++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */
++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */
++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */
++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */
++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */
++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */
++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */
++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */
++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */
++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */
++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */
++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */
++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */
++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */
++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */
++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */
++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */
++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */
++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */
++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */
++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */
++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */
++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */
++#define R_PARISC_LORESERVE 128
++#define R_PARISC_COPY 128 /* Copy relocation. */
++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */
++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */
++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */
++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */
++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */
++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */
++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/
++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */
++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */
++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */
++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */
++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */
++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */
++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */
++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */
++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/
++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/
++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */
++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */
++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */
++#define R_PARISC_GNU_VTENTRY 232
++#define R_PARISC_GNU_VTINHERIT 233
++#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */
++#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */
++#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */
++#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */
++#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */
++#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */
++#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */
++#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */
++#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */
++#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */
++#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */
++#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */
++#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L
++#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R
++#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L
++#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R
++#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32
++#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64
++#define R_PARISC_HIRESERVE 255
++
++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */
++
++#define PT_HP_TLS (PT_LOOS + 0x0)
++#define PT_HP_CORE_NONE (PT_LOOS + 0x1)
++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2)
++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3)
++#define PT_HP_CORE_COMM (PT_LOOS + 0x4)
++#define PT_HP_CORE_PROC (PT_LOOS + 0x5)
++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6)
++#define PT_HP_CORE_STACK (PT_LOOS + 0x7)
++#define PT_HP_CORE_SHM (PT_LOOS + 0x8)
++#define PT_HP_CORE_MMF (PT_LOOS + 0x9)
++#define PT_HP_PARALLEL (PT_LOOS + 0x10)
++#define PT_HP_FASTBIND (PT_LOOS + 0x11)
++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12)
++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13)
++#define PT_HP_STACK (PT_LOOS + 0x14)
++
++#define PT_PARISC_ARCHEXT 0x70000000
++#define PT_PARISC_UNWIND 0x70000001
++
++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */
++
++#define PF_PARISC_SBP 0x08000000
++
++#define PF_HP_PAGE_SIZE 0x00100000
++#define PF_HP_FAR_SHARED 0x00200000
++#define PF_HP_NEAR_SHARED 0x00400000
++#define PF_HP_CODE 0x01000000
++#define PF_HP_MODIFY 0x02000000
++#define PF_HP_LAZYSWAP 0x04000000
++#define PF_HP_SBP 0x08000000
++
++
++/* Alpha specific definitions. */
++
++/* Legal values for e_flags field of Elf64_Ehdr. */
++
++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */
++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */
++
++/* Legal values for sh_type field of Elf64_Shdr. */
++
++/* These two are primerily concerned with ECOFF debugging info. */
++#define SHT_ALPHA_DEBUG 0x70000001
++#define SHT_ALPHA_REGINFO 0x70000002
++
++/* Legal values for sh_flags field of Elf64_Shdr. */
++
++#define SHF_ALPHA_GPREL 0x10000000
++
++/* Legal values for st_other field of Elf64_Sym. */
++#define STO_ALPHA_NOPV 0x80 /* No PV required. */
++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */
++
++/* Alpha relocs. */
++
++#define R_ALPHA_NONE 0 /* No reloc */
++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */
++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */
++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */
++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */
++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */
++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */
++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */
++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */
++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */
++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */
++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */
++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */
++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */
++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */
++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */
++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */
++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */
++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */
++#define R_ALPHA_TLS_GD_HI 28
++#define R_ALPHA_TLSGD 29
++#define R_ALPHA_TLS_LDM 30
++#define R_ALPHA_DTPMOD64 31
++#define R_ALPHA_GOTDTPREL 32
++#define R_ALPHA_DTPREL64 33
++#define R_ALPHA_DTPRELHI 34
++#define R_ALPHA_DTPRELLO 35
++#define R_ALPHA_DTPREL16 36
++#define R_ALPHA_GOTTPREL 37
++#define R_ALPHA_TPREL64 38
++#define R_ALPHA_TPRELHI 39
++#define R_ALPHA_TPRELLO 40
++#define R_ALPHA_TPREL16 41
++/* Keep this the last entry. */
++#define R_ALPHA_NUM 46
++
++/* Magic values of the LITUSE relocation addend. */
++#define LITUSE_ALPHA_ADDR 0
++#define LITUSE_ALPHA_BASE 1
++#define LITUSE_ALPHA_BYTOFF 2
++#define LITUSE_ALPHA_JSR 3
++#define LITUSE_ALPHA_TLS_GD 4
++#define LITUSE_ALPHA_TLS_LDM 5
++
++/* Legal values for d_tag of Elf64_Dyn. */
++#define DT_ALPHA_PLTRO (DT_LOPROC + 0)
++#define DT_ALPHA_NUM 1
++
++/* PowerPC specific declarations */
++
++/* Values for Elf32/64_Ehdr.e_flags. */
++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */
++
++/* Cygnus local bits below */
++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/
++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib
++ flag */
++
++/* PowerPC relocations defined by the ABIs */
++#define R_PPC_NONE 0
++#define R_PPC_ADDR32 1 /* 32bit absolute address */
++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
++#define R_PPC_ADDR16 3 /* 16bit absolute address */
++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
++#define R_PPC_ADDR14_BRTAKEN 8
++#define R_PPC_ADDR14_BRNTAKEN 9
++#define R_PPC_REL24 10 /* PC relative 26 bit */
++#define R_PPC_REL14 11 /* PC relative 16 bit */
++#define R_PPC_REL14_BRTAKEN 12
++#define R_PPC_REL14_BRNTAKEN 13
++#define R_PPC_GOT16 14
++#define R_PPC_GOT16_LO 15
++#define R_PPC_GOT16_HI 16
++#define R_PPC_GOT16_HA 17
++#define R_PPC_PLTREL24 18
++#define R_PPC_COPY 19
++#define R_PPC_GLOB_DAT 20
++#define R_PPC_JMP_SLOT 21
++#define R_PPC_RELATIVE 22
++#define R_PPC_LOCAL24PC 23
++#define R_PPC_UADDR32 24
++#define R_PPC_UADDR16 25
++#define R_PPC_REL32 26
++#define R_PPC_PLT32 27
++#define R_PPC_PLTREL32 28
++#define R_PPC_PLT16_LO 29
++#define R_PPC_PLT16_HI 30
++#define R_PPC_PLT16_HA 31
++#define R_PPC_SDAREL16 32
++#define R_PPC_SECTOFF 33
++#define R_PPC_SECTOFF_LO 34
++#define R_PPC_SECTOFF_HI 35
++#define R_PPC_SECTOFF_HA 36
++
++/* PowerPC relocations defined for the TLS access ABI. */
++#define R_PPC_TLS 67 /* none (sym+add)@tls */
++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */
++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */
++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */
++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */
++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */
++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */
++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */
++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */
++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */
++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */
++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */
++
++/* The remaining relocs are from the Embedded ELF ABI, and are not
++ in the SVR4 ELF ABI. */
++#define R_PPC_EMB_NADDR32 101
++#define R_PPC_EMB_NADDR16 102
++#define R_PPC_EMB_NADDR16_LO 103
++#define R_PPC_EMB_NADDR16_HI 104
++#define R_PPC_EMB_NADDR16_HA 105
++#define R_PPC_EMB_SDAI16 106
++#define R_PPC_EMB_SDA2I16 107
++#define R_PPC_EMB_SDA2REL 108
++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */
++#define R_PPC_EMB_MRKREF 110
++#define R_PPC_EMB_RELSEC16 111
++#define R_PPC_EMB_RELST_LO 112
++#define R_PPC_EMB_RELST_HI 113
++#define R_PPC_EMB_RELST_HA 114
++#define R_PPC_EMB_BIT_FLD 115
++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */
++
++/* Diab tool relocations. */
++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */
++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */
++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */
++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */
++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */
++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */
++
++/* GNU extension to support local ifunc. */
++#define R_PPC_IRELATIVE 248
++
++/* GNU relocs used in PIC code sequences. */
++#define R_PPC_REL16 249 /* half16 (sym+add-.) */
++#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */
++#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */
++#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */
++
++/* This is a phony reloc to handle any old fashioned TOC16 references
++ that may still be in object files. */
++#define R_PPC_TOC16 255
++
++/* PowerPC specific values for the Dyn d_tag field. */
++#define DT_PPC_GOT (DT_LOPROC + 0)
++#define DT_PPC_NUM 1
++
++/* PowerPC64 relocations defined by the ABIs */
++#define R_PPC64_NONE R_PPC_NONE
++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */
++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */
++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */
++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */
++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */
++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */
++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */
++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN
++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN
++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */
++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */
++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN
++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN
++#define R_PPC64_GOT16 R_PPC_GOT16
++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO
++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI
++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA
++
++#define R_PPC64_COPY R_PPC_COPY
++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT
++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT
++#define R_PPC64_RELATIVE R_PPC_RELATIVE
++
++#define R_PPC64_UADDR32 R_PPC_UADDR32
++#define R_PPC64_UADDR16 R_PPC_UADDR16
++#define R_PPC64_REL32 R_PPC_REL32
++#define R_PPC64_PLT32 R_PPC_PLT32
++#define R_PPC64_PLTREL32 R_PPC_PLTREL32
++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO
++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI
++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA
++
++#define R_PPC64_SECTOFF R_PPC_SECTOFF
++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO
++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI
++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA
++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */
++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */
++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */
++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */
++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */
++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */
++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */
++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */
++#define R_PPC64_PLT64 45 /* doubleword64 L + A */
++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */
++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */
++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */
++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */
++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */
++#define R_PPC64_TOC 51 /* doubleword64 .TOC */
++#define R_PPC64_PLTGOT16 52 /* half16* M + A */
++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */
++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */
++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */
++
++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */
++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */
++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */
++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */
++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */
++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */
++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */
++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */
++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */
++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */
++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */
++
++/* PowerPC64 relocations defined for the TLS access ABI. */
++#define R_PPC64_TLS 67 /* none (sym+add)@tls */
++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */
++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */
++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */
++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */
++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */
++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */
++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */
++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */
++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */
++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */
++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */
++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */
++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */
++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */
++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */
++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */
++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */
++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */
++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */
++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */
++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */
++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */
++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */
++
++/* GNU extension to support local ifunc. */
++#define R_PPC64_JMP_IREL 247
++#define R_PPC64_IRELATIVE 248
++#define R_PPC64_REL16 249 /* half16 (sym+add-.) */
++#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */
++#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */
++#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */
++
++/* PowerPC64 specific values for the Dyn d_tag field. */
++#define DT_PPC64_GLINK (DT_LOPROC + 0)
++#define DT_PPC64_OPD (DT_LOPROC + 1)
++#define DT_PPC64_OPDSZ (DT_LOPROC + 2)
++#define DT_PPC64_NUM 3
++
++
++/* ARM specific declarations */
++
++/* Processor specific flags for the ELF header e_flags field. */
++#define EF_ARM_RELEXEC 0x01
++#define EF_ARM_HASENTRY 0x02
++#define EF_ARM_INTERWORK 0x04
++#define EF_ARM_APCS_26 0x08
++#define EF_ARM_APCS_FLOAT 0x10
++#define EF_ARM_PIC 0x20
++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */
++#define EF_ARM_NEW_ABI 0x80
++#define EF_ARM_OLD_ABI 0x100
++#define EF_ARM_SOFT_FLOAT 0x200
++#define EF_ARM_VFP_FLOAT 0x400
++#define EF_ARM_MAVERICK_FLOAT 0x800
++
++
++/* Other constants defined in the ARM ELF spec. version B-01. */
++/* NB. These conflict with values defined above. */
++#define EF_ARM_SYMSARESORTED 0x04
++#define EF_ARM_DYNSYMSUSESEGIDX 0x08
++#define EF_ARM_MAPSYMSFIRST 0x10
++#define EF_ARM_EABIMASK 0XFF000000
++
++/* Constants defined in AAELF. */
++#define EF_ARM_BE8 0x00800000
++#define EF_ARM_LE8 0x00400000
++
++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK)
++#define EF_ARM_EABI_UNKNOWN 0x00000000
++#define EF_ARM_EABI_VER1 0x01000000
++#define EF_ARM_EABI_VER2 0x02000000
++#define EF_ARM_EABI_VER3 0x03000000
++#define EF_ARM_EABI_VER4 0x04000000
++#define EF_ARM_EABI_VER5 0x05000000
++
++/* Additional symbol types for Thumb. */
++#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */
++#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */
++
++/* ARM-specific values for sh_flags */
++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */
++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined
++ in the input to a link step. */
++
++/* ARM-specific program header flags */
++#define PF_ARM_SB 0x10000000 /* Segment contains the location
++ addressed by the static base. */
++#define PF_ARM_PI 0x20000000 /* Position-independent segment. */
++#define PF_ARM_ABS 0x40000000 /* Absolute segment. */
++
++/* Processor specific values for the Phdr p_type field. */
++#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */
++
++/* Processor specific values for the Shdr sh_type field. */
++#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */
++#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */
++#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */
++
++
++/* ARM relocs. */
++
++#define R_ARM_NONE 0 /* No reloc */
++#define R_ARM_PC24 1 /* PC relative 26 bit branch */
++#define R_ARM_ABS32 2 /* Direct 32 bit */
++#define R_ARM_REL32 3 /* PC relative 32 bit */
++#define R_ARM_PC13 4
++#define R_ARM_ABS16 5 /* Direct 16 bit */
++#define R_ARM_ABS12 6 /* Direct 12 bit */
++#define R_ARM_THM_ABS5 7
++#define R_ARM_ABS8 8 /* Direct 8 bit */
++#define R_ARM_SBREL32 9
++#define R_ARM_THM_PC22 10
++#define R_ARM_THM_PC8 11
++#define R_ARM_AMP_VCALL9 12
++#define R_ARM_SWI24 13 /* Obsolete static relocation. */
++#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */
++#define R_ARM_THM_SWI8 14
++#define R_ARM_XPC25 15
++#define R_ARM_THM_XPC22 16
++#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */
++#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */
++#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */
++#define R_ARM_COPY 20 /* Copy symbol at runtime */
++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */
++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */
++#define R_ARM_RELATIVE 23 /* Adjust by program base */
++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */
++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */
++#define R_ARM_GOT32 26 /* 32 bit GOT entry */
++#define R_ARM_PLT32 27 /* 32 bit PLT address */
++#define R_ARM_ALU_PCREL_7_0 32
++#define R_ARM_ALU_PCREL_15_8 33
++#define R_ARM_ALU_PCREL_23_15 34
++#define R_ARM_LDR_SBREL_11_0 35
++#define R_ARM_ALU_SBREL_19_12 36
++#define R_ARM_ALU_SBREL_27_20 37
++#define R_ARM_TLS_GOTDESC 90
++#define R_ARM_TLS_CALL 91
++#define R_ARM_TLS_DESCSEQ 92
++#define R_ARM_THM_TLS_CALL 93
++#define R_ARM_GNU_VTENTRY 100
++#define R_ARM_GNU_VTINHERIT 101
++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */
++#define R_ARM_THM_PC9 103 /* thumb conditional branch */
++#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic
++ thread local data */
++#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic
++ thread local data */
++#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS
++ block */
++#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of
++ static TLS block offset */
++#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static
++ TLS block */
++#define R_ARM_THM_TLS_DESCSEQ 129
++#define R_ARM_IRELATIVE 160
++#define R_ARM_RXPC25 249
++#define R_ARM_RSBREL32 250
++#define R_ARM_THM_RPC22 251
++#define R_ARM_RREL32 252
++#define R_ARM_RABS22 253
++#define R_ARM_RPC24 254
++#define R_ARM_RBASE 255
++/* Keep this the last entry. */
++#define R_ARM_NUM 256
++
++/* IA-64 specific declarations. */
++
++/* Processor specific flags for the Ehdr e_flags field. */
++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */
++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */
++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */
++
++/* Processor specific values for the Phdr p_type field. */
++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */
++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */
++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12)
++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13)
++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14)
++
++/* Processor specific flags for the Phdr p_flags field. */
++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */
++
++/* Processor specific values for the Shdr sh_type field. */
++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */
++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */
++
++/* Processor specific flags for the Shdr sh_flags field. */
++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */
++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */
++
++/* Processor specific values for the Dyn d_tag field. */
++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0)
++#define DT_IA_64_NUM 1
++
++/* IA-64 relocations. */
++#define R_IA64_NONE 0x00 /* none */
++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */
++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */
++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */
++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */
++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */
++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */
++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */
++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */
++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */
++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */
++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */
++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */
++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */
++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */
++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */
++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */
++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */
++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */
++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */
++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */
++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */
++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */
++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */
++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */
++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */
++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */
++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */
++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */
++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */
++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */
++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */
++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */
++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */
++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */
++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */
++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */
++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */
++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */
++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */
++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */
++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */
++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */
++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */
++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */
++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */
++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */
++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */
++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */
++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */
++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */
++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */
++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */
++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */
++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */
++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */
++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */
++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */
++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */
++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */
++#define R_IA64_COPY 0x84 /* copy relocation */
++#define R_IA64_SUB 0x85 /* Addend and symbol difference */
++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */
++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */
++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */
++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */
++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */
++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */
++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */
++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */
++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */
++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */
++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */
++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */
++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */
++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */
++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */
++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */
++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */
++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
++
++/* SH specific declarations */
++
++/* Processor specific flags for the ELF header e_flags field. */
++#define EF_SH_MACH_MASK 0x1f
++#define EF_SH_UNKNOWN 0x0
++#define EF_SH1 0x1
++#define EF_SH2 0x2
++#define EF_SH3 0x3
++#define EF_SH_DSP 0x4
++#define EF_SH3_DSP 0x5
++#define EF_SH4AL_DSP 0x6
++#define EF_SH3E 0x8
++#define EF_SH4 0x9
++#define EF_SH2E 0xb
++#define EF_SH4A 0xc
++#define EF_SH2A 0xd
++#define EF_SH4_NOFPU 0x10
++#define EF_SH4A_NOFPU 0x11
++#define EF_SH4_NOMMU_NOFPU 0x12
++#define EF_SH2A_NOFPU 0x13
++#define EF_SH3_NOMMU 0x14
++#define EF_SH2A_SH4_NOFPU 0x15
++#define EF_SH2A_SH3_NOFPU 0x16
++#define EF_SH2A_SH4 0x17
++#define EF_SH2A_SH3E 0x18
++
++/* SH relocs. */
++#define R_SH_NONE 0
++#define R_SH_DIR32 1
++#define R_SH_REL32 2
++#define R_SH_DIR8WPN 3
++#define R_SH_IND12W 4
++#define R_SH_DIR8WPL 5
++#define R_SH_DIR8WPZ 6
++#define R_SH_DIR8BP 7
++#define R_SH_DIR8W 8
++#define R_SH_DIR8L 9
++#define R_SH_SWITCH16 25
++#define R_SH_SWITCH32 26
++#define R_SH_USES 27
++#define R_SH_COUNT 28
++#define R_SH_ALIGN 29
++#define R_SH_CODE 30
++#define R_SH_DATA 31
++#define R_SH_LABEL 32
++#define R_SH_SWITCH8 33
++#define R_SH_GNU_VTINHERIT 34
++#define R_SH_GNU_VTENTRY 35
++#define R_SH_TLS_GD_32 144
++#define R_SH_TLS_LD_32 145
++#define R_SH_TLS_LDO_32 146
++#define R_SH_TLS_IE_32 147
++#define R_SH_TLS_LE_32 148
++#define R_SH_TLS_DTPMOD32 149
++#define R_SH_TLS_DTPOFF32 150
++#define R_SH_TLS_TPOFF32 151
++#define R_SH_GOT32 160
++#define R_SH_PLT32 161
++#define R_SH_COPY 162
++#define R_SH_GLOB_DAT 163
++#define R_SH_JMP_SLOT 164
++#define R_SH_RELATIVE 165
++#define R_SH_GOTOFF 166
++#define R_SH_GOTPC 167
++/* Keep this the last entry. */
++#define R_SH_NUM 256
++
++/* S/390 specific definitions. */
++
++/* Valid values for the e_flags field. */
++
++#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */
++
++/* Additional s390 relocs */
++
++#define R_390_NONE 0 /* No reloc. */
++#define R_390_8 1 /* Direct 8 bit. */
++#define R_390_12 2 /* Direct 12 bit. */
++#define R_390_16 3 /* Direct 16 bit. */
++#define R_390_32 4 /* Direct 32 bit. */
++#define R_390_PC32 5 /* PC relative 32 bit. */
++#define R_390_GOT12 6 /* 12 bit GOT offset. */
++#define R_390_GOT32 7 /* 32 bit GOT offset. */
++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */
++#define R_390_COPY 9 /* Copy symbol at runtime. */
++#define R_390_GLOB_DAT 10 /* Create GOT entry. */
++#define R_390_JMP_SLOT 11 /* Create PLT entry. */
++#define R_390_RELATIVE 12 /* Adjust by program base. */
++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */
++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */
++#define R_390_GOT16 15 /* 16 bit GOT offset. */
++#define R_390_PC16 16 /* PC relative 16 bit. */
++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */
++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */
++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */
++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */
++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */
++#define R_390_64 22 /* Direct 64 bit. */
++#define R_390_PC64 23 /* PC relative 64 bit. */
++#define R_390_GOT64 24 /* 64 bit GOT offset. */
++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */
++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */
++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */
++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */
++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */
++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */
++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */
++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */
++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */
++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */
++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */
++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */
++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */
++#define R_390_TLS_GDCALL 38 /* Tag for function call in general
++ dynamic TLS code. */
++#define R_390_TLS_LDCALL 39 /* Tag for function call in local
++ dynamic TLS code. */
++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic
++ thread local data. */
++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic
++ thread local data. */
++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS
++ block offset. */
++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS
++ block offset. */
++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS
++ block offset. */
++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic
++ thread local data in LE code. */
++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic
++ thread local data in LE code. */
++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for
++ negated static TLS block offset. */
++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for
++ negated static TLS block offset. */
++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for
++ negated static TLS block offset. */
++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to
++ static TLS block. */
++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to
++ static TLS block. */
++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS
++ block. */
++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS
++ block. */
++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */
++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */
++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS
++ block. */
++#define R_390_20 57 /* Direct 20 bit. */
++#define R_390_GOT20 58 /* 20 bit GOT offset. */
++#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */
++#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS
++ block offset. */
++#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */
++/* Keep this the last entry. */
++#define R_390_NUM 62
++
++
++/* CRIS relocations. */
++#define R_CRIS_NONE 0
++#define R_CRIS_8 1
++#define R_CRIS_16 2
++#define R_CRIS_32 3
++#define R_CRIS_8_PCREL 4
++#define R_CRIS_16_PCREL 5
++#define R_CRIS_32_PCREL 6
++#define R_CRIS_GNU_VTINHERIT 7
++#define R_CRIS_GNU_VTENTRY 8
++#define R_CRIS_COPY 9
++#define R_CRIS_GLOB_DAT 10
++#define R_CRIS_JUMP_SLOT 11
++#define R_CRIS_RELATIVE 12
++#define R_CRIS_16_GOT 13
++#define R_CRIS_32_GOT 14
++#define R_CRIS_16_GOTPLT 15
++#define R_CRIS_32_GOTPLT 16
++#define R_CRIS_32_GOTREL 17
++#define R_CRIS_32_PLT_GOTREL 18
++#define R_CRIS_32_PLT_PCREL 19
++
++#define R_CRIS_NUM 20
++
++
++/* AMD x86-64 relocations. */
++#define R_X86_64_NONE 0 /* No reloc */
++#define R_X86_64_64 1 /* Direct 64 bit */
++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */
++#define R_X86_64_PLT32 4 /* 32 bit PLT address */
++#define R_X86_64_COPY 5 /* Copy symbol at runtime */
++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */
++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */
++#define R_X86_64_RELATIVE 8 /* Adjust by program base */
++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative
++ offset to GOT */
++#define R_X86_64_32 10 /* Direct 32 bit zero extended */
++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */
++#define R_X86_64_16 12 /* Direct 16 bit zero extended */
++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */
++#define R_X86_64_8 14 /* Direct 8 bit sign extended */
++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */
++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */
++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */
++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */
++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset
++ to two GOT entries for GD symbol */
++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset
++ to two GOT entries for LD symbol */
++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */
++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset
++ to GOT entry for IE symbol */
++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */
++#define R_X86_64_PC64 24 /* PC relative 64 bit */
++#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */
++#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative
++ offset to GOT */
++#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */
++#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset
++ to GOT entry */
++#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */
++#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */
++#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset
++ to PLT entry */
++#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */
++#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */
++#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */
++#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS
++ descriptor. */
++#define R_X86_64_TLSDESC 36 /* TLS descriptor. */
++#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */
++#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */
++
++#define R_X86_64_NUM 39
++
++
++/* AM33 relocations. */
++#define R_MN10300_NONE 0 /* No reloc. */
++#define R_MN10300_32 1 /* Direct 32 bit. */
++#define R_MN10300_16 2 /* Direct 16 bit. */
++#define R_MN10300_8 3 /* Direct 8 bit. */
++#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */
++#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */
++#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */
++#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */
++#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */
++#define R_MN10300_24 9 /* Direct 24 bit. */
++#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */
++#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */
++#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */
++#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */
++#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */
++#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */
++#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */
++#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */
++#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */
++#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */
++#define R_MN10300_COPY 20 /* Copy symbol at runtime. */
++#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */
++#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */
++#define R_MN10300_RELATIVE 23 /* Adjust by program base. */
++
++#define R_MN10300_NUM 24
++
++
++/* M32R relocs. */
++#define R_M32R_NONE 0 /* No reloc. */
++#define R_M32R_16 1 /* Direct 16 bit. */
++#define R_M32R_32 2 /* Direct 32 bit. */
++#define R_M32R_24 3 /* Direct 24 bit. */
++#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */
++#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */
++#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */
++#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */
++#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */
++#define R_M32R_LO16 9 /* Low 16 bit. */
++#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */
++#define R_M32R_GNU_VTINHERIT 11
++#define R_M32R_GNU_VTENTRY 12
++/* M32R relocs use SHT_RELA. */
++#define R_M32R_16_RELA 33 /* Direct 16 bit. */
++#define R_M32R_32_RELA 34 /* Direct 32 bit. */
++#define R_M32R_24_RELA 35 /* Direct 24 bit. */
++#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */
++#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */
++#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */
++#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */
++#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */
++#define R_M32R_LO16_RELA 41 /* Low 16 bit */
++#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */
++#define R_M32R_RELA_GNU_VTINHERIT 43
++#define R_M32R_RELA_GNU_VTENTRY 44
++#define R_M32R_REL32 45 /* PC relative 32 bit. */
++
++#define R_M32R_GOT24 48 /* 24 bit GOT entry */
++#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */
++#define R_M32R_COPY 50 /* Copy symbol at runtime */
++#define R_M32R_GLOB_DAT 51 /* Create GOT entry */
++#define R_M32R_JMP_SLOT 52 /* Create PLT entry */
++#define R_M32R_RELATIVE 53 /* Adjust by program base */
++#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */
++#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */
++#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned
++ low */
++#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed
++ low */
++#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */
++#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to
++ GOT with unsigned low */
++#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to
++ GOT with signed low */
++#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to
++ GOT */
++#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT
++ with unsigned low */
++#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT
++ with signed low */
++#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */
++#define R_M32R_NUM 256 /* Keep this the last entry. */
++
++
++/* TILEPro relocations. */
++#define R_TILEPRO_NONE 0 /* No reloc */
++#define R_TILEPRO_32 1 /* Direct 32 bit */
++#define R_TILEPRO_16 2 /* Direct 16 bit */
++#define R_TILEPRO_8 3 /* Direct 8 bit */
++#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */
++#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */
++#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */
++#define R_TILEPRO_LO16 7 /* Low 16 bit */
++#define R_TILEPRO_HI16 8 /* High 16 bit */
++#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */
++#define R_TILEPRO_COPY 10 /* Copy relocation */
++#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */
++#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */
++#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */
++#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */
++#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */
++#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */
++#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */
++#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */
++#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */
++#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */
++#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */
++#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */
++#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */
++#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */
++#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */
++#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */
++#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */
++#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */
++#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */
++#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */
++#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */
++#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */
++#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */
++#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */
++#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */
++#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */
++#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */
++#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */
++#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */
++#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */
++#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */
++#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */
++#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */
++#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */
++#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */
++#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */
++#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */
++#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */
++#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */
++/* Relocs 56-59 are currently not defined. */
++#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */
++#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */
++#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */
++#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */
++#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */
++#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */
++#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */
++#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */
++#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */
++#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */
++#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */
++#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */
++#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */
++#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */
++
++#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */
++#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */
++
++#define R_TILEPRO_NUM 130
++
++
++/* TILE-Gx relocations. */
++#define R_TILEGX_NONE 0 /* No reloc */
++#define R_TILEGX_64 1 /* Direct 64 bit */
++#define R_TILEGX_32 2 /* Direct 32 bit */
++#define R_TILEGX_16 3 /* Direct 16 bit */
++#define R_TILEGX_8 4 /* Direct 8 bit */
++#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */
++#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */
++#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */
++#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */
++#define R_TILEGX_HW0 9 /* hword 0 16-bit */
++#define R_TILEGX_HW1 10 /* hword 1 16-bit */
++#define R_TILEGX_HW2 11 /* hword 2 16-bit */
++#define R_TILEGX_HW3 12 /* hword 3 16-bit */
++#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */
++#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */
++#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */
++#define R_TILEGX_COPY 16 /* Copy relocation */
++#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */
++#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */
++#define R_TILEGX_RELATIVE 19 /* Adjust by program base */
++#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */
++#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */
++#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */
++#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */
++#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */
++#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */
++#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */
++#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */
++#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */
++#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */
++#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */
++#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */
++#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */
++#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */
++#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */
++#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */
++#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */
++#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */
++#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */
++#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */
++#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */
++#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */
++#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */
++#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */
++#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */
++#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */
++#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */
++#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */
++#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */
++#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */
++#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */
++#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */
++#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */
++#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */
++#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */
++#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */
++#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */
++#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */
++#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */
++#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */
++#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */
++#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */
++#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */
++#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */
++#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */
++#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */
++/* Relocs 66-71 are currently not defined. */
++#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */
++#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */
++#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */
++#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */
++/* Relocs 76-77 are currently not defined. */
++#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */
++#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */
++#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */
++#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */
++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */
++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */
++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */
++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */
++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */
++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */
++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */
++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */
++/* Relocs 90-91 are currently not defined. */
++#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */
++#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */
++/* Relocs 94-99 are currently not defined. */
++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */
++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */
++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */
++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */
++/* Relocs 104-105 are currently not defined. */
++#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */
++#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */
++#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */
++#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */
++#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */
++#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */
++#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */
++#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */
++#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */
++#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */
++#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */
++#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */
++#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */
++#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */
++#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */
++#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */
++
++#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */
++#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */
++
++#define R_TILEGX_NUM 130
++
++#endif /* elf.h */
+--- a/scripts/mod/mk_elfconfig.c
++++ b/scripts/mod/mk_elfconfig.c
+@@ -2,7 +2,11 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#ifndef __APPLE__
+ #include <elf.h>
++#else
++#include "elf.h"
++#endif
+
+ int
+ main(int argc, char **argv)
+--- a/scripts/mod/modpost.h
++++ b/scripts/mod/modpost.h
+@@ -8,7 +8,11 @@
+ #include <sys/mman.h>
+ #include <fcntl.h>
+ #include <unistd.h>
++#if !(defined(__APPLE__) || defined(__CYGWIN__))
+ #include <elf.h>
++#else
++#include "elf.h"
++#endif
+
+ #include "elfconfig.h"
+
diff --git a/target/linux/generic/hack-6.1/211-darwin-uuid-typedef-clash.patch b/target/linux/generic/hack-6.1/211-darwin-uuid-typedef-clash.patch
new file mode 100644
index 0000000000..50a6227148
--- /dev/null
+++ b/target/linux/generic/hack-6.1/211-darwin-uuid-typedef-clash.patch
@@ -0,0 +1,22 @@
+From e44fc2af1ddc452b6659d08c16973d65c73b7d0a Mon Sep 17 00:00:00 2001
+From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+Date: Wed, 5 Feb 2020 18:36:43 +0000
+Subject: [PATCH] file2alias: build on macos
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ scripts/mod/file2alias.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/scripts/mod/file2alias.c
++++ b/scripts/mod/file2alias.c
+@@ -38,6 +38,9 @@ typedef struct {
+ __u8 b[16];
+ } guid_t;
+
++#ifdef __APPLE__
++#define uuid_t compat_uuid_t
++#endif
+ /* backwards compatibility, don't use in new code */
+ typedef struct {
+ __u8 b[16];
diff --git a/target/linux/generic/hack-6.1/212-tools_portability.patch b/target/linux/generic/hack-6.1/212-tools_portability.patch
new file mode 100644
index 0000000000..b488155f94
--- /dev/null
+++ b/target/linux/generic/hack-6.1/212-tools_portability.patch
@@ -0,0 +1,110 @@
+From 48232d3d931c95953ce2ddfe7da7bb164aef6a73 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:03:16 +0200
+Subject: fix portability of some includes files in tools/ used on the host
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ tools/include/tools/be_byteshift.h | 4 ++++
+ tools/include/tools/le_byteshift.h | 4 ++++
+ tools/include/tools/linux_types.h | 22 ++++++++++++++++++++++
+ 3 files changed, 30 insertions(+)
+ create mode 100644 tools/include/tools/linux_types.h
+
+--- a/tools/include/tools/be_byteshift.h
++++ b/tools/include/tools/be_byteshift.h
+@@ -2,6 +2,10 @@
+ #ifndef _TOOLS_BE_BYTESHIFT_H
+ #define _TOOLS_BE_BYTESHIFT_H
+
++#ifndef __linux__
++#include "linux_types.h"
++#endif
++
+ #include <stdint.h>
+
+ static inline uint16_t __get_unaligned_be16(const uint8_t *p)
+--- a/tools/include/tools/le_byteshift.h
++++ b/tools/include/tools/le_byteshift.h
+@@ -2,6 +2,10 @@
+ #ifndef _TOOLS_LE_BYTESHIFT_H
+ #define _TOOLS_LE_BYTESHIFT_H
+
++#ifndef __linux__
++#include "linux_types.h"
++#endif
++
+ #include <stdint.h>
+
+ static inline uint16_t __get_unaligned_le16(const uint8_t *p)
+--- /dev/null
++++ b/tools/include/tools/linux_types.h
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_TYPES_H
++#define __LINUX_TYPES_H
++
++#include <stdint.h>
++
++typedef int8_t __s8;
++typedef uint8_t __u8;
++typedef uint8_t __be8;
++typedef uint8_t __le8;
++
++typedef int16_t __s16;
++typedef uint16_t __u16;
++typedef uint16_t __be16;
++typedef uint16_t __le16;
++
++typedef int32_t __s32;
++typedef uint32_t __u32;
++typedef uint32_t __be32;
++typedef uint32_t __le32;
++
++typedef int64_t __s64;
++typedef uint64_t __u64;
++typedef uint64_t __be64;
++typedef uint64_t __le64;
++
++#endif
+--- a/tools/include/linux/types.h
++++ b/tools/include/linux/types.h
+@@ -10,8 +10,12 @@
+ #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
+ #endif
+
++#ifndef __linux__
++#include <tools/linux_types.h>
++#else
+ #include <asm/types.h>
+ #include <asm/posix_types.h>
++#endif
+
+ struct page;
+ struct kmem_cache;
+--- a/tools/perf/pmu-events/jevents.c
++++ b/tools/perf/pmu-events/jevents.c
+@@ -1,4 +1,6 @@
++#ifdef __linux__
+ #define _XOPEN_SOURCE 500 /* needed for nftw() */
++#endif
+ #define _GNU_SOURCE /* needed for asprintf() */
+
+ /* Parse event JSON files */
+@@ -35,6 +37,7 @@
+ #include <stdlib.h>
+ #include <errno.h>
+ #include <string.h>
++#include <strings.h>
+ #include <ctype.h>
+ #include <unistd.h>
+ #include <stdarg.h>
+--- a/tools/perf/pmu-events/json.c
++++ b/tools/perf/pmu-events/json.c
+@@ -38,7 +38,6 @@
+ #include <unistd.h>
+ #include "jsmn.h"
+ #include "json.h"
+-#include <linux/kernel.h>
+
+
+ static char *mapfile(const char *fn, size_t *size)
diff --git a/target/linux/generic/hack-6.1/214-spidev_h_portability.patch b/target/linux/generic/hack-6.1/214-spidev_h_portability.patch
new file mode 100644
index 0000000000..db754a2903
--- /dev/null
+++ b/target/linux/generic/hack-6.1/214-spidev_h_portability.patch
@@ -0,0 +1,24 @@
+From be9be95ff10e16a5b4ad36f903978d0cc5747024 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:04:08 +0200
+Subject: kernel: fix linux/spi/spidev.h portability issues with musl
+
+Felix will try to get this define included into musl
+
+lede-commit: 795e7cf60de19e7a076a46874fab7bb88b43bbff
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/uapi/linux/spi/spidev.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/uapi/linux/spi/spidev.h
++++ b/include/uapi/linux/spi/spidev.h
+@@ -93,7 +93,7 @@ struct spi_ioc_transfer {
+
+ /* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */
+ #define SPI_MSGSIZE(N) \
+- ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \
++ ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << 13)) \
+ ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0)
+ #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
+
diff --git a/target/linux/generic/hack-6.1/220-arm-gc_sections.patch b/target/linux/generic/hack-6.1/220-arm-gc_sections.patch
new file mode 100644
index 0000000000..0aa3eb840c
--- /dev/null
+++ b/target/linux/generic/hack-6.1/220-arm-gc_sections.patch
@@ -0,0 +1,123 @@
+From e3d8676f5722b7622685581e06e8f53e6138e3ab Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 15 Jul 2017 23:42:36 +0200
+Subject: use -ffunction-sections, -fdata-sections and --gc-sections
+
+In combination with kernel symbol export stripping this significantly reduces
+the kernel image size. Used on both ARM and MIPS architectures.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Jonas Gorski <jogo@openwrt.org>
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -117,6 +117,7 @@ config ARM
+ select HAVE_UID16
+ select HAVE_VIRT_CPU_ACCOUNTING_GEN
+ select IRQ_FORCED_THREADING
++ select HAVE_LD_DEAD_CODE_DATA_ELIMINATION
+ select MODULES_USE_ELF_REL
+ select NEED_DMA_MAP_STATE
+ select OF_EARLY_FLATTREE if OF
+--- a/arch/arm/boot/compressed/Makefile
++++ b/arch/arm/boot/compressed/Makefile
+@@ -92,6 +92,7 @@ endif
+ ifeq ($(CONFIG_USE_OF),y)
+ OBJS += $(libfdt_objs) fdt_check_mem_start.o
+ endif
++KBUILD_CFLAGS_KERNEL := $(patsubst -f%-sections,,$(KBUILD_CFLAGS_KERNEL))
+
+ # -fstack-protector-strong triggers protection checks in this code,
+ # but it is being used too early to link to meaningful stack_chk logic.
+--- a/arch/arm/kernel/vmlinux.lds.S
++++ b/arch/arm/kernel/vmlinux.lds.S
+@@ -75,7 +75,7 @@ SECTIONS
+ . = ALIGN(4);
+ __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {
+ __start___ex_table = .;
+- ARM_MMU_KEEP(*(__ex_table))
++ KEEP(*(__ex_table))
+ __stop___ex_table = .;
+ }
+
+@@ -100,24 +100,24 @@ SECTIONS
+ }
+ .init.arch.info : {
+ __arch_info_begin = .;
+- *(.arch.info.init)
++ KEEP(*(.arch.info.init))
+ __arch_info_end = .;
+ }
+ .init.tagtable : {
+ __tagtable_begin = .;
+- *(.taglist.init)
++ KEEP(*(.taglist.init))
+ __tagtable_end = .;
+ }
+ #ifdef CONFIG_SMP_ON_UP
+ .init.smpalt : {
+ __smpalt_begin = .;
+- *(.alt.smp.init)
++ KEEP(*(.alt.smp.init))
+ __smpalt_end = .;
+ }
+ #endif
+ .init.pv_table : {
+ __pv_table_begin = .;
+- *(.pv_table)
++ KEEP(*(.pv_table))
+ __pv_table_end = .;
+ }
+
+--- a/arch/arm/include/asm/vmlinux.lds.h
++++ b/arch/arm/include/asm/vmlinux.lds.h
+@@ -42,13 +42,13 @@
+ #define PROC_INFO \
+ . = ALIGN(4); \
+ __proc_info_begin = .; \
+- *(.proc.info.init) \
++ KEEP(*(.proc.info.init)) \
+ __proc_info_end = .;
+
+ #define IDMAP_TEXT \
+ ALIGN_FUNCTION(); \
+ __idmap_text_start = .; \
+- *(.idmap.text) \
++ KEEP(*(.idmap.text)) \
+ __idmap_text_end = .; \
+
+ #define ARM_DISCARD \
+@@ -109,12 +109,12 @@
+ . = ALIGN(8); \
+ .ARM.unwind_idx : { \
+ __start_unwind_idx = .; \
+- *(.ARM.exidx*) \
++ KEEP(*(.ARM.exidx*)) \
+ __stop_unwind_idx = .; \
+ } \
+ .ARM.unwind_tab : { \
+ __start_unwind_tab = .; \
+- *(.ARM.extab*) \
++ KEEP(*(.ARM.extab*)) \
+ __stop_unwind_tab = .; \
+ }
+
+@@ -126,7 +126,7 @@
+ __vectors_lma = .; \
+ OVERLAY 0xffff0000 : NOCROSSREFS AT(__vectors_lma) { \
+ .vectors { \
+- *(.vectors) \
++ KEEP(*(.vectors)) \
+ } \
+ .vectors.bhb.loop8 { \
+ *(.vectors.bhb.loop8) \
+@@ -144,7 +144,7 @@
+ \
+ __stubs_lma = .; \
+ .stubs ADDR(.vectors) + 0x1000 : AT(__stubs_lma) { \
+- *(.stubs) \
++ KEEP(*(.stubs)) \
+ } \
+ ARM_LMA(__stubs, .stubs); \
+ . = __stubs_lma + SIZEOF(.stubs); \
diff --git a/target/linux/generic/hack-6.1/221-module_exports.patch b/target/linux/generic/hack-6.1/221-module_exports.patch
new file mode 100644
index 0000000000..87f541b46f
--- /dev/null
+++ b/target/linux/generic/hack-6.1/221-module_exports.patch
@@ -0,0 +1,126 @@
+From b14784e7883390c20ed3ff904892255404a5914b Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:05:53 +0200
+Subject: add an optional config option for stripping all unnecessary symbol exports from the kernel image
+
+lede-commit: bb5a40c64b7c4f4848509fa0a6625055fc9e66cc
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/asm-generic/vmlinux.lds.h | 18 +++++++++++++++---
+ include/linux/export.h | 9 ++++++++-
+ scripts/Makefile.build | 2 +-
+ 3 files changed, 24 insertions(+), 5 deletions(-)
+
+--- a/include/asm-generic/vmlinux.lds.h
++++ b/include/asm-generic/vmlinux.lds.h
+@@ -81,6 +81,16 @@
+ #define RO_EXCEPTION_TABLE
+ #endif
+
++#ifndef SYMTAB_KEEP
++#define SYMTAB_KEEP KEEP(*(SORT(___ksymtab+*)))
++#define SYMTAB_KEEP_GPL KEEP(*(SORT(___ksymtab_gpl+*)))
++#endif
++
++#ifndef SYMTAB_DISCARD
++#define SYMTAB_DISCARD
++#define SYMTAB_DISCARD_GPL
++#endif
++
+ /* Align . to a 8 byte boundary equals to maximum function alignment. */
+ #define ALIGN_FUNCTION() . = ALIGN(8)
+
+@@ -485,14 +495,14 @@
+ /* Kernel symbol table: Normal symbols */ \
+ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \
+ __start___ksymtab = .; \
+- KEEP(*(SORT(___ksymtab+*))) \
++ SYMTAB_KEEP \
+ __stop___ksymtab = .; \
+ } \
+ \
+ /* Kernel symbol table: GPL-only symbols */ \
+ __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \
+ __start___ksymtab_gpl = .; \
+- KEEP(*(SORT(___ksymtab_gpl+*))) \
++ SYMTAB_KEEP_GPL \
+ __stop___ksymtab_gpl = .; \
+ } \
+ \
+@@ -512,7 +522,7 @@
+ \
+ /* Kernel symbol table: strings */ \
+ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \
+- *(__ksymtab_strings) \
++ *(__ksymtab_strings+*) \
+ } \
+ \
+ /* __*init sections */ \
+@@ -1022,6 +1032,8 @@
+
+ #define COMMON_DISCARDS \
+ SANITIZER_DISCARDS \
++ SYMTAB_DISCARD \
++ SYMTAB_DISCARD_GPL \
+ *(.discard) \
+ *(.discard.*) \
+ *(.modinfo) \
+--- a/include/linux/export.h
++++ b/include/linux/export.h
+@@ -84,6 +84,12 @@ struct kernel_symbol {
+
+ #else
+
++#ifdef MODULE
++#define __EXPORT_SUFFIX(sym)
++#else
++#define __EXPORT_SUFFIX(sym) "+" #sym
++#endif
++
+ /*
+ * For every exported symbol, do the following:
+ *
+@@ -101,7 +107,7 @@ struct kernel_symbol {
+ extern const char __kstrtab_##sym[]; \
+ extern const char __kstrtabns_##sym[]; \
+ __CRC_SYMBOL(sym, sec); \
+- asm(" .section \"__ksymtab_strings\",\"aMS\",%progbits,1 \n" \
++ asm(" .section \"__ksymtab_strings" __EXPORT_SUFFIX(sym) "\",\"aMS\",%progbits,1 \n" \
+ "__kstrtab_" #sym ": \n" \
+ " .asciz \"" #sym "\" \n" \
+ "__kstrtabns_" #sym ": \n" \
+--- a/include/asm-generic/export.h
++++ b/include/asm-generic/export.h
+@@ -26,6 +26,12 @@
+ #endif
+ .endm
+
++#ifdef MODULE
++#define __EXPORT_SUFFIX(name)
++#else
++#define __EXPORT_SUFFIX(name) + #name
++#endif
++
+ /*
+ * note on .section use: we specify progbits since usage of the "M" (SHF_MERGE)
+ * section flag requires it. Use '%progbits' instead of '@progbits' since the
+@@ -39,7 +45,7 @@
+ __ksymtab_\name:
+ __put \val, __kstrtab_\name
+ .previous
+- .section __ksymtab_strings,"aMS",%progbits,1
++ .section __ksymtab_strings __EXPORT_SUFFIX(name),"aMS",%progbits,1
+ __kstrtab_\name:
+ .asciz "\name"
+ .previous
+--- a/scripts/Makefile.build
++++ b/scripts/Makefile.build
+@@ -397,7 +397,7 @@ targets += $(real-dtb-y) $(lib-y) $(alwa
+ # Linker scripts preprocessor (.lds.S -> .lds)
+ # ---------------------------------------------------------------------------
+ quiet_cmd_cpp_lds_S = LDS $@
+- cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -U$(ARCH) \
++ cmd_cpp_lds_S = $(CPP) $(EXTRA_LDSFLAGS) $(cpp_flags) -P -U$(ARCH) \
+ -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $<
+
+ $(obj)/%.lds: $(src)/%.lds.S FORCE
diff --git a/target/linux/generic/hack-6.1/230-openwrt_lzma_options.patch b/target/linux/generic/hack-6.1/230-openwrt_lzma_options.patch
new file mode 100644
index 0000000000..f9361b060b
--- /dev/null
+++ b/target/linux/generic/hack-6.1/230-openwrt_lzma_options.patch
@@ -0,0 +1,34 @@
+From b3d00b452467f621317953d9e4c6f9ae8dcfd271 Mon Sep 17 00:00:00 2001
+From: Imre Kaloz <kaloz@openwrt.org>
+Date: Fri, 7 Jul 2017 17:06:55 +0200
+Subject: use the openwrt lzma options for now
+
+lede-commit: 548de949f392049420a6a1feeef118b30ab8ea8c
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+---
+ lib/decompress.c | 1 +
+ scripts/Makefile.lib | 2 +-
+ usr/gen_initramfs_list.sh | 10 +++++-----
+ 3 files changed, 7 insertions(+), 6 deletions(-)
+
+--- a/lib/decompress.c
++++ b/lib/decompress.c
+@@ -53,6 +53,7 @@ static const struct compress_format comp
+ { {0x1f, 0x9e}, "gzip", gunzip },
+ { {0x42, 0x5a}, "bzip2", bunzip2 },
+ { {0x5d, 0x00}, "lzma", unlzma },
++ { {0x6d, 0x00}, "lzma-openwrt", unlzma },
+ { {0xfd, 0x37}, "xz", unxz },
+ { {0x89, 0x4c}, "lzo", unlzo },
+ { {0x02, 0x21}, "lz4", unlz4 },
+--- a/scripts/Makefile.lib
++++ b/scripts/Makefile.lib
+@@ -402,7 +402,7 @@ quiet_cmd_bzip2 = BZIP2 $@
+ # ---------------------------------------------------------------------------
+
+ quiet_cmd_lzma = LZMA $@
+- cmd_lzma = { cat $(real-prereqs) | $(LZMA) -9; $(size_append); } > $@
++ cmd_lzma = { cat $(real-prereqs) | $(LZMA) e -d20 -lc1 -lp2 -pb2 -eos -si -so; $(size_append); } > $@
+
+ quiet_cmd_lzo = LZO $@
+ cmd_lzo = { cat $(real-prereqs) | $(KLZOP) -9; $(size_append); } > $@
diff --git a/target/linux/generic/hack-6.1/250-netfilter_depends.patch b/target/linux/generic/hack-6.1/250-netfilter_depends.patch
new file mode 100644
index 0000000000..d9a2b81d74
--- /dev/null
+++ b/target/linux/generic/hack-6.1/250-netfilter_depends.patch
@@ -0,0 +1,27 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: hack: net: remove bogus netfilter dependencies
+
+lede-commit: 589d2a377dee27d206fc3725325309cf649e4df6
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/netfilter/Kconfig | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/net/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -242,7 +242,6 @@ config NF_CONNTRACK_FTP
+
+ config NF_CONNTRACK_H323
+ tristate "H.323 protocol support"
+- depends on IPV6 || IPV6=n
+ depends on NETFILTER_ADVANCED
+ help
+ H.323 is a VoIP signalling protocol from ITU-T. As one of the most
+@@ -1105,7 +1104,6 @@ config NETFILTER_XT_TARGET_SECMARK
+
+ config NETFILTER_XT_TARGET_TCPMSS
+ tristate '"TCPMSS" target support'
+- depends on IPV6 || IPV6=n
+ default m if NETFILTER_ADVANCED=n
+ help
+ This option adds a `TCPMSS' target, which allows you to alter the
diff --git a/target/linux/generic/hack-6.1/251-kconfig.patch b/target/linux/generic/hack-6.1/251-kconfig.patch
new file mode 100644
index 0000000000..0e24c3e804
--- /dev/null
+++ b/target/linux/generic/hack-6.1/251-kconfig.patch
@@ -0,0 +1,210 @@
+From da3c50704f14132f4adf80d48e9a4cd5d46e54c9 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 7 Jul 2017 17:09:21 +0200
+Subject: kconfig: owrt specifc dependencies
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ crypto/Kconfig | 10 +++++-----
+ drivers/bcma/Kconfig | 1 +
+ drivers/ssb/Kconfig | 3 ++-
+ lib/Kconfig | 8 ++++----
+ net/netfilter/Kconfig | 2 +-
+ net/wireless/Kconfig | 17 ++++++++++-------
+ sound/core/Kconfig | 4 ++--
+ 7 files changed, 25 insertions(+), 20 deletions(-)
+
+--- a/crypto/Kconfig
++++ b/crypto/Kconfig
+@@ -34,7 +34,7 @@ config CRYPTO_FIPS
+ this is.
+
+ config CRYPTO_ALGAPI
+- tristate
++ tristate "ALGAPI"
+ select CRYPTO_ALGAPI2
+ help
+ This option provides the API for cryptographic algorithms.
+@@ -43,7 +43,7 @@ config CRYPTO_ALGAPI2
+ tristate
+
+ config CRYPTO_AEAD
+- tristate
++ tristate "AEAD"
+ select CRYPTO_AEAD2
+ select CRYPTO_ALGAPI
+
+@@ -54,7 +54,7 @@ config CRYPTO_AEAD2
+ select CRYPTO_RNG2
+
+ config CRYPTO_SKCIPHER
+- tristate
++ tristate "SKCIPHER"
+ select CRYPTO_SKCIPHER2
+ select CRYPTO_ALGAPI
+
+@@ -64,7 +64,7 @@ config CRYPTO_SKCIPHER2
+ select CRYPTO_RNG2
+
+ config CRYPTO_HASH
+- tristate
++ tristate "HASH"
+ select CRYPTO_HASH2
+ select CRYPTO_ALGAPI
+
+@@ -73,7 +73,7 @@ config CRYPTO_HASH2
+ select CRYPTO_ALGAPI2
+
+ config CRYPTO_RNG
+- tristate
++ tristate "RNG"
+ select CRYPTO_RNG2
+ select CRYPTO_ALGAPI
+
+--- a/drivers/bcma/Kconfig
++++ b/drivers/bcma/Kconfig
+@@ -16,6 +16,7 @@ if BCMA
+ # Support for Block-I/O. SELECT this from the driver that needs it.
+ config BCMA_BLOCKIO
+ bool
++ default y
+
+ config BCMA_HOST_PCI_POSSIBLE
+ bool
+--- a/drivers/ssb/Kconfig
++++ b/drivers/ssb/Kconfig
+@@ -29,6 +29,7 @@ config SSB_SPROM
+ config SSB_BLOCKIO
+ bool
+ depends on SSB
++ default y
+
+ config SSB_PCIHOST_POSSIBLE
+ bool
+@@ -49,7 +50,7 @@ config SSB_PCIHOST
+ config SSB_B43_PCI_BRIDGE
+ bool
+ depends on SSB_PCIHOST
+- default n
++ default y
+
+ config SSB_PCMCIAHOST_POSSIBLE
+ bool
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -443,16 +443,16 @@ config BCH_CONST_T
+ # Textsearch support is select'ed if needed
+ #
+ config TEXTSEARCH
+- bool
++ bool "Textsearch support"
+
+ config TEXTSEARCH_KMP
+- tristate
++ tristate "Textsearch KMP"
+
+ config TEXTSEARCH_BM
+- tristate
++ tristate "Textsearch BM"
+
+ config TEXTSEARCH_FSM
+- tristate
++ tristate "Textsearch FSM"
+
+ config BTREE
+ bool
+--- a/net/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -11,7 +11,7 @@ config NETFILTER_INGRESS
+ infrastructure.
+
+ config NETFILTER_NETLINK
+- tristate
++ tristate "Netfilter NFNETLINK interface"
+
+ config NETFILTER_FAMILY_BRIDGE
+ bool
+--- a/net/wireless/Kconfig
++++ b/net/wireless/Kconfig
+@@ -1,6 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config WIRELESS_EXT
+- bool
++ bool "Wireless extensions"
+
+ config WEXT_CORE
+ def_bool y
+@@ -12,10 +12,10 @@ config WEXT_PROC
+ depends on WEXT_CORE
+
+ config WEXT_SPY
+- bool
++ bool "WEXT_SPY"
+
+ config WEXT_PRIV
+- bool
++ bool "WEXT_PRIV"
+
+ config CFG80211
+ tristate "cfg80211 - wireless configuration API"
+@@ -204,7 +204,7 @@ config CFG80211_WEXT_EXPORT
+ endif # CFG80211
+
+ config LIB80211
+- tristate
++ tristate "LIB80211"
+ default n
+ help
+ This options enables a library of common routines used
+@@ -213,17 +213,17 @@ config LIB80211
+ Drivers should select this themselves if needed.
+
+ config LIB80211_CRYPT_WEP
+- tristate
++ tristate "LIB80211_CRYPT_WEP"
+ select CRYPTO_LIB_ARC4
+
+ config LIB80211_CRYPT_CCMP
+- tristate
++ tristate "LIB80211_CRYPT_CCMP"
+ select CRYPTO
+ select CRYPTO_AES
+ select CRYPTO_CCM
+
+ config LIB80211_CRYPT_TKIP
+- tristate
++ tristate "LIB80211_CRYPT_TKIP"
+ select CRYPTO_LIB_ARC4
+
+ config LIB80211_DEBUG
+--- a/sound/core/Kconfig
++++ b/sound/core/Kconfig
+@@ -17,7 +17,7 @@ config SND_DMAENGINE_PCM
+ tristate
+
+ config SND_HWDEP
+- tristate
++ tristate "Sound hardware support"
+
+ config SND_SEQ_DEVICE
+ tristate
+@@ -27,7 +27,7 @@ config SND_RAWMIDI
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
+
+ config SND_COMPRESS_OFFLOAD
+- tristate
++ tristate "Compression offloading support"
+
+ config SND_JACK
+ bool
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -432,7 +432,7 @@ config NET_DEVLINK
+ default n
+
+ config PAGE_POOL
+- bool
++ bool "Page pool support"
+
+ config PAGE_POOL_STATS
+ default n
diff --git a/target/linux/generic/hack-6.1/253-ksmbd-config.patch b/target/linux/generic/hack-6.1/253-ksmbd-config.patch
new file mode 100644
index 0000000000..b8cb94f62b
--- /dev/null
+++ b/target/linux/generic/hack-6.1/253-ksmbd-config.patch
@@ -0,0 +1,32 @@
+From dcd966fa7ca63f38cf7147e1184d13d66e2ca340 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:33:30 +0200
+Subject: [PATCH] Kconfig: add tristate for OID and ASNI string
+
+---
+ init/Kconfig | 2 +-
+ lib/Kconfig | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -2388,7 +2388,7 @@ config PADATA
+ bool
+
+ config ASN1
+- tristate
++ tristate "ASN1"
+ help
+ Build a simple ASN.1 grammar compiler that produces a bytecode output
+ that can be interpreted by the ASN.1 stream decoder and used to
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -614,7 +614,7 @@ config LIBFDT
+ bool
+
+ config OID_REGISTRY
+- tristate
++ tristate "OID"
+ help
+ Enable fast lookup object identifier registry.
+
diff --git a/target/linux/generic/hack-6.1/259-regmap_dynamic.patch b/target/linux/generic/hack-6.1/259-regmap_dynamic.patch
new file mode 100644
index 0000000000..76a5ace6f3
--- /dev/null
+++ b/target/linux/generic/hack-6.1/259-regmap_dynamic.patch
@@ -0,0 +1,144 @@
+From 811d9e2268a62b830cfe93cd8bc929afcb8b198b Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 15 Jul 2017 21:12:38 +0200
+Subject: kernel: move regmap bloat out of the kernel image if it is only being used in modules
+
+lede-commit: 96f39119815028073583e4fca3a9c5fe9141e998
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/base/regmap/Kconfig | 15 ++++++++++-----
+ drivers/base/regmap/Makefile | 12 ++++++++----
+ drivers/base/regmap/regmap.c | 3 +++
+ include/linux/regmap.h | 2 +-
+ 4 files changed, 22 insertions(+), 10 deletions(-)
+
+--- a/drivers/base/regmap/Kconfig
++++ b/drivers/base/regmap/Kconfig
+@@ -4,10 +4,9 @@
+ # subsystems should select the appropriate symbols.
+
+ config REGMAP
+- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO)
+ select IRQ_DOMAIN if REGMAP_IRQ
+ select MDIO_BUS if REGMAP_MDIO
+- bool
++ tristate
+
+ config REGCACHE_COMPRESSED
+ select LZO_COMPRESS
+@@ -15,53 +14,67 @@ config REGCACHE_COMPRESSED
+ bool
+
+ config REGMAP_AC97
++ select REGMAP
+ tristate
+
+ config REGMAP_I2C
++ select REGMAP
+ tristate
+ depends on I2C
+
+ config REGMAP_SLIMBUS
++ select REGMAP
+ tristate
+ depends on SLIMBUS
+
+ config REGMAP_SPI
++ select REGMAP
+ tristate
+ depends on SPI
+
+ config REGMAP_SPMI
++ select REGMAP
+ tristate
+ depends on SPMI
+
+ config REGMAP_W1
++ select REGMAP
+ tristate
+ depends on W1
+
+ config REGMAP_MDIO
++ select REGMAP
+ tristate
+
+ config REGMAP_MMIO
++ select REGMAP
+ tristate
+
+ config REGMAP_IRQ
++ select REGMAP
+ bool
+
+ config REGMAP_SOUNDWIRE
++ select REGMAP
+ tristate
+ depends on SOUNDWIRE
+
+ config REGMAP_SOUNDWIRE_MBQ
++ select REGMAP
+ tristate
+ depends on SOUNDWIRE
+
+ config REGMAP_SCCB
++ select REGMAP
+ tristate
+ depends on I2C
+
+ config REGMAP_I3C
++ select REGMAP
+ tristate
+ depends on I3C
+
+ config REGMAP_SPI_AVMM
++ select REGMAP
+ tristate
+ depends on SPI
+--- a/drivers/base/regmap/Makefile
++++ b/drivers/base/regmap/Makefile
+@@ -2,10 +2,14 @@
+ # For include/trace/define_trace.h to include trace.h
+ CFLAGS_regmap.o := -I$(src)
+
+-obj-$(CONFIG_REGMAP) += regmap.o regcache.o
+-obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o
+-obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
+-obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
++regmap-core-objs = regmap.o regcache.o regcache-rbtree.o regcache-flat.o
++ifdef CONFIG_DEBUG_FS
++regmap-core-objs += regmap-debugfs.o
++endif
++ifdef CONFIG_REGCACHE_COMPRESSED
++regmap-core-objs += regcache-lzo.o
++endif
++obj-$(CONFIG_REGMAP) += regmap-core.o
+ obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
+ obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
+ obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o
+--- a/drivers/base/regmap/regmap.c
++++ b/drivers/base/regmap/regmap.c
+@@ -9,6 +9,7 @@
+ #include <linux/device.h>
+ #include <linux/slab.h>
+ #include <linux/export.h>
++#include <linux/module.h>
+ #include <linux/mutex.h>
+ #include <linux/err.h>
+ #include <linux/property.h>
+@@ -3358,3 +3359,5 @@ static int __init regmap_initcall(void)
+ return 0;
+ }
+ postcore_initcall(regmap_initcall);
++
++MODULE_LICENSE("GPL");
+--- a/include/linux/regmap.h
++++ b/include/linux/regmap.h
+@@ -180,7 +180,7 @@ struct reg_sequence {
+ __ret ?: __tmp; \
+ })
+
+-#ifdef CONFIG_REGMAP
++#if IS_REACHABLE(CONFIG_REGMAP)
+
+ enum regmap_endian {
+ /* Unspecified -> 0 -> Backwards compatible default */
diff --git a/target/linux/generic/hack-6.1/260-crypto_test_dependencies.patch b/target/linux/generic/hack-6.1/260-crypto_test_dependencies.patch
new file mode 100644
index 0000000000..64daa82743
--- /dev/null
+++ b/target/linux/generic/hack-6.1/260-crypto_test_dependencies.patch
@@ -0,0 +1,52 @@
+From fd1799b0bf5efa46dd3e6dfbbf3955564807e508 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:12:51 +0200
+Subject: kernel: prevent cryptomgr from pulling in useless extra dependencies for tests that are not run
+
+Reduces kernel size after LZMA by about 5k on MIPS
+
+lede-commit: 044c316167e076479a344c59905e5b435b84a77f
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ crypto/Kconfig | 13 ++++++-------
+ crypto/algboss.c | 4 ++++
+ 2 files changed, 10 insertions(+), 7 deletions(-)
+
+--- a/crypto/Kconfig
++++ b/crypto/Kconfig
+@@ -121,13 +121,13 @@ config CRYPTO_MANAGER
+ cbc(aes).
+
+ config CRYPTO_MANAGER2
+- def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y)
+- select CRYPTO_AEAD2
+- select CRYPTO_HASH2
+- select CRYPTO_SKCIPHER2
+- select CRYPTO_AKCIPHER2
+- select CRYPTO_KPP2
+- select CRYPTO_ACOMP2
++ def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y && !CRYPTO_MANAGER_DISABLE_TESTS)
++ select CRYPTO_AEAD2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_HASH2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_SKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_AKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_KPP2 if !CRYPTO_MANAGER_DISABLE_TESTS
++ select CRYPTO_ACOMP2 if !CRYPTO_MANAGER_DISABLE_TESTS
+
+ config CRYPTO_USER
+ tristate "Userspace cryptographic algorithm configuration"
+--- a/crypto/algboss.c
++++ b/crypto/algboss.c
+@@ -211,8 +211,12 @@ static int cryptomgr_schedule_test(struc
+ type = alg->cra_flags;
+
+ /* Do not test internal algorithms. */
++#ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS
++ type |= CRYPTO_ALG_TESTED;
++#else
+ if (type & CRYPTO_ALG_INTERNAL)
+ type |= CRYPTO_ALG_TESTED;
++#endif
+
+ param->type = type;
+
diff --git a/target/linux/generic/hack-6.1/261-lib-arc4-unhide.patch b/target/linux/generic/hack-6.1/261-lib-arc4-unhide.patch
new file mode 100644
index 0000000000..ee923c73f7
--- /dev/null
+++ b/target/linux/generic/hack-6.1/261-lib-arc4-unhide.patch
@@ -0,0 +1,24 @@
+From 241e5d3f7b0dd3c01f8c7fa83cbc9a3882286d53 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:35:18 +0200
+Subject: [PATCH] lib/crypto: add tristate string for ARC4
+
+This makes it possible to select CONFIG_CRYPTO_LIB_ARC4 directly. We
+need this to be able to compile this into the kernel and make use of it
+from backports.
+
+---
+ lib/crypto/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/lib/crypto/Kconfig
++++ b/lib/crypto/Kconfig
+@@ -6,7 +6,7 @@ config CRYPTO_LIB_AES
+ tristate
+
+ config CRYPTO_LIB_ARC4
+- tristate
++ tristate "ARC4 cipher library"
+
+ config CRYPTO_ARCH_HAVE_LIB_BLAKE2S
+ bool
diff --git a/target/linux/generic/hack-6.1/280-rfkill-stubs.patch b/target/linux/generic/hack-6.1/280-rfkill-stubs.patch
new file mode 100644
index 0000000000..7a650d132e
--- /dev/null
+++ b/target/linux/generic/hack-6.1/280-rfkill-stubs.patch
@@ -0,0 +1,84 @@
+From 236c1acdfef5958010ac9814a9872e0a46fd78ee Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 7 Jul 2017 17:13:44 +0200
+Subject: rfkill: add fake rfkill support
+
+allow building of modules depending on RFKILL even if RFKILL is not enabled.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ include/linux/rfkill.h | 2 +-
+ net/Makefile | 2 +-
+ net/rfkill/Kconfig | 14 +++++++++-----
+ net/rfkill/Makefile | 2 +-
+ 4 files changed, 12 insertions(+), 8 deletions(-)
+
+--- a/include/linux/rfkill.h
++++ b/include/linux/rfkill.h
+@@ -64,7 +64,7 @@ struct rfkill_ops {
+ int (*set_block)(void *data, bool blocked);
+ };
+
+-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
++#if defined(CONFIG_RFKILL_FULL) || defined(CONFIG_RFKILL_FULL_MODULE)
+ /**
+ * rfkill_alloc - Allocate rfkill structure
+ * @name: name of the struct -- the string is not copied internally
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -52,7 +52,7 @@ obj-$(CONFIG_TIPC) += tipc/
+ obj-$(CONFIG_NETLABEL) += netlabel/
+ obj-$(CONFIG_IUCV) += iucv/
+ obj-$(CONFIG_SMC) += smc/
+-obj-$(CONFIG_RFKILL) += rfkill/
++obj-$(CONFIG_RFKILL_FULL) += rfkill/
+ obj-$(CONFIG_NET_9P) += 9p/
+ obj-$(CONFIG_CAIF) += caif/
+ obj-$(CONFIG_DCB) += dcb/
+--- a/net/rfkill/Kconfig
++++ b/net/rfkill/Kconfig
+@@ -2,7 +2,11 @@
+ #
+ # RF switch subsystem configuration
+ #
+-menuconfig RFKILL
++config RFKILL
++ bool
++ default y
++
++menuconfig RFKILL_FULL
+ tristate "RF switch subsystem support"
+ help
+ Say Y here if you want to have control over RF switches
+@@ -14,19 +18,19 @@ menuconfig RFKILL
+ # LED trigger support
+ config RFKILL_LEDS
+ bool
+- depends on RFKILL
++ depends on RFKILL_FULL
+ depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS
+ default y
+
+ config RFKILL_INPUT
+ bool "RF switch input support" if EXPERT
+- depends on RFKILL
++ depends on RFKILL_FULL
+ depends on INPUT = y || RFKILL = INPUT
+ default y if !EXPERT
+
+ config RFKILL_GPIO
+ tristate "GPIO RFKILL driver"
+- depends on RFKILL
++ depends on RFKILL_FULL
+ depends on GPIOLIB || COMPILE_TEST
+ default n
+ help
+--- a/net/rfkill/Makefile
++++ b/net/rfkill/Makefile
+@@ -5,5 +5,5 @@
+
+ rfkill-y += core.o
+ rfkill-$(CONFIG_RFKILL_INPUT) += input.o
+-obj-$(CONFIG_RFKILL) += rfkill.o
++obj-$(CONFIG_RFKILL_FULL) += rfkill.o
+ obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o
diff --git a/target/linux/generic/hack-6.1/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch b/target/linux/generic/hack-6.1/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch
new file mode 100644
index 0000000000..f21f200136
--- /dev/null
+++ b/target/linux/generic/hack-6.1/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch
@@ -0,0 +1,64 @@
+From: Ben Menchaca <ben.menchaca@qca.qualcomm.com>
+Date: Fri, 7 Jun 2013 18:35:22 -0500
+Subject: MIPS: r4k_cache: use more efficient cache blast
+
+Optimize the compiler output for larger cache blast cases that are
+common for DMA-based networking.
+
+Signed-off-by: Ben Menchaca <ben.menchaca@qca.qualcomm.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+--- a/arch/mips/include/asm/r4kcache.h
++++ b/arch/mips/include/asm/r4kcache.h
+@@ -286,14 +286,46 @@ static inline void prot##extra##blast_##
+ unsigned long end) \
+ { \
+ unsigned long lsize = cpu_##desc##_line_size(); \
++ unsigned long lsize_2 = lsize * 2; \
++ unsigned long lsize_3 = lsize * 3; \
++ unsigned long lsize_4 = lsize * 4; \
++ unsigned long lsize_5 = lsize * 5; \
++ unsigned long lsize_6 = lsize * 6; \
++ unsigned long lsize_7 = lsize * 7; \
++ unsigned long lsize_8 = lsize * 8; \
+ unsigned long addr = start & ~(lsize - 1); \
+- unsigned long aend = (end - 1) & ~(lsize - 1); \
++ unsigned long aend = (end + lsize - 1) & ~(lsize - 1); \
++ int lines = (aend - addr) / lsize; \
+ \
+- while (1) { \
++ while (lines >= 8) { \
++ prot##cache_op(hitop, addr); \
++ prot##cache_op(hitop, addr + lsize); \
++ prot##cache_op(hitop, addr + lsize_2); \
++ prot##cache_op(hitop, addr + lsize_3); \
++ prot##cache_op(hitop, addr + lsize_4); \
++ prot##cache_op(hitop, addr + lsize_5); \
++ prot##cache_op(hitop, addr + lsize_6); \
++ prot##cache_op(hitop, addr + lsize_7); \
++ addr += lsize_8; \
++ lines -= 8; \
++ } \
++ \
++ if (lines & 0x4) { \
++ prot##cache_op(hitop, addr); \
++ prot##cache_op(hitop, addr + lsize); \
++ prot##cache_op(hitop, addr + lsize_2); \
++ prot##cache_op(hitop, addr + lsize_3); \
++ addr += lsize_4; \
++ } \
++ \
++ if (lines & 0x2) { \
++ prot##cache_op(hitop, addr); \
++ prot##cache_op(hitop, addr + lsize); \
++ addr += lsize_2; \
++ } \
++ \
++ if (lines & 0x1) { \
+ prot##cache_op(hitop, addr); \
+- if (addr == aend) \
+- break; \
+- addr += lsize; \
+ } \
+ }
+
diff --git a/target/linux/generic/hack-6.1/321-powerpc_crtsavres_prereq.patch b/target/linux/generic/hack-6.1/321-powerpc_crtsavres_prereq.patch
new file mode 100644
index 0000000000..f1942e2037
--- /dev/null
+++ b/target/linux/generic/hack-6.1/321-powerpc_crtsavres_prereq.patch
@@ -0,0 +1,38 @@
+From 107c0964cb8db7ca28ac5199426414fdab3c274d Mon Sep 17 00:00:00 2001
+From: "Alexandros C. Couloumbis" <alex@ozo.com>
+Date: Fri, 7 Jul 2017 17:14:51 +0200
+Subject: hack: arch: powerpc: drop register save/restore library from modules
+
+Upstream GCC uses a libgcc function for saving/restoring registers. This
+makes the code bigger, and upstream kernels need to carry that function
+for every single kernel module. Our GCC is patched to avoid those
+references, so we can drop the extra bloat for modules.
+
+lede-commit: e8e1084654f50904e6bf77b70b2de3f137d7b3ec
+Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com>
+---
+ arch/powerpc/Makefile | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/arch/powerpc/Makefile
++++ b/arch/powerpc/Makefile
+@@ -44,19 +44,6 @@ machine-$(CONFIG_PPC64) += 64
+ machine-$(CONFIG_CPU_LITTLE_ENDIAN) += le
+ UTS_MACHINE := $(subst $(space),,$(machine-y))
+
+-# XXX This needs to be before we override LD below
+-ifdef CONFIG_PPC32
+-KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o
+-else
+-ifeq ($(call ld-ifversion, -ge, 22500, y),y)
+-# Have the linker provide sfpr if possible.
+-# There is a corresponding test in arch/powerpc/lib/Makefile
+-KBUILD_LDFLAGS_MODULE += --save-restore-funcs
+-else
+-KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o
+-endif
+-endif
+-
+ ifdef CONFIG_CPU_LITTLE_ENDIAN
+ KBUILD_CFLAGS += -mlittle-endian
+ KBUILD_LDFLAGS += -EL
diff --git a/target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch b/target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch
new file mode 100644
index 0000000000..32a7fb6266
--- /dev/null
+++ b/target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch
@@ -0,0 +1,98 @@
+From 0bccc3722bdd88e8ae995e77ef9f7b77ee4cbdee Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 7 Apr 2021 22:45:54 +0100
+Subject: [PATCH 2/2] mtd: blktrans: call add disks after mtd device
+To: linux-mtd@lists.infradead.org
+Cc: Vignesh Raghavendra <vigneshr@ti.com>,
+ Richard Weinberger <richard@nod.at>,
+ Miquel Raynal <miquel.raynal@bootlin.com>,
+ David Woodhouse <dwmw2@infradead.org>
+
+Calling device_add_disk while holding mtd_table_mutex leads
+to deadlock in case part_bits!=0 as block partition parsers
+will try to open the newly created disks, trying to acquire
+mutex once again.
+Move device_add_disk to additional function called after
+add partitions of an MTD device have been added and locks
+have been released.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/mtd_blkdevs.c | 33 ++++++++++++++++++++++++++-------
+ drivers/mtd/mtdcore.c | 3 +++
+ include/linux/mtd/blktrans.h | 1 +
+ 3 files changed, 30 insertions(+), 7 deletions(-)
+
+--- a/drivers/mtd/mtd_blkdevs.c
++++ b/drivers/mtd/mtd_blkdevs.c
+@@ -384,13 +384,6 @@ int add_mtd_blktrans_dev(struct mtd_blkt
+ if (new->readonly)
+ set_disk_ro(gd, 1);
+
+- device_add_disk(&new->mtd->dev, gd, NULL);
+-
+- if (new->disk_attributes) {
+- ret = sysfs_create_group(&disk_to_dev(gd)->kobj,
+- new->disk_attributes);
+- WARN_ON(ret);
+- }
+ return 0;
+
+ out_free_tag_set:
+@@ -402,6 +395,27 @@ out_list_del:
+ return ret;
+ }
+
++void register_mtd_blktrans_devs(void)
++{
++ struct mtd_blktrans_ops *tr;
++ struct mtd_blktrans_dev *dev, *next;
++ int ret;
++
++ list_for_each_entry(tr, &blktrans_majors, list) {
++ list_for_each_entry_safe(dev, next, &tr->devs, list) {
++ if (disk_live(dev->disk))
++ continue;
++
++ device_add_disk(&dev->mtd->dev, dev->disk, NULL);
++ if (dev->disk_attributes) {
++ ret = sysfs_create_group(&disk_to_dev(dev->disk)->kobj,
++ dev->disk_attributes);
++ WARN_ON(ret);
++ }
++ }
++ }
++}
++
+ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
+ {
+ unsigned long flags;
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -32,6 +32,7 @@
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/mtd/blktrans.h>
+
+ #include "mtdcore.h"
+
+@@ -1082,6 +1083,8 @@ int mtd_device_parse_register(struct mtd
+ register_reboot_notifier(&mtd->reboot_notifier);
+ }
+
++ register_mtd_blktrans_devs();
++
+ out:
+ if (ret) {
+ nvmem_unregister(mtd->otp_user_nvmem);
+--- a/include/linux/mtd/blktrans.h
++++ b/include/linux/mtd/blktrans.h
+@@ -76,6 +76,7 @@ extern int deregister_mtd_blktrans(struc
+ extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
+ extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
+ extern int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev);
++extern void register_mtd_blktrans_devs(void);
+
+ /**
+ * module_mtd_blktrans() - Helper macro for registering a mtd blktrans driver
diff --git a/target/linux/generic/hack-6.1/410-block-fit-partition-parser.patch b/target/linux/generic/hack-6.1/410-block-fit-partition-parser.patch
new file mode 100644
index 0000000000..96c89f41c4
--- /dev/null
+++ b/target/linux/generic/hack-6.1/410-block-fit-partition-parser.patch
@@ -0,0 +1,214 @@
+From 69357074558daf6ff24c9f58714935e9e095a865 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:37:33 +0200
+Subject: [PATCH] kernel: add block fit partition parser
+
+---
+ block/blk.h | 2 ++
+ block/partitions/Kconfig | 7 +++++++
+ block/partitions/Makefile | 1 +
+ block/partitions/check.h | 3 +++
+ block/partitions/core.c | 17 +++++++++++++++++
+ block/partitions/efi.c | 8 ++++++++
+ block/partitions/efi.h | 3 +++
+ block/partitions/msdos.c | 10 ++++++++++
+ drivers/mtd/mtd_blkdevs.c | 2 ++
+ drivers/mtd/ubi/block.c | 3 +++
+ include/linux/msdos_partition.h | 1 +
+ 11 files changed, 57 insertions(+)
+
+--- a/block/blk.h
++++ b/block/blk.h
+@@ -354,6 +354,8 @@ void blk_free_ext_minor(unsigned int min
+ #define ADDPART_FLAG_NONE 0
+ #define ADDPART_FLAG_RAID 1
+ #define ADDPART_FLAG_WHOLEDISK 2
++#define ADDPART_FLAG_READONLY 4
++#define ADDPART_FLAG_ROOTDEV 8
+ int bdev_add_partition(struct gendisk *disk, int partno, sector_t start,
+ sector_t length);
+ int bdev_del_partition(struct gendisk *disk, int partno);
+--- a/block/partitions/Kconfig
++++ b/block/partitions/Kconfig
+@@ -101,6 +101,13 @@ config ATARI_PARTITION
+ Say Y here if you would like to use hard disks under Linux which
+ were partitioned under the Atari OS.
+
++config FIT_PARTITION
++ bool "Flattened-Image-Tree (FIT) partition support" if PARTITION_ADVANCED
++ default n
++ help
++ Say Y here if your system needs to mount the filesystem part of
++ a Flattened-Image-Tree (FIT) image commonly used with Das U-Boot.
++
+ config IBM_PARTITION
+ bool "IBM disk label and partition support"
+ depends on PARTITION_ADVANCED && S390
+--- a/block/partitions/Makefile
++++ b/block/partitions/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_ACORN_PARTITION) += acorn.o
+ obj-$(CONFIG_AMIGA_PARTITION) += amiga.o
+ obj-$(CONFIG_ATARI_PARTITION) += atari.o
+ obj-$(CONFIG_AIX_PARTITION) += aix.o
++obj-$(CONFIG_FIT_PARTITION) += fit.o
+ obj-$(CONFIG_CMDLINE_PARTITION) += cmdline.o
+ obj-$(CONFIG_MAC_PARTITION) += mac.o
+ obj-$(CONFIG_LDM_PARTITION) += ldm.o
+--- a/block/partitions/check.h
++++ b/block/partitions/check.h
+@@ -58,6 +58,7 @@ int amiga_partition(struct parsed_partit
+ int atari_partition(struct parsed_partitions *state);
+ int cmdline_partition(struct parsed_partitions *state);
+ int efi_partition(struct parsed_partitions *state);
++int fit_partition(struct parsed_partitions *state);
+ int ibm_partition(struct parsed_partitions *);
+ int karma_partition(struct parsed_partitions *state);
+ int ldm_partition(struct parsed_partitions *state);
+@@ -68,3 +69,5 @@ int sgi_partition(struct parsed_partitio
+ int sun_partition(struct parsed_partitions *state);
+ int sysv68_partition(struct parsed_partitions *state);
+ int ultrix_partition(struct parsed_partitions *state);
++
++int parse_fit_partitions(struct parsed_partitions *state, u64 start_sector, u64 nr_sectors, int *slot, int add_remain);
+--- a/block/partitions/core.c
++++ b/block/partitions/core.c
+@@ -12,6 +12,10 @@
+ #include <linux/vmalloc.h>
+ #include <linux/blktrace_api.h>
+ #include <linux/raid/detect.h>
++#ifdef CONFIG_FIT_PARTITION
++#include <linux/root_dev.h>
++#endif
++
+ #include "check.h"
+
+ static int (*check_part[])(struct parsed_partitions *) = {
+@@ -48,6 +52,9 @@ static int (*check_part[])(struct parsed
+ #ifdef CONFIG_EFI_PARTITION
+ efi_partition, /* this must come before msdos */
+ #endif
++#ifdef CONFIG_FIT_PARTITION
++ fit_partition,
++#endif
+ #ifdef CONFIG_SGI_PARTITION
+ sgi_partition,
+ #endif
+@@ -408,6 +415,11 @@ static struct block_device *add_partitio
+ goto out_del;
+ }
+
++#ifdef CONFIG_FIT_PARTITION
++ if (flags & ADDPART_FLAG_READONLY)
++ bdev->bd_read_only = true;
++#endif
++
+ /* everything is up and running, commence */
+ err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL);
+ if (err)
+@@ -598,6 +610,11 @@ static bool blk_add_partition(struct gen
+ (state->parts[p].flags & ADDPART_FLAG_RAID))
+ md_autodetect_dev(part->bd_dev);
+
++#ifdef CONFIG_FIT_PARTITION
++ if ((state->parts[p].flags & ADDPART_FLAG_ROOTDEV) && ROOT_DEV == 0)
++ ROOT_DEV = part->bd_dev;
++#endif
++
+ return true;
+ }
+
+--- a/block/partitions/efi.c
++++ b/block/partitions/efi.c
+@@ -716,6 +716,9 @@ int efi_partition(struct parsed_partitio
+ gpt_entry *ptes = NULL;
+ u32 i;
+ unsigned ssz = queue_logical_block_size(state->disk->queue) / 512;
++#ifdef CONFIG_FIT_PARTITION
++ u32 extra_slot = 64;
++#endif
+
+ if (!find_valid_gpt(state, &gpt, &ptes) || !gpt || !ptes) {
+ kfree(gpt);
+@@ -749,6 +752,11 @@ int efi_partition(struct parsed_partitio
+ ARRAY_SIZE(ptes[i].partition_name));
+ utf16_le_to_7bit(ptes[i].partition_name, label_max, info->volname);
+ state->parts[i + 1].has_info = true;
++#ifdef CONFIG_FIT_PARTITION
++ /* If this is a U-Boot FIT volume it may have subpartitions */
++ if (!efi_guidcmp(ptes[i].partition_type_guid, PARTITION_LINUX_FIT_GUID))
++ (void) parse_fit_partitions(state, start * ssz, size * ssz, &extra_slot, 1);
++#endif
+ }
+ kfree(ptes);
+ kfree(gpt);
+--- a/block/partitions/efi.h
++++ b/block/partitions/efi.h
+@@ -52,6 +52,9 @@
+ #define PARTITION_LINUX_LVM_GUID \
+ EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \
+ 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28)
++#define PARTITION_LINUX_FIT_GUID \
++ EFI_GUID( 0xcae9be83, 0xb15f, 0x49cc, \
++ 0x86, 0x3f, 0x08, 0x1b, 0x74, 0x4a, 0x2d, 0x93)
+
+ typedef struct _gpt_header {
+ __le64 signature;
+--- a/block/partitions/msdos.c
++++ b/block/partitions/msdos.c
+@@ -564,6 +564,15 @@ static void parse_minix(struct parsed_pa
+ #endif /* CONFIG_MINIX_SUBPARTITION */
+ }
+
++static void parse_fit_mbr(struct parsed_partitions *state,
++ sector_t offset, sector_t size, int origin)
++{
++#ifdef CONFIG_FIT_PARTITION
++ u32 extra_slot = 64;
++ (void) parse_fit_partitions(state, offset, size, &extra_slot, 1);
++#endif /* CONFIG_FIT_PARTITION */
++}
++
+ static struct {
+ unsigned char id;
+ void (*parse)(struct parsed_partitions *, sector_t, sector_t, int);
+@@ -575,6 +584,7 @@ static struct {
+ {UNIXWARE_PARTITION, parse_unixware},
+ {SOLARIS_X86_PARTITION, parse_solaris_x86},
+ {NEW_SOLARIS_X86_PARTITION, parse_solaris_x86},
++ {FIT_PARTITION, parse_fit_mbr},
+ {0, NULL},
+ };
+
+--- a/drivers/mtd/mtd_blkdevs.c
++++ b/drivers/mtd/mtd_blkdevs.c
+@@ -345,6 +345,8 @@ int add_mtd_blktrans_dev(struct mtd_blkt
+ gd->first_minor = (new->devnum) << tr->part_bits;
+ gd->minors = 1 << tr->part_bits;
+ gd->fops = &mtd_block_ops;
++ if (IS_ENABLED(CONFIG_FIT_PARTITION) && !mtd_type_is_nand(new->mtd))
++ gd->flags |= GENHD_FL_EXT_DEVT;
+
+ if (tr->part_bits)
+ if (new->devnum < 26)
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -428,6 +428,9 @@ int ubiblock_create(struct ubi_volume_in
+ goto out_cleanup_disk;
+ }
+ gd->private_data = dev;
++#ifdef CONFIG_FIT_PARTITION
++ gd->flags |= GENHD_FL_EXT_DEVT;
++#endif
+ sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id);
+ set_capacity(gd, disk_capacity);
+ dev->gd = gd;
+--- a/include/linux/msdos_partition.h
++++ b/include/linux/msdos_partition.h
+@@ -31,6 +31,7 @@ enum msdos_sys_ind {
+ LINUX_LVM_PARTITION = 0x8e,
+ LINUX_RAID_PARTITION = 0xfd, /* autodetect RAID partition */
+
++ FIT_PARTITION = 0x2e, /* U-Boot uImage.FIT */
+ SOLARIS_X86_PARTITION = 0x82, /* also Linux swap partitions */
+ NEW_SOLARIS_X86_PARTITION = 0xbf,
+
diff --git a/target/linux/generic/hack-6.1/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch b/target/linux/generic/hack-6.1/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch
new file mode 100644
index 0000000000..947b2c262f
--- /dev/null
+++ b/target/linux/generic/hack-6.1/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch
@@ -0,0 +1,24 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 7 Nov 2022 23:48:24 +0100
+Subject: [PATCH] mtd: support OpenWrt's MTD_ROOTFS_ROOT_DEV
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This allows setting ROOT_DEV to MTD partition named "rootfs".
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -761,7 +761,8 @@ int add_mtd_device(struct mtd_info *mtd)
+
+ mutex_unlock(&mtd_table_mutex);
+
+- if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL)) {
++ if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL) ||
++ (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && !strcmp(mtd->name, "rootfs") && ROOT_DEV == 0)) {
+ if (IS_BUILTIN(CONFIG_MTD)) {
+ pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
diff --git a/target/linux/generic/hack-6.1/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch b/target/linux/generic/hack-6.1/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch
new file mode 100644
index 0000000000..965a331a19
--- /dev/null
+++ b/target/linux/generic/hack-6.1/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch
@@ -0,0 +1,120 @@
+From 6fa9e3678eb002246df1280322b6a024853950a5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 11 Oct 2021 00:53:14 +0200
+Subject: [PATCH] drivers: mtd: parsers: add nvmem support to cmdlinepart
+
+Assuming cmdlinepart is only one level deep partition scheme and that
+static partition are also defined in DTS, we can assign an of_node for
+partition declared from bootargs. cmdlinepart have priority than
+fiexed-partition parser so in this specific case the parser doesn't
+assign an of_node. Fix this by searching a defined of_node using a
+similar fixed_partition parser and if a partition is found with the same
+label, check that it has the same offset and size and return the DT
+of_node to correctly use NVMEM cells.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+---
+ drivers/mtd/parsers/cmdlinepart.c | 71 +++++++++++++++++++++++++++++++
+ 1 file changed, 71 insertions(+)
+
+--- a/drivers/mtd/parsers/cmdlinepart.c
++++ b/drivers/mtd/parsers/cmdlinepart.c
+@@ -43,6 +43,7 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/module.h>
+ #include <linux/err.h>
++#include <linux/of.h>
+
+ /* debug macro */
+ #if 0
+@@ -323,6 +324,68 @@ static int mtdpart_setup_real(char *s)
+ return 0;
+ }
+
++static int search_fixed_partition(struct mtd_info *master,
++ struct mtd_partition *target_part,
++ struct mtd_partition *fixed_part)
++{
++ struct device_node *mtd_node;
++ struct device_node *ofpart_node;
++ struct device_node *pp;
++ struct mtd_partition part;
++ const char *partname;
++
++ mtd_node = mtd_get_of_node(master);
++ if (!mtd_node)
++ return -EINVAL;
++
++ ofpart_node = of_get_child_by_name(mtd_node, "partitions");
++
++ for_each_child_of_node(ofpart_node, pp) {
++ const __be32 *reg;
++ int len;
++ int a_cells, s_cells;
++
++ reg = of_get_property(pp, "reg", &len);
++ if (!reg) {
++ pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n",
++ master->name, pp,
++ mtd_node);
++ continue;
++ }
++
++ a_cells = of_n_addr_cells(pp);
++ s_cells = of_n_size_cells(pp);
++ if (len / 4 != a_cells + s_cells) {
++ pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n",
++ master->name, pp,
++ mtd_node);
++ continue;
++ }
++
++ part.offset = of_read_number(reg, a_cells);
++ part.size = of_read_number(reg + a_cells, s_cells);
++ part.of_node = pp;
++
++ partname = of_get_property(pp, "label", &len);
++ if (!partname)
++ partname = of_get_property(pp, "name", &len);
++ part.name = partname;
++
++ if (!strncmp(target_part->name, part.name, len)) {
++ if (part.offset != target_part->offset)
++ return -EINVAL;
++
++ if (part.size != target_part->size)
++ return -EINVAL;
++
++ memcpy(fixed_part, &part, sizeof(struct mtd_partition));
++ return 0;
++ }
++ }
++
++ return -EINVAL;
++}
++
+ /*
+ * Main function to be called from the MTD mapping driver/device to
+ * obtain the partitioning information. At this point the command line
+@@ -338,6 +401,7 @@ static int parse_cmdline_partitions(stru
+ int i, err;
+ struct cmdline_mtd_partition *part;
+ const char *mtd_id = master->name;
++ struct mtd_partition fixed_part;
+
+ /* parse command line */
+ if (!cmdline_parsed) {
+@@ -382,6 +446,13 @@ static int parse_cmdline_partitions(stru
+ sizeof(*part->parts) * (part->num_parts - i));
+ i--;
+ }
++
++ err = search_fixed_partition(master, &part->parts[i], &fixed_part);
++ if (!err) {
++ part->parts[i].of_node = fixed_part.of_node;
++ pr_info("Found partition defined in DT for %s. Assigning OF node to support nvmem.",
++ part->parts[i].name);
++ }
+ }
+
+ *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
diff --git a/target/linux/generic/hack-6.1/430-mtk-bmt-support.patch b/target/linux/generic/hack-6.1/430-mtk-bmt-support.patch
new file mode 100644
index 0000000000..2a83f46e0e
--- /dev/null
+++ b/target/linux/generic/hack-6.1/430-mtk-bmt-support.patch
@@ -0,0 +1,33 @@
+From ac84397efb3b3868c71c10ad7521161773228a17 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:41:44 +0200
+Subject: [PATCH] mtd/nand: add MediaTek NAND bad block managment table
+
+---
+ drivers/mtd/nand/Kconfig | 4 ++++
+ drivers/mtd/nand/Makefile | 1 +
+ 2 files changed, 5 insertions(+)
+
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -46,6 +46,10 @@ config MTD_NAND_ECC_SW_BCH
+ ECC codes. They are used with NAND devices requiring more than 1 bit
+ of error correction.
+
++config MTD_NAND_MTK_BMT
++ bool "Support MediaTek NAND Bad-block Management Table"
++ default n
++
+ endmenu
+
+ endmenu
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -2,6 +2,7 @@
+
+ nandcore-objs := core.o bbt.o
+ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
++obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o
+
+ obj-y += onenand/
+ obj-y += raw/
diff --git a/target/linux/generic/hack-6.1/600-bridge_offload.patch b/target/linux/generic/hack-6.1/600-bridge_offload.patch
new file mode 100644
index 0000000000..9d71a741b2
--- /dev/null
+++ b/target/linux/generic/hack-6.1/600-bridge_offload.patch
@@ -0,0 +1,846 @@
+From 11c3fae5afa6cac444d12622e2cf5af60a99c1ef Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:43:15 +0200
+Subject: [PATCH] net/bridge: add bridge offload
+
+---
+ include/linux/if_bridge.h | 1 +
+ net/bridge/Makefile | 2 +-
+ net/bridge/br.c | 8 +
+ net/bridge/br_device.c | 2 +
+ net/bridge/br_fdb.c | 5 +
+ net/bridge/br_forward.c | 3 +
+ net/bridge/br_if.c | 6 +-
+ net/bridge/br_input.c | 5 +
+ net/bridge/br_offload.c | 438 ++++++++++++++++++++++++++++++++
+ net/bridge/br_private.h | 22 +-
+ net/bridge/br_private_offload.h | 23 ++
+ net/bridge/br_stp.c | 3 +
+ net/bridge/br_sysfs_br.c | 35 +++
+ net/bridge/br_sysfs_if.c | 2 +
+ net/bridge/br_vlan_tunnel.c | 3 +
+ 15 files changed, 555 insertions(+), 3 deletions(-)
+ create mode 100644 net/bridge/br_offload.c
+ create mode 100644 net/bridge/br_private_offload.h
+
+--- a/include/linux/if_bridge.h
++++ b/include/linux/if_bridge.h
+@@ -59,6 +59,7 @@ struct br_ip_list {
+ #define BR_MRP_LOST_IN_CONT BIT(19)
+ #define BR_TX_FWD_OFFLOAD BIT(20)
+ #define BR_BPDU_FILTER BIT(21)
++#define BR_OFFLOAD BIT(22)
+
+ #define BR_DEFAULT_AGEING_TIME (300 * HZ)
+
+--- a/net/bridge/Makefile
++++ b/net/bridge/Makefile
+@@ -5,7 +5,7 @@
+
+ obj-$(CONFIG_BRIDGE) += bridge.o
+
+-bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
++bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o br_offload.o \
+ br_ioctl.o br_stp.o br_stp_bpdu.o \
+ br_stp_if.o br_stp_timer.o br_netlink.o \
+ br_netlink_tunnel.o br_arp_nd_proxy.o
+--- a/net/bridge/br.c
++++ b/net/bridge/br.c
+@@ -18,6 +18,7 @@
+ #include <net/switchdev.h>
+
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ /*
+ * Handle changes in state of network devices enslaved to a bridge.
+@@ -381,6 +382,10 @@ static int __init br_init(void)
+ if (err)
+ goto err_out;
+
++ err = br_offload_init();
++ if (err)
++ goto err_out0;
++
+ err = register_pernet_subsys(&br_net_ops);
+ if (err)
+ goto err_out1;
+@@ -430,6 +435,8 @@ err_out3:
+ err_out2:
+ unregister_pernet_subsys(&br_net_ops);
+ err_out1:
++ br_offload_fini();
++err_out0:
+ br_fdb_fini();
+ err_out:
+ stp_proto_unregister(&br_stp_proto);
+@@ -452,6 +459,7 @@ static void __exit br_deinit(void)
+ #if IS_ENABLED(CONFIG_ATM_LANE)
+ br_fdb_test_addr_hook = NULL;
+ #endif
++ br_offload_fini();
+ br_fdb_fini();
+ }
+
+--- a/net/bridge/br_device.c
++++ b/net/bridge/br_device.c
+@@ -524,6 +524,8 @@ void br_dev_setup(struct net_device *dev
+ br->bridge_hello_time = br->hello_time = 2 * HZ;
+ br->bridge_forward_delay = br->forward_delay = 15 * HZ;
+ br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME;
++ br->offload_cache_size = 128;
++ br->offload_cache_reserved = 8;
+ dev->max_mtu = ETH_MAX_MTU;
+
+ br_netfilter_rtable_init(br);
+--- a/net/bridge/br_fdb.c
++++ b/net/bridge/br_fdb.c
+@@ -23,6 +23,7 @@
+ #include <net/switchdev.h>
+ #include <trace/events/bridge.h>
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ static const struct rhashtable_params br_fdb_rht_params = {
+ .head_offset = offsetof(struct net_bridge_fdb_entry, rhnode),
+@@ -518,6 +519,8 @@ static struct net_bridge_fdb_entry *fdb_
+ fdb->key.vlan_id = vid;
+ fdb->flags = flags;
+ fdb->updated = fdb->used = jiffies;
++ INIT_HLIST_HEAD(&fdb->offload_in);
++ INIT_HLIST_HEAD(&fdb->offload_out);
+ if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl,
+ &fdb->rhnode,
+ br_fdb_rht_params)) {
+@@ -794,6 +797,8 @@ static void fdb_notify(struct net_bridge
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
++ br_offload_fdb_update(fdb);
++
+ if (swdev_notify)
+ br_switchdev_fdb_notify(br, fdb, type);
+
+--- a/net/bridge/br_forward.c
++++ b/net/bridge/br_forward.c
+@@ -16,6 +16,7 @@
+ #include <linux/if_vlan.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ /* Don't forward packets to originating port or forwarding disabled */
+ static inline int should_deliver(const struct net_bridge_port *p,
+@@ -32,6 +33,8 @@ static inline int should_deliver(const s
+
+ int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
+ {
++ br_offload_output(skb);
++
+ skb_push(skb, ETH_HLEN);
+ if (!is_skb_forwardable(skb->dev, skb))
+ goto drop;
+--- a/net/bridge/br_if.c
++++ b/net/bridge/br_if.c
+@@ -25,6 +25,7 @@
+ #include <net/net_namespace.h>
+
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ /*
+ * Determine initial path cost based on speed.
+@@ -428,7 +429,7 @@ static struct net_bridge_port *new_nbp(s
+ p->path_cost = port_cost(dev);
+ p->priority = 0x8000 >> BR_PORT_BITS;
+ p->port_no = index;
+- p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
++ p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_OFFLOAD;
+ br_init_port(p);
+ br_set_state(p, BR_STATE_DISABLED);
+ br_stp_port_timer_init(p);
+@@ -771,6 +772,9 @@ void br_port_flags_change(struct net_bri
+
+ if (mask & BR_NEIGH_SUPPRESS)
+ br_recalculate_neigh_suppress_enabled(br);
++
++ if (mask & BR_OFFLOAD)
++ br_offload_port_state(p);
+ }
+
+ bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
+--- a/net/bridge/br_input.c
++++ b/net/bridge/br_input.c
+@@ -22,6 +22,7 @@
+ #include <linux/rculist.h>
+ #include "br_private.h"
+ #include "br_private_tunnel.h"
++#include "br_private_offload.h"
+
+ static int
+ br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
+@@ -171,6 +172,7 @@ int br_handle_frame_finish(struct net *n
+ dst->used = now;
+ br_forward(dst->dst, skb, local_rcv, false);
+ } else {
++ br_offload_skb_disable(skb);
+ if (!mcast_hit)
+ br_flood(br, skb, pkt_type, local_rcv, false);
+ else
+@@ -304,6 +306,9 @@ static rx_handler_result_t br_handle_fra
+ memset(skb->cb, 0, sizeof(struct br_input_skb_cb));
+
+ p = br_port_get_rcu(skb->dev);
++ if (br_offload_input(p, skb))
++ return RX_HANDLER_CONSUMED;
++
+ if (p->flags & BR_VLAN_TUNNEL)
+ br_handle_ingress_vlan_tunnel(skb, p, nbp_vlan_group_rcu(p));
+
+--- /dev/null
++++ b/net/bridge/br_offload.c
+@@ -0,0 +1,438 @@
++// SPDX-License-Identifier: GPL-2.0-only
++#include <linux/kernel.h>
++#include <linux/workqueue.h>
++#include "br_private.h"
++#include "br_private_offload.h"
++
++static DEFINE_SPINLOCK(offload_lock);
++
++struct bridge_flow_key {
++ u8 dest[ETH_ALEN];
++ u8 src[ETH_ALEN];
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ u16 vlan_tag;
++ bool vlan_present;
++#endif
++};
++
++struct bridge_flow {
++ struct net_bridge_port *port;
++ struct rhash_head node;
++ struct bridge_flow_key key;
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ bool vlan_out_present;
++ u16 vlan_out;
++#endif
++
++ unsigned long used;
++ struct net_bridge_fdb_entry *fdb_in, *fdb_out;
++ struct hlist_node fdb_list_in, fdb_list_out;
++
++ struct rcu_head rcu;
++};
++
++static const struct rhashtable_params flow_params = {
++ .automatic_shrinking = true,
++ .head_offset = offsetof(struct bridge_flow, node),
++ .key_len = sizeof(struct bridge_flow_key),
++ .key_offset = offsetof(struct bridge_flow, key),
++};
++
++static struct kmem_cache *offload_cache __read_mostly;
++
++static void
++flow_rcu_free(struct rcu_head *head)
++{
++ struct bridge_flow *flow;
++
++ flow = container_of(head, struct bridge_flow, rcu);
++ kmem_cache_free(offload_cache, flow);
++}
++
++static void
++__br_offload_flow_free(struct bridge_flow *flow)
++{
++ flow->used = 0;
++ hlist_del(&flow->fdb_list_in);
++ hlist_del(&flow->fdb_list_out);
++
++ call_rcu(&flow->rcu, flow_rcu_free);
++}
++
++static void
++br_offload_flow_free(struct bridge_flow *flow)
++{
++ if (rhashtable_remove_fast(&flow->port->offload.rht, &flow->node,
++ flow_params) != 0)
++ return;
++
++ __br_offload_flow_free(flow);
++}
++
++static bool
++br_offload_flow_fdb_refresh_time(struct bridge_flow *flow,
++ struct net_bridge_fdb_entry *fdb)
++{
++ if (!time_after(flow->used, fdb->updated))
++ return false;
++
++ fdb->updated = flow->used;
++
++ return true;
++}
++
++
++static void
++br_offload_flow_refresh_time(struct bridge_flow *flow)
++{
++ br_offload_flow_fdb_refresh_time(flow, flow->fdb_in);
++ br_offload_flow_fdb_refresh_time(flow, flow->fdb_out);
++}
++
++static void
++br_offload_destroy_cb(void *ptr, void *arg)
++{
++ struct bridge_flow *flow = ptr;
++
++ __br_offload_flow_free(flow);
++}
++
++static bool
++br_offload_need_gc(struct net_bridge_port *p)
++{
++ return (atomic_read(&p->offload.rht.nelems) +
++ p->br->offload_cache_reserved) >= p->br->offload_cache_size;
++}
++
++static void
++br_offload_gc_work(struct work_struct *work)
++{
++ struct rhashtable_iter hti;
++ struct net_bridge_port *p;
++ struct bridge_flow *gc_flow = NULL;
++ struct bridge_flow *flow;
++ unsigned long gc_used;
++
++ p = container_of(work, struct net_bridge_port, offload.gc_work);
++
++ if (!br_offload_need_gc(p))
++ return;
++
++ rhashtable_walk_enter(&p->offload.rht, &hti);
++ rhashtable_walk_start(&hti);
++ while ((flow = rhashtable_walk_next(&hti)) != NULL) {
++ unsigned long used;
++
++ if (IS_ERR(flow))
++ continue;
++
++ used = READ_ONCE(flow->used);
++ if (!used)
++ continue;
++
++ if (gc_flow && !time_before(used, gc_used))
++ continue;
++
++ gc_flow = flow;
++ gc_used = used;
++ }
++ rhashtable_walk_stop(&hti);
++ rhashtable_walk_exit(&hti);
++
++ if (!gc_flow)
++ return;
++
++ spin_lock_bh(&offload_lock);
++ if (br_offload_need_gc(p) && gc_flow &&
++ gc_flow->used == gc_used)
++ br_offload_flow_free(gc_flow);
++ if (p->offload.enabled && br_offload_need_gc(p))
++ queue_work(system_long_wq, work);
++ spin_unlock_bh(&offload_lock);
++
++}
++
++void br_offload_port_state(struct net_bridge_port *p)
++{
++ struct net_bridge_port_offload *o = &p->offload;
++ bool enabled = true;
++ bool flush = false;
++
++ if (p->state != BR_STATE_FORWARDING ||
++ !(p->flags & BR_OFFLOAD))
++ enabled = false;
++
++ spin_lock_bh(&offload_lock);
++ if (o->enabled == enabled)
++ goto out;
++
++ if (enabled) {
++ if (!o->gc_work.func)
++ INIT_WORK(&o->gc_work, br_offload_gc_work);
++ rhashtable_init(&o->rht, &flow_params);
++ } else {
++ flush = true;
++ rhashtable_free_and_destroy(&o->rht, br_offload_destroy_cb, o);
++ }
++
++ o->enabled = enabled;
++
++out:
++ spin_unlock_bh(&offload_lock);
++
++ if (flush)
++ flush_work(&o->gc_work);
++}
++
++void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb)
++{
++ struct bridge_flow *f;
++ struct hlist_node *tmp;
++
++ spin_lock_bh(&offload_lock);
++
++ hlist_for_each_entry_safe(f, tmp, &fdb->offload_in, fdb_list_in)
++ br_offload_flow_free(f);
++
++ hlist_for_each_entry_safe(f, tmp, &fdb->offload_out, fdb_list_out)
++ br_offload_flow_free(f);
++
++ spin_unlock_bh(&offload_lock);
++}
++
++static void
++br_offload_prepare_key(struct net_bridge_port *p, struct bridge_flow_key *key,
++ struct sk_buff *skb)
++{
++ memset(key, 0, sizeof(*key));
++ memcpy(key, eth_hdr(skb), 2 * ETH_ALEN);
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ if (!br_opt_get(p->br, BROPT_VLAN_ENABLED))
++ return;
++
++ if (!skb_vlan_tag_present(skb) || skb->vlan_proto != p->br->vlan_proto)
++ return;
++
++ key->vlan_present = true;
++ key->vlan_tag = skb_vlan_tag_get_id(skb);
++#endif
++}
++
++void br_offload_output(struct sk_buff *skb)
++{
++ struct net_bridge_port_offload *o;
++ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb;
++ struct net_bridge_port *p, *inp;
++ struct net_device *dev;
++ struct net_bridge_fdb_entry *fdb_in, *fdb_out;
++ struct net_bridge_vlan_group *vg;
++ struct bridge_flow_key key;
++ struct bridge_flow *flow;
++ u16 vlan;
++
++ if (!cb->offload)
++ return;
++
++ rcu_read_lock();
++
++ p = br_port_get_rcu(skb->dev);
++ if (!p)
++ goto out;
++
++ o = &p->offload;
++ if (!o->enabled)
++ goto out;
++
++ if (atomic_read(&p->offload.rht.nelems) >= p->br->offload_cache_size)
++ goto out;
++
++ dev = dev_get_by_index_rcu(dev_net(p->br->dev), cb->input_ifindex);
++ if (!dev)
++ goto out;
++
++ inp = br_port_get_rcu(dev);
++ if (!inp)
++ goto out;
++
++ vg = nbp_vlan_group_rcu(inp);
++ vlan = cb->input_vlan_present ? cb->input_vlan_tag : br_get_pvid(vg);
++ fdb_in = br_fdb_find_rcu(p->br, eth_hdr(skb)->h_source, vlan);
++ if (!fdb_in || !fdb_in->dst)
++ goto out;
++
++ vg = nbp_vlan_group_rcu(p);
++ vlan = skb_vlan_tag_present(skb) ? skb_vlan_tag_get_id(skb) : br_get_pvid(vg);
++ fdb_out = br_fdb_find_rcu(p->br, eth_hdr(skb)->h_dest, vlan);
++ if (!fdb_out || !fdb_out->dst)
++ goto out;
++
++ br_offload_prepare_key(p, &key, skb);
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ key.vlan_present = cb->input_vlan_present;
++ key.vlan_tag = cb->input_vlan_tag;
++#endif
++
++ flow = kmem_cache_alloc(offload_cache, GFP_ATOMIC);
++ flow->port = inp;
++ memcpy(&flow->key, &key, sizeof(key));
++
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ flow->vlan_out_present = skb_vlan_tag_present(skb);
++ flow->vlan_out = skb_vlan_tag_get(skb);
++#endif
++
++ flow->fdb_in = fdb_in;
++ flow->fdb_out = fdb_out;
++ flow->used = jiffies;
++
++ spin_lock_bh(&offload_lock);
++ if (!o->enabled ||
++ atomic_read(&p->offload.rht.nelems) >= p->br->offload_cache_size ||
++ rhashtable_insert_fast(&inp->offload.rht, &flow->node, flow_params)) {
++ kmem_cache_free(offload_cache, flow);
++ goto out_unlock;
++ }
++
++ hlist_add_head(&flow->fdb_list_in, &fdb_in->offload_in);
++ hlist_add_head(&flow->fdb_list_out, &fdb_out->offload_out);
++
++ if (br_offload_need_gc(p))
++ queue_work(system_long_wq, &p->offload.gc_work);
++
++out_unlock:
++ spin_unlock_bh(&offload_lock);
++
++out:
++ rcu_read_unlock();
++}
++
++bool br_offload_input(struct net_bridge_port *p, struct sk_buff *skb)
++{
++ struct net_bridge_port_offload *o = &p->offload;
++ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb;
++ struct bridge_flow_key key;
++ struct net_bridge_port *dst;
++ struct bridge_flow *flow;
++ unsigned long now = jiffies;
++ bool ret = false;
++
++ if (skb->len < sizeof(key))
++ return false;
++
++ if (!o->enabled)
++ return false;
++
++ if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
++ return false;
++
++ br_offload_prepare_key(p, &key, skb);
++
++ rcu_read_lock();
++ flow = rhashtable_lookup(&o->rht, &key, flow_params);
++ if (!flow) {
++ cb->offload = 1;
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ cb->input_vlan_present = key.vlan_present != 0;
++ cb->input_vlan_tag = key.vlan_tag;
++#endif
++ cb->input_ifindex = p->dev->ifindex;
++ goto out;
++ }
++
++ if (flow->fdb_in->dst != p)
++ goto out;
++
++ dst = flow->fdb_out->dst;
++ if (!dst)
++ goto out;
++
++ ret = true;
++#ifdef CONFIG_BRIDGE_VLAN_FILTERING
++ if (!flow->vlan_out_present && key.vlan_present) {
++ __vlan_hwaccel_clear_tag(skb);
++ } else if (flow->vlan_out_present) {
++ if (skb_vlan_tag_present(skb) &&
++ skb->vlan_proto != p->br->vlan_proto) {
++ /* Protocol-mismatch, empty out vlan_tci for new tag */
++ skb_push(skb, ETH_HLEN);
++ skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto,
++ skb_vlan_tag_get(skb));
++ if (unlikely(!skb))
++ goto out;
++
++ skb_pull(skb, ETH_HLEN);
++ skb_reset_mac_len(skb);
++ }
++
++ __vlan_hwaccel_put_tag(skb, p->br->vlan_proto,
++ flow->vlan_out);
++ }
++#endif
++
++ skb->dev = dst->dev;
++ skb_push(skb, ETH_HLEN);
++
++ if (skb_warn_if_lro(skb) || !is_skb_forwardable(skb->dev, skb)) {
++ kfree_skb(skb);
++ goto out;
++ }
++
++ if (now - flow->used >= HZ) {
++ flow->used = now;
++ br_offload_flow_refresh_time(flow);
++ }
++
++ skb_forward_csum(skb);
++ dev_queue_xmit(skb);
++
++out:
++ rcu_read_unlock();
++ return ret;
++}
++
++static void
++br_offload_check_gc(struct net_bridge *br)
++{
++ struct net_bridge_port *p;
++
++ spin_lock_bh(&br->lock);
++ list_for_each_entry(p, &br->port_list, list)
++ if (br_offload_need_gc(p))
++ queue_work(system_long_wq, &p->offload.gc_work);
++ spin_unlock_bh(&br->lock);
++}
++
++
++int br_offload_set_cache_size(struct net_bridge *br, unsigned long val,
++ struct netlink_ext_ack *extack)
++{
++ br->offload_cache_size = val;
++ br_offload_check_gc(br);
++
++ return 0;
++}
++
++int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val,
++ struct netlink_ext_ack *extack)
++{
++ br->offload_cache_reserved = val;
++ br_offload_check_gc(br);
++
++ return 0;
++}
++
++int __init br_offload_init(void)
++{
++ offload_cache = kmem_cache_create("bridge_offload_cache",
++ sizeof(struct bridge_flow),
++ 0, SLAB_HWCACHE_ALIGN, NULL);
++ if (!offload_cache)
++ return -ENOMEM;
++
++ return 0;
++}
++
++void br_offload_fini(void)
++{
++ kmem_cache_destroy(offload_cache);
++}
+--- a/net/bridge/br_private.h
++++ b/net/bridge/br_private.h
+@@ -268,7 +268,13 @@ struct net_bridge_fdb_entry {
+ unsigned long updated ____cacheline_aligned_in_smp;
+ unsigned long used;
+
+- struct rcu_head rcu;
++ union {
++ struct {
++ struct hlist_head offload_in;
++ struct hlist_head offload_out;
++ };
++ struct rcu_head rcu;
++ };
+ };
+
+ #define MDB_PG_FLAGS_PERMANENT BIT(0)
+@@ -343,6 +349,12 @@ struct net_bridge_mdb_entry {
+ struct rcu_head rcu;
+ };
+
++struct net_bridge_port_offload {
++ struct rhashtable rht;
++ struct work_struct gc_work;
++ bool enabled;
++};
++
+ struct net_bridge_port {
+ struct net_bridge *br;
+ struct net_device *dev;
+@@ -403,6 +415,7 @@ struct net_bridge_port {
+ u16 backup_redirected_cnt;
+
+ struct bridge_stp_xstats stp_xstats;
++ struct net_bridge_port_offload offload;
+ };
+
+ #define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj)
+@@ -519,6 +532,9 @@ struct net_bridge {
+ struct kobject *ifobj;
+ u32 auto_cnt;
+
++ u32 offload_cache_size;
++ u32 offload_cache_reserved;
++
+ #ifdef CONFIG_NET_SWITCHDEV
+ /* Counter used to make sure that hardware domains get unique
+ * identifiers in case a bridge spans multiple switchdev instances.
+@@ -553,6 +569,10 @@ struct br_input_skb_cb {
+ #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
+ u8 br_netfilter_broute:1;
+ #endif
++ u8 offload:1;
++ u8 input_vlan_present:1;
++ u16 input_vlan_tag;
++ int input_ifindex;
+
+ #ifdef CONFIG_NET_SWITCHDEV
+ /* Set if TX data plane offloading is used towards at least one
+--- /dev/null
++++ b/net/bridge/br_private_offload.h
+@@ -0,0 +1,23 @@
++#ifndef __BR_OFFLOAD_H
++#define __BR_OFFLOAD_H
++
++bool br_offload_input(struct net_bridge_port *p, struct sk_buff *skb);
++void br_offload_output(struct sk_buff *skb);
++void br_offload_port_state(struct net_bridge_port *p);
++void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb);
++int br_offload_init(void);
++void br_offload_fini(void);
++int br_offload_set_cache_size(struct net_bridge *br, unsigned long val,
++ struct netlink_ext_ack *extack);
++int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val,
++ struct netlink_ext_ack *extack);
++
++static inline void br_offload_skb_disable(struct sk_buff *skb)
++{
++ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb;
++
++ if (cb->offload)
++ cb->offload = 0;
++}
++
++#endif
+--- a/net/bridge/br_stp.c
++++ b/net/bridge/br_stp.c
+@@ -12,6 +12,7 @@
+
+ #include "br_private.h"
+ #include "br_private_stp.h"
++#include "br_private_offload.h"
+
+ /* since time values in bpdu are in jiffies and then scaled (1/256)
+ * before sending, make sure that is at least one STP tick.
+@@ -52,6 +53,8 @@ void br_set_state(struct net_bridge_port
+ (unsigned int) p->port_no, p->dev->name,
+ br_port_state_names[p->state]);
+
++ br_offload_port_state(p);
++
+ if (p->br->stp_enabled == BR_KERNEL_STP) {
+ switch (p->state) {
+ case BR_STATE_BLOCKING:
+--- a/net/bridge/br_sysfs_br.c
++++ b/net/bridge/br_sysfs_br.c
+@@ -18,6 +18,7 @@
+ #include <linux/sched/signal.h>
+
+ #include "br_private.h"
++#include "br_private_offload.h"
+
+ /* IMPORTANT: new bridge options must be added with netlink support only
+ * please do not add new sysfs entries
+@@ -930,6 +931,38 @@ static ssize_t vlan_stats_per_port_store
+ static DEVICE_ATTR_RW(vlan_stats_per_port);
+ #endif
+
++static ssize_t offload_cache_size_show(struct device *d,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct net_bridge *br = to_bridge(d);
++ return sprintf(buf, "%u\n", br->offload_cache_size);
++}
++
++static ssize_t offload_cache_size_store(struct device *d,
++ struct device_attribute *attr,
++ const char *buf, size_t len)
++{
++ return store_bridge_parm(d, buf, len, br_offload_set_cache_size);
++}
++static DEVICE_ATTR_RW(offload_cache_size);
++
++static ssize_t offload_cache_reserved_show(struct device *d,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct net_bridge *br = to_bridge(d);
++ return sprintf(buf, "%u\n", br->offload_cache_reserved);
++}
++
++static ssize_t offload_cache_reserved_store(struct device *d,
++ struct device_attribute *attr,
++ const char *buf, size_t len)
++{
++ return store_bridge_parm(d, buf, len, br_offload_set_cache_reserved);
++}
++static DEVICE_ATTR_RW(offload_cache_reserved);
++
+ static struct attribute *bridge_attrs[] = {
+ &dev_attr_forward_delay.attr,
+ &dev_attr_hello_time.attr,
+@@ -984,6 +1017,8 @@ static struct attribute *bridge_attrs[]
+ &dev_attr_vlan_stats_enabled.attr,
+ &dev_attr_vlan_stats_per_port.attr,
+ #endif
++ &dev_attr_offload_cache_size.attr,
++ &dev_attr_offload_cache_reserved.attr,
+ NULL
+ };
+
+--- a/net/bridge/br_sysfs_if.c
++++ b/net/bridge/br_sysfs_if.c
+@@ -241,6 +241,7 @@ BRPORT_ATTR_FLAG(broadcast_flood, BR_BCA
+ BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
+ BRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
+ BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER);
++BRPORT_ATTR_FLAG(offload, BR_OFFLOAD);
+
+ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
+@@ -295,6 +296,7 @@ static const struct brport_attribute *br
+ &brport_attr_isolated,
+ &brport_attr_bpdu_filter,
+ &brport_attr_backup_port,
++ &brport_attr_offload,
+ NULL
+ };
+
+--- a/net/bridge/br_vlan_tunnel.c
++++ b/net/bridge/br_vlan_tunnel.c
+@@ -15,6 +15,7 @@
+
+ #include "br_private.h"
+ #include "br_private_tunnel.h"
++#include "br_private_offload.h"
+
+ static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg,
+ const void *ptr)
+@@ -180,6 +181,7 @@ void br_handle_ingress_vlan_tunnel(struc
+ skb_dst_drop(skb);
+
+ __vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid);
++ br_offload_skb_disable(skb);
+ }
+
+ int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
+@@ -201,6 +203,7 @@ int br_handle_egress_vlan_tunnel(struct
+ if (err)
+ return err;
+
++ br_offload_skb_disable(skb);
+ tunnel_dst = rcu_dereference(vlan->tinfo.tunnel_dst);
+ if (tunnel_dst && dst_hold_safe(&tunnel_dst->dst))
+ skb_dst_set(skb, &tunnel_dst->dst);
diff --git a/target/linux/generic/hack-6.1/601-of_net-add-mac-address-ascii-support.patch b/target/linux/generic/hack-6.1/601-of_net-add-mac-address-ascii-support.patch
new file mode 100644
index 0000000000..55866c3135
--- /dev/null
+++ b/target/linux/generic/hack-6.1/601-of_net-add-mac-address-ascii-support.patch
@@ -0,0 +1,116 @@
+From: Yousong Zhou <yszhou4tech@gmail.com>
+Subject: [PATCH] of: net: add nvmem cell mac-address-ascii support
+
+This is needed for devices with mac address stored in ascii format,
+e.g. HiWiFi HC6361 to be ported in the following patch.
+
+Submitted-by: Yousong Zhou <yszhou4tech@gmail.com>
+---
+ net/core/of_net.c | 83 ++++++++++++------
+ 1 files changed, 72 insertions(+), 11 deletions(-)
+
+--- a/net/core/of_net.c
++++ b/net/core/of_net.c
+@@ -57,13 +57,70 @@ static int of_get_mac_addr(struct device
+ return -ENODEV;
+ }
+
++static void *nvmem_cell_get_mac_address(struct nvmem_cell *cell)
++{
++ size_t len;
++ void *mac;
++
++ mac = nvmem_cell_read(cell, &len);
++ if (IS_ERR(mac))
++ return mac;
++ if (len != ETH_ALEN) {
++ kfree(mac);
++ return ERR_PTR(-EINVAL);
++ }
++ return mac;
++}
++
++static void *nvmem_cell_get_mac_address_ascii(struct nvmem_cell *cell)
++{
++ size_t len;
++ int ret;
++ void *mac_ascii;
++ u8 *mac;
++
++ mac_ascii = nvmem_cell_read(cell, &len);
++ if (IS_ERR(mac_ascii))
++ return mac_ascii;
++ if (len != ETH_ALEN*2+5) {
++ kfree(mac_ascii);
++ return ERR_PTR(-EINVAL);
++ }
++ mac = kmalloc(ETH_ALEN, GFP_KERNEL);
++ if (!mac) {
++ kfree(mac_ascii);
++ return ERR_PTR(-ENOMEM);
++ }
++ ret = sscanf(mac_ascii, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
++ &mac[0], &mac[1], &mac[2],
++ &mac[3], &mac[4], &mac[5]);
++ kfree(mac_ascii);
++ if (ret == ETH_ALEN)
++ return mac;
++ kfree(mac);
++ return ERR_PTR(-EINVAL);
++}
++
++static struct nvmem_cell_mac_address_property {
++ char *name;
++ void *(*read)(struct nvmem_cell *);
++} nvmem_cell_mac_address_properties[] = {
++ {
++ .name = "mac-address",
++ .read = nvmem_cell_get_mac_address,
++ }, {
++ .name = "mac-address-ascii",
++ .read = nvmem_cell_get_mac_address_ascii,
++ },
++};
++
+ static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr)
+ {
+ struct platform_device *pdev = of_find_device_by_node(np);
++ struct nvmem_cell_mac_address_property *property;
+ struct nvmem_cell *cell;
+ const void *mac;
+- size_t len;
+- int ret;
++ int ret, i;
+
+ /* Try lookup by device first, there might be a nvmem_cell_lookup
+ * associated with a given device.
+@@ -74,17 +131,26 @@ static int of_get_mac_addr_nvmem(struct
+ return ret;
+ }
+
+- cell = of_nvmem_cell_get(np, "mac-address");
++ for (i = 0; i < ARRAY_SIZE(nvmem_cell_mac_address_properties); i++) {
++ property = &nvmem_cell_mac_address_properties[i];
++ cell = of_nvmem_cell_get(np, property->name);
++ /* For -EPROBE_DEFER don't try other properties.
++ * We'll get back to this one.
++ */
++ if (!IS_ERR(cell) || PTR_ERR(cell) == -EPROBE_DEFER)
++ break;
++ }
++
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+- mac = nvmem_cell_read(cell, &len);
++ mac = property->read(cell);
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(mac))
+ return PTR_ERR(mac);
+
+- if (len != ETH_ALEN || !is_valid_ether_addr(mac)) {
++ if (!is_valid_ether_addr(mac)) {
+ kfree(mac);
+ return -EINVAL;
+ }
diff --git a/target/linux/generic/hack-6.1/645-netfilter-connmark-introduce-set-dscpmark.patch b/target/linux/generic/hack-6.1/645-netfilter-connmark-introduce-set-dscpmark.patch
new file mode 100644
index 0000000000..c368c4ae3b
--- /dev/null
+++ b/target/linux/generic/hack-6.1/645-netfilter-connmark-introduce-set-dscpmark.patch
@@ -0,0 +1,214 @@
+From eda40b8c8c82e0f2789d6bc8bf63846dce2e8f32 Mon Sep 17 00:00:00 2001
+From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+Date: Sat, 23 Mar 2019 09:29:49 +0000
+Subject: [PATCH] netfilter: connmark: introduce set-dscpmark
+
+set-dscpmark is a method of storing the DSCP of an ip packet into
+conntrack mark. In combination with a suitable tc filter action
+(act_ctinfo) DSCP values are able to be stored in the mark on egress and
+restored on ingress across links that otherwise alter or bleach DSCP.
+
+This is useful for qdiscs such as CAKE which are able to shape according
+to policies based on DSCP.
+
+Ingress classification is traditionally a challenging task since
+iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT
+lookups, hence are unable to see internal IPv4 addresses as used on the
+typical home masquerading gateway.
+
+x_tables CONNMARK set-dscpmark target solves the problem of storing the
+DSCP to the conntrack mark in a way suitable for the new act_ctinfo tc
+action to restore.
+
+The set-dscpmark option accepts 2 parameters, a 32bit 'dscpmask' and a
+32bit 'statemask'. The dscp mask must be 6 contiguous bits and
+represents the area where the DSCP will be stored in the connmark. The
+state mask is a minimum 1 bit length mask that must not overlap with the
+dscpmask. It represents a flag which is set when the DSCP has been
+stored in the conntrack mark. This is useful to implement a 'one shot'
+iptables based classification where the 'complicated' iptables rules are
+only run once to classify the connection on initial (egress) packet and
+subsequent packets are all marked/restored with the same DSCP. A state
+mask of zero disables the setting of a status bit/s.
+
+example syntax with a suitably modified iptables user space application:
+
+iptables -A QOS_MARK_eth0 -t mangle -j CONNMARK --set-dscpmark 0xfc000000/0x01000000
+
+Would store the DSCP in the top 6 bits of the 32bit mark field, and use
+the LSB of the top byte as the 'DSCP has been stored' marker.
+
+|----0xFC----conntrack mark----000000---|
+| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0|
+| DSCP | unused | flag |unused |
+|-----------------------0x01---000000---|
+ ^ ^
+ | |
+ ---| Conditional flag
+ | set this when dscp
+|-ip diffserv-| stored in mark
+| 6 bits |
+|-------------|
+
+an identically configured tc action to restore looks like:
+
+tc filter show dev eth0 ingress
+filter parent ffff: protocol all pref 10 u32 chain 0
+filter parent ffff: protocol all pref 10 u32 chain 0 fh 800: ht divisor 1
+filter parent ffff: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1: not_in_hw
+ match 00000000/00000000 at 0
+ action order 1: ctinfo zone 0 pipe
+ index 2 ref 1 bind 1 dscp 0xfc000000/0x1000000
+
+ action order 2: mirred (Egress Redirect to device ifb4eth0) stolen
+ index 1 ref 1 bind 1
+
+|----0xFC----conntrack mark----000000---|
+| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0|
+| DSCP | unused | flag |unused |
+|-----------------------0x01---000000---|
+ | |
+ | |
+ ---| Conditional flag
+ v only restore if set
+|-ip diffserv-|
+| 6 bits |
+|-------------|
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ include/uapi/linux/netfilter/xt_connmark.h | 10 ++++
+ net/netfilter/xt_connmark.c | 55 ++++++++++++++++++----
+ 2 files changed, 57 insertions(+), 8 deletions(-)
+
+--- a/include/uapi/linux/netfilter/xt_connmark.h
++++ b/include/uapi/linux/netfilter/xt_connmark.h
+@@ -20,6 +20,11 @@ enum {
+ };
+
+ enum {
++ XT_CONNMARK_VALUE = (1 << 0),
++ XT_CONNMARK_DSCP = (1 << 1)
++};
++
++enum {
+ D_SHIFT_LEFT = 0,
+ D_SHIFT_RIGHT,
+ };
+@@ -34,6 +39,11 @@ struct xt_connmark_tginfo2 {
+ __u8 shift_dir, shift_bits, mode;
+ };
+
++struct xt_connmark_tginfo3 {
++ __u32 ctmark, ctmask, nfmask;
++ __u8 shift_dir, shift_bits, mode, func;
++};
++
+ struct xt_connmark_mtinfo1 {
+ __u32 mark, mask;
+ __u8 invert;
+--- a/net/netfilter/xt_connmark.c
++++ b/net/netfilter/xt_connmark.c
+@@ -24,13 +24,13 @@ MODULE_ALIAS("ipt_connmark");
+ MODULE_ALIAS("ip6t_connmark");
+
+ static unsigned int
+-connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
++connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo3 *info)
+ {
+ enum ip_conntrack_info ctinfo;
+ u_int32_t new_targetmark;
+ struct nf_conn *ct;
+ u_int32_t newmark;
+- u_int32_t oldmark;
++ u_int8_t dscp;
+
+ ct = nf_ct_get(skb, &ctinfo);
+ if (ct == NULL)
+@@ -38,13 +38,24 @@ connmark_tg_shift(struct sk_buff *skb, c
+
+ switch (info->mode) {
+ case XT_CONNMARK_SET:
+- oldmark = READ_ONCE(ct->mark);
+- newmark = (oldmark & ~info->ctmask) ^ info->ctmark;
+- if (info->shift_dir == D_SHIFT_RIGHT)
+- newmark >>= info->shift_bits;
+- else
+- newmark <<= info->shift_bits;
++ newmark = READ_ONCE(ct->mark);
++ if (info->func & XT_CONNMARK_VALUE) {
++ newmark = (newmark & ~info->ctmask) ^ info->ctmark;
++ if (info->shift_dir == D_SHIFT_RIGHT)
++ newmark >>= info->shift_bits;
++ else
++ newmark <<= info->shift_bits;
++ } else if (info->func & XT_CONNMARK_DSCP) {
++ if (skb->protocol == htons(ETH_P_IP))
++ dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2;
++ else if (skb->protocol == htons(ETH_P_IPV6))
++ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2;
++ else /* protocol doesn't have diffserv */
++ break;
+
++ newmark = (newmark & ~info->ctmark) |
++ (info->ctmask | (dscp << info->shift_bits));
++ }
+ if (READ_ONCE(ct->mark) != newmark) {
+ WRITE_ONCE(ct->mark, newmark);
+ nf_conntrack_event_cache(IPCT_MARK, ct);
+@@ -83,20 +94,36 @@ static unsigned int
+ connmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
+ {
+ const struct xt_connmark_tginfo1 *info = par->targinfo;
+- const struct xt_connmark_tginfo2 info2 = {
++ const struct xt_connmark_tginfo3 info3 = {
+ .ctmark = info->ctmark,
+ .ctmask = info->ctmask,
+ .nfmask = info->nfmask,
+ .mode = info->mode,
++ .func = XT_CONNMARK_VALUE
+ };
+
+- return connmark_tg_shift(skb, &info2);
++ return connmark_tg_shift(skb, &info3);
+ }
+
+ static unsigned int
+ connmark_tg_v2(struct sk_buff *skb, const struct xt_action_param *par)
+ {
+ const struct xt_connmark_tginfo2 *info = par->targinfo;
++ const struct xt_connmark_tginfo3 info3 = {
++ .ctmark = info->ctmark,
++ .ctmask = info->ctmask,
++ .nfmask = info->nfmask,
++ .mode = info->mode,
++ .func = XT_CONNMARK_VALUE
++ };
++
++ return connmark_tg_shift(skb, &info3);
++}
++
++static unsigned int
++connmark_tg_v3(struct sk_buff *skb, const struct xt_action_param *par)
++{
++ const struct xt_connmark_tginfo3 *info = par->targinfo;
+
+ return connmark_tg_shift(skb, info);
+ }
+@@ -167,6 +194,16 @@ static struct xt_target connmark_tg_reg[
+ .targetsize = sizeof(struct xt_connmark_tginfo2),
+ .destroy = connmark_tg_destroy,
+ .me = THIS_MODULE,
++ },
++ {
++ .name = "CONNMARK",
++ .revision = 3,
++ .family = NFPROTO_UNSPEC,
++ .checkentry = connmark_tg_check,
++ .target = connmark_tg_v3,
++ .targetsize = sizeof(struct xt_connmark_tginfo3),
++ .destroy = connmark_tg_destroy,
++ .me = THIS_MODULE,
+ }
+ };
+
diff --git a/target/linux/generic/hack-6.1/650-netfilter-add-xt_FLOWOFFLOAD-target.patch b/target/linux/generic/hack-6.1/650-netfilter-add-xt_FLOWOFFLOAD-target.patch
new file mode 100644
index 0000000000..aa6c668558
--- /dev/null
+++ b/target/linux/generic/hack-6.1/650-netfilter-add-xt_FLOWOFFLOAD-target.patch
@@ -0,0 +1,861 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 20 Feb 2018 15:56:02 +0100
+Subject: [PATCH] netfilter: add xt_FLOWOFFLOAD target
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ create mode 100644 net/netfilter/xt_OFFLOAD.c
+
+--- a/net/ipv4/netfilter/Kconfig
++++ b/net/ipv4/netfilter/Kconfig
+@@ -56,8 +56,6 @@ config NF_TABLES_ARP
+ help
+ This option enables the ARP support for nf_tables.
+
+-endif # NF_TABLES
+-
+ config NF_FLOW_TABLE_IPV4
+ tristate "Netfilter flow table IPv4 module"
+ depends on NF_FLOW_TABLE
+@@ -66,6 +64,8 @@ config NF_FLOW_TABLE_IPV4
+
+ To compile it as a module, choose M here.
+
++endif # NF_TABLES
++
+ config NF_DUP_IPV4
+ tristate "Netfilter IPv4 packet duplication to alternate destination"
+ depends on !NF_CONNTRACK || NF_CONNTRACK
+--- a/net/ipv6/netfilter/Kconfig
++++ b/net/ipv6/netfilter/Kconfig
+@@ -45,7 +45,6 @@ config NFT_FIB_IPV6
+ multicast or blackhole.
+
+ endif # NF_TABLES_IPV6
+-endif # NF_TABLES
+
+ config NF_FLOW_TABLE_IPV6
+ tristate "Netfilter flow table IPv6 module"
+@@ -55,6 +54,8 @@ config NF_FLOW_TABLE_IPV6
+
+ To compile it as a module, choose M here.
+
++endif # NF_TABLES
++
+ config NF_DUP_IPV6
+ tristate "Netfilter IPv6 packet duplication to alternate destination"
+ depends on !NF_CONNTRACK || NF_CONNTRACK
+--- a/net/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -707,8 +707,6 @@ config NFT_REJECT_NETDEV
+
+ endif # NF_TABLES_NETDEV
+
+-endif # NF_TABLES
+-
+ config NF_FLOW_TABLE_INET
+ tristate "Netfilter flow table mixed IPv4/IPv6 module"
+ depends on NF_FLOW_TABLE
+@@ -717,11 +715,12 @@ config NF_FLOW_TABLE_INET
+
+ To compile it as a module, choose M here.
+
++endif # NF_TABLES
++
+ config NF_FLOW_TABLE
+ tristate "Netfilter flow table module"
+ depends on NETFILTER_INGRESS
+ depends on NF_CONNTRACK
+- depends on NF_TABLES
+ help
+ This option adds the flow table core infrastructure.
+
+@@ -1010,6 +1009,15 @@ config NETFILTER_XT_TARGET_NOTRACK
+ depends on NETFILTER_ADVANCED
+ select NETFILTER_XT_TARGET_CT
+
++config NETFILTER_XT_TARGET_FLOWOFFLOAD
++ tristate '"FLOWOFFLOAD" target support'
++ depends on NF_FLOW_TABLE
++ depends on NETFILTER_INGRESS
++ help
++ This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload
++ module to speed up processing of packets by bypassing the usual
++ netfilter chains
++
+ config NETFILTER_XT_TARGET_RATEEST
+ tristate '"RATEEST" target support'
+ depends on NETFILTER_ADVANCED
+--- a/net/netfilter/Makefile
++++ b/net/netfilter/Makefile
+@@ -143,6 +143,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF
+ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
++obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
+ obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
+--- /dev/null
++++ b/net/netfilter/xt_FLOWOFFLOAD.c
+@@ -0,0 +1,698 @@
++/*
++ * Copyright (C) 2018-2021 Felix Fietkau <nbd@nbd.name>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/netfilter.h>
++#include <linux/netfilter/xt_FLOWOFFLOAD.h>
++#include <linux/if_vlan.h>
++#include <net/ip.h>
++#include <net/netfilter/nf_conntrack.h>
++#include <net/netfilter/nf_conntrack_extend.h>
++#include <net/netfilter/nf_conntrack_helper.h>
++#include <net/netfilter/nf_flow_table.h>
++
++struct xt_flowoffload_hook {
++ struct hlist_node list;
++ struct nf_hook_ops ops;
++ struct net *net;
++ bool registered;
++ bool used;
++};
++
++struct xt_flowoffload_table {
++ struct nf_flowtable ft;
++ struct hlist_head hooks;
++ struct delayed_work work;
++};
++
++struct nf_forward_info {
++ const struct net_device *indev;
++ const struct net_device *outdev;
++ const struct net_device *hw_outdev;
++ struct id {
++ __u16 id;
++ __be16 proto;
++ } encap[NF_FLOW_TABLE_ENCAP_MAX];
++ u8 num_encaps;
++ u8 ingress_vlans;
++ u8 h_source[ETH_ALEN];
++ u8 h_dest[ETH_ALEN];
++ enum flow_offload_xmit_type xmit_type;
++};
++
++static DEFINE_SPINLOCK(hooks_lock);
++
++struct xt_flowoffload_table flowtable[2];
++
++static unsigned int
++xt_flowoffload_net_hook(void *priv, struct sk_buff *skb,
++ const struct nf_hook_state *state)
++{
++ struct vlan_ethhdr *veth;
++ __be16 proto;
++
++ switch (skb->protocol) {
++ case htons(ETH_P_8021Q):
++ veth = (struct vlan_ethhdr *)skb_mac_header(skb);
++ proto = veth->h_vlan_encapsulated_proto;
++ break;
++ case htons(ETH_P_PPP_SES):
++ proto = nf_flow_pppoe_proto(skb);
++ break;
++ default:
++ proto = skb->protocol;
++ break;
++ }
++
++ switch (proto) {
++ case htons(ETH_P_IP):
++ return nf_flow_offload_ip_hook(priv, skb, state);
++ case htons(ETH_P_IPV6):
++ return nf_flow_offload_ipv6_hook(priv, skb, state);
++ }
++
++ return NF_ACCEPT;
++}
++
++static int
++xt_flowoffload_create_hook(struct xt_flowoffload_table *table,
++ struct net_device *dev)
++{
++ struct xt_flowoffload_hook *hook;
++ struct nf_hook_ops *ops;
++
++ hook = kzalloc(sizeof(*hook), GFP_ATOMIC);
++ if (!hook)
++ return -ENOMEM;
++
++ ops = &hook->ops;
++ ops->pf = NFPROTO_NETDEV;
++ ops->hooknum = NF_NETDEV_INGRESS;
++ ops->priority = 10;
++ ops->priv = &table->ft;
++ ops->hook = xt_flowoffload_net_hook;
++ ops->dev = dev;
++
++ hlist_add_head(&hook->list, &table->hooks);
++ mod_delayed_work(system_power_efficient_wq, &table->work, 0);
++
++ return 0;
++}
++
++static struct xt_flowoffload_hook *
++flow_offload_lookup_hook(struct xt_flowoffload_table *table,
++ struct net_device *dev)
++{
++ struct xt_flowoffload_hook *hook;
++
++ hlist_for_each_entry(hook, &table->hooks, list) {
++ if (hook->ops.dev == dev)
++ return hook;
++ }
++
++ return NULL;
++}
++
++static void
++xt_flowoffload_check_device(struct xt_flowoffload_table *table,
++ struct net_device *dev)
++{
++ struct xt_flowoffload_hook *hook;
++
++ if (!dev)
++ return;
++
++ spin_lock_bh(&hooks_lock);
++ hook = flow_offload_lookup_hook(table, dev);
++ if (hook)
++ hook->used = true;
++ else
++ xt_flowoffload_create_hook(table, dev);
++ spin_unlock_bh(&hooks_lock);
++}
++
++static void
++xt_flowoffload_register_hooks(struct xt_flowoffload_table *table)
++{
++ struct xt_flowoffload_hook *hook;
++
++restart:
++ hlist_for_each_entry(hook, &table->hooks, list) {
++ if (hook->registered)
++ continue;
++
++ hook->registered = true;
++ hook->net = dev_net(hook->ops.dev);
++ spin_unlock_bh(&hooks_lock);
++ nf_register_net_hook(hook->net, &hook->ops);
++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
++ table->ft.type->setup(&table->ft, hook->ops.dev,
++ FLOW_BLOCK_BIND);
++ spin_lock_bh(&hooks_lock);
++ goto restart;
++ }
++
++}
++
++static bool
++xt_flowoffload_cleanup_hooks(struct xt_flowoffload_table *table)
++{
++ struct xt_flowoffload_hook *hook;
++ bool active = false;
++
++restart:
++ spin_lock_bh(&hooks_lock);
++ hlist_for_each_entry(hook, &table->hooks, list) {
++ if (hook->used || !hook->registered) {
++ active = true;
++ continue;
++ }
++
++ hlist_del(&hook->list);
++ spin_unlock_bh(&hooks_lock);
++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
++ table->ft.type->setup(&table->ft, hook->ops.dev,
++ FLOW_BLOCK_UNBIND);
++ nf_unregister_net_hook(hook->net, &hook->ops);
++ kfree(hook);
++ goto restart;
++ }
++ spin_unlock_bh(&hooks_lock);
++
++ return active;
++}
++
++static void
++xt_flowoffload_check_hook(struct nf_flowtable *flowtable,
++ struct flow_offload *flow, void *data)
++{
++ struct xt_flowoffload_table *table;
++ struct flow_offload_tuple *tuple0 = &flow->tuplehash[0].tuple;
++ struct flow_offload_tuple *tuple1 = &flow->tuplehash[1].tuple;
++ struct xt_flowoffload_hook *hook;
++
++ table = container_of(flowtable, struct xt_flowoffload_table, ft);
++
++ spin_lock_bh(&hooks_lock);
++ hlist_for_each_entry(hook, &table->hooks, list) {
++ if (hook->ops.dev->ifindex != tuple0->iifidx &&
++ hook->ops.dev->ifindex != tuple1->iifidx)
++ continue;
++
++ hook->used = true;
++ }
++ spin_unlock_bh(&hooks_lock);
++}
++
++static void
++xt_flowoffload_hook_work(struct work_struct *work)
++{
++ struct xt_flowoffload_table *table;
++ struct xt_flowoffload_hook *hook;
++ int err;
++
++ table = container_of(work, struct xt_flowoffload_table, work.work);
++
++ spin_lock_bh(&hooks_lock);
++ xt_flowoffload_register_hooks(table);
++ hlist_for_each_entry(hook, &table->hooks, list)
++ hook->used = false;
++ spin_unlock_bh(&hooks_lock);
++
++ err = nf_flow_table_iterate(&table->ft, xt_flowoffload_check_hook,
++ NULL);
++ if (err && err != -EAGAIN)
++ goto out;
++
++ if (!xt_flowoffload_cleanup_hooks(table))
++ return;
++
++out:
++ queue_delayed_work(system_power_efficient_wq, &table->work, HZ);
++}
++
++static bool
++xt_flowoffload_skip(struct sk_buff *skb, int family)
++{
++ if (skb_sec_path(skb))
++ return true;
++
++ if (family == NFPROTO_IPV4) {
++ const struct ip_options *opt = &(IPCB(skb)->opt);
++
++ if (unlikely(opt->optlen))
++ return true;
++ }
++
++ return false;
++}
++
++static enum flow_offload_xmit_type nf_xmit_type(struct dst_entry *dst)
++{
++ if (dst_xfrm(dst))
++ return FLOW_OFFLOAD_XMIT_XFRM;
++
++ return FLOW_OFFLOAD_XMIT_NEIGH;
++}
++
++static void nf_default_forward_path(struct nf_flow_route *route,
++ struct dst_entry *dst_cache,
++ enum ip_conntrack_dir dir,
++ struct net_device **dev)
++{
++ dev[!dir] = dst_cache->dev;
++ route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex;
++ route->tuple[dir].dst = dst_cache;
++ route->tuple[dir].xmit_type = nf_xmit_type(dst_cache);
++}
++
++static bool nf_is_valid_ether_device(const struct net_device *dev)
++{
++ if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER ||
++ dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr))
++ return false;
++
++ return true;
++}
++
++static void nf_dev_path_info(const struct net_device_path_stack *stack,
++ struct nf_forward_info *info,
++ unsigned char *ha)
++{
++ const struct net_device_path *path;
++ int i;
++
++ memcpy(info->h_dest, ha, ETH_ALEN);
++
++ for (i = 0; i < stack->num_paths; i++) {
++ path = &stack->path[i];
++ switch (path->type) {
++ case DEV_PATH_ETHERNET:
++ case DEV_PATH_DSA:
++ case DEV_PATH_VLAN:
++ case DEV_PATH_PPPOE:
++ info->indev = path->dev;
++ if (is_zero_ether_addr(info->h_source))
++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
++
++ if (path->type == DEV_PATH_ETHERNET)
++ break;
++ if (path->type == DEV_PATH_DSA) {
++ i = stack->num_paths;
++ break;
++ }
++
++ /* DEV_PATH_VLAN and DEV_PATH_PPPOE */
++ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
++ info->indev = NULL;
++ break;
++ }
++ if (!info->outdev)
++ info->outdev = path->dev;
++ info->encap[info->num_encaps].id = path->encap.id;
++ info->encap[info->num_encaps].proto = path->encap.proto;
++ info->num_encaps++;
++ if (path->type == DEV_PATH_PPPOE)
++ memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN);
++ break;
++ case DEV_PATH_BRIDGE:
++ if (is_zero_ether_addr(info->h_source))
++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
++
++ switch (path->bridge.vlan_mode) {
++ case DEV_PATH_BR_VLAN_UNTAG_HW:
++ info->ingress_vlans |= BIT(info->num_encaps - 1);
++ break;
++ case DEV_PATH_BR_VLAN_TAG:
++ info->encap[info->num_encaps].id = path->bridge.vlan_id;
++ info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
++ info->num_encaps++;
++ break;
++ case DEV_PATH_BR_VLAN_UNTAG:
++ info->num_encaps--;
++ break;
++ case DEV_PATH_BR_VLAN_KEEP:
++ break;
++ }
++ break;
++ default:
++ info->indev = NULL;
++ break;
++ }
++ }
++ if (!info->outdev)
++ info->outdev = info->indev;
++
++ info->hw_outdev = info->indev;
++
++ if (nf_is_valid_ether_device(info->indev))
++ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
++}
++
++static int nf_dev_fill_forward_path(const struct nf_flow_route *route,
++ const struct dst_entry *dst_cache,
++ const struct nf_conn *ct,
++ enum ip_conntrack_dir dir, u8 *ha,
++ struct net_device_path_stack *stack)
++{
++ const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
++ struct net_device *dev = dst_cache->dev;
++ struct neighbour *n;
++ u8 nud_state;
++
++ if (!nf_is_valid_ether_device(dev))
++ goto out;
++
++ n = dst_neigh_lookup(dst_cache, daddr);
++ if (!n)
++ return -1;
++
++ read_lock_bh(&n->lock);
++ nud_state = n->nud_state;
++ ether_addr_copy(ha, n->ha);
++ read_unlock_bh(&n->lock);
++ neigh_release(n);
++
++ if (!(nud_state & NUD_VALID))
++ return -1;
++
++out:
++ return dev_fill_forward_path(dev, ha, stack);
++}
++
++static void nf_dev_forward_path(struct nf_flow_route *route,
++ const struct nf_conn *ct,
++ enum ip_conntrack_dir dir,
++ struct net_device **devs)
++{
++ const struct dst_entry *dst = route->tuple[dir].dst;
++ struct net_device_path_stack stack;
++ struct nf_forward_info info = {};
++ unsigned char ha[ETH_ALEN];
++ int i;
++
++ if (nf_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
++ nf_dev_path_info(&stack, &info, ha);
++
++ devs[!dir] = (struct net_device *)info.indev;
++ if (!info.indev)
++ return;
++
++ route->tuple[!dir].in.ifindex = info.indev->ifindex;
++ for (i = 0; i < info.num_encaps; i++) {
++ route->tuple[!dir].in.encap[i].id = info.encap[i].id;
++ route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
++ }
++ route->tuple[!dir].in.num_encaps = info.num_encaps;
++ route->tuple[!dir].in.ingress_vlans = info.ingress_vlans;
++
++ if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
++ memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
++ memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN);
++ route->tuple[dir].out.ifindex = info.outdev->ifindex;
++ route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex;
++ route->tuple[dir].xmit_type = info.xmit_type;
++ }
++}
++
++static int
++xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct,
++ const struct xt_action_param *par,
++ struct nf_flow_route *route, enum ip_conntrack_dir dir,
++ struct net_device **devs)
++{
++ struct dst_entry *this_dst = skb_dst(skb);
++ struct dst_entry *other_dst = NULL;
++ struct flowi fl;
++
++ memset(&fl, 0, sizeof(fl));
++ switch (xt_family(par)) {
++ case NFPROTO_IPV4:
++ fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip;
++ fl.u.ip4.flowi4_oif = xt_in(par)->ifindex;
++ break;
++ case NFPROTO_IPV6:
++ fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.dst.u3.in6;
++ fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6;
++ fl.u.ip6.flowi6_oif = xt_in(par)->ifindex;
++ break;
++ }
++
++ nf_route(xt_net(par), &other_dst, &fl, false, xt_family(par));
++ if (!other_dst)
++ return -ENOENT;
++
++ nf_default_forward_path(route, this_dst, dir, devs);
++ nf_default_forward_path(route, other_dst, !dir, devs);
++
++ if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH &&
++ route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) {
++ nf_dev_forward_path(route, ct, dir, devs);
++ nf_dev_forward_path(route, ct, !dir, devs);
++ }
++
++ return 0;
++}
++
++static unsigned int
++flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
++{
++ struct xt_flowoffload_table *table;
++ const struct xt_flowoffload_target_info *info = par->targinfo;
++ struct tcphdr _tcph, *tcph = NULL;
++ enum ip_conntrack_info ctinfo;
++ enum ip_conntrack_dir dir;
++ struct nf_flow_route route = {};
++ struct flow_offload *flow = NULL;
++ struct net_device *devs[2] = {};
++ struct nf_conn *ct;
++ struct net *net;
++
++ if (xt_flowoffload_skip(skb, xt_family(par)))
++ return XT_CONTINUE;
++
++ ct = nf_ct_get(skb, &ctinfo);
++ if (ct == NULL)
++ return XT_CONTINUE;
++
++ switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
++ case IPPROTO_TCP:
++ if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
++ return XT_CONTINUE;
++
++ tcph = skb_header_pointer(skb, par->thoff,
++ sizeof(_tcph), &_tcph);
++ if (unlikely(!tcph || tcph->fin || tcph->rst))
++ return XT_CONTINUE;
++ break;
++ case IPPROTO_UDP:
++ break;
++ default:
++ return XT_CONTINUE;
++ }
++
++ if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) ||
++ ct->status & (IPS_SEQ_ADJUST | IPS_NAT_CLASH))
++ return XT_CONTINUE;
++
++ if (!nf_ct_is_confirmed(ct))
++ return XT_CONTINUE;
++
++ devs[dir] = xt_out(par);
++ devs[!dir] = xt_in(par);
++
++ if (!devs[dir] || !devs[!dir])
++ return XT_CONTINUE;
++
++ if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
++ return XT_CONTINUE;
++
++ dir = CTINFO2DIR(ctinfo);
++
++ if (xt_flowoffload_route(skb, ct, par, &route, dir, devs) < 0)
++ goto err_flow_route;
++
++ flow = flow_offload_alloc(ct);
++ if (!flow)
++ goto err_flow_alloc;
++
++ if (flow_offload_route_init(flow, &route) < 0)
++ goto err_flow_add;
++
++ if (tcph) {
++ ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
++ ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
++ }
++
++ table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)];
++
++ net = read_pnet(&table->ft.net);
++ if (!net)
++ write_pnet(&table->ft.net, xt_net(par));
++
++ if (flow_offload_add(&table->ft, flow) < 0)
++ goto err_flow_add;
++
++ xt_flowoffload_check_device(table, devs[0]);
++ xt_flowoffload_check_device(table, devs[1]);
++
++ dst_release(route.tuple[!dir].dst);
++
++ return XT_CONTINUE;
++
++err_flow_add:
++ flow_offload_free(flow);
++err_flow_alloc:
++ dst_release(route.tuple[!dir].dst);
++err_flow_route:
++ clear_bit(IPS_OFFLOAD_BIT, &ct->status);
++
++ return XT_CONTINUE;
++}
++
++static int flowoffload_chk(const struct xt_tgchk_param *par)
++{
++ struct xt_flowoffload_target_info *info = par->targinfo;
++
++ if (info->flags & ~XT_FLOWOFFLOAD_MASK)
++ return -EINVAL;
++
++ return 0;
++}
++
++static struct xt_target offload_tg_reg __read_mostly = {
++ .family = NFPROTO_UNSPEC,
++ .name = "FLOWOFFLOAD",
++ .revision = 0,
++ .targetsize = sizeof(struct xt_flowoffload_target_info),
++ .usersize = sizeof(struct xt_flowoffload_target_info),
++ .checkentry = flowoffload_chk,
++ .target = flowoffload_tg,
++ .me = THIS_MODULE,
++};
++
++static int flow_offload_netdev_event(struct notifier_block *this,
++ unsigned long event, void *ptr)
++{
++ struct xt_flowoffload_hook *hook0, *hook1;
++ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
++
++ if (event != NETDEV_UNREGISTER)
++ return NOTIFY_DONE;
++
++ spin_lock_bh(&hooks_lock);
++ hook0 = flow_offload_lookup_hook(&flowtable[0], dev);
++ if (hook0)
++ hlist_del(&hook0->list);
++
++ hook1 = flow_offload_lookup_hook(&flowtable[1], dev);
++ if (hook1)
++ hlist_del(&hook1->list);
++ spin_unlock_bh(&hooks_lock);
++
++ if (hook0) {
++ nf_unregister_net_hook(hook0->net, &hook0->ops);
++ kfree(hook0);
++ }
++
++ if (hook1) {
++ nf_unregister_net_hook(hook1->net, &hook1->ops);
++ kfree(hook1);
++ }
++
++ nf_flow_table_cleanup(dev);
++
++ return NOTIFY_DONE;
++}
++
++static struct notifier_block flow_offload_netdev_notifier = {
++ .notifier_call = flow_offload_netdev_event,
++};
++
++static int nf_flow_rule_route_inet(struct net *net,
++ const struct flow_offload *flow,
++ enum flow_offload_tuple_dir dir,
++ struct nf_flow_rule *flow_rule)
++{
++ const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
++ int err;
++
++ switch (flow_tuple->l3proto) {
++ case NFPROTO_IPV4:
++ err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule);
++ break;
++ case NFPROTO_IPV6:
++ err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule);
++ break;
++ default:
++ err = -1;
++ break;
++ }
++
++ return err;
++}
++
++static struct nf_flowtable_type flowtable_inet = {
++ .family = NFPROTO_INET,
++ .init = nf_flow_table_init,
++ .setup = nf_flow_table_offload_setup,
++ .action = nf_flow_rule_route_inet,
++ .free = nf_flow_table_free,
++ .hook = xt_flowoffload_net_hook,
++ .owner = THIS_MODULE,
++};
++
++static int init_flowtable(struct xt_flowoffload_table *tbl)
++{
++ INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work);
++ tbl->ft.type = &flowtable_inet;
++ tbl->ft.flags = NF_FLOWTABLE_COUNTER;
++
++ return nf_flow_table_init(&tbl->ft);
++}
++
++static int __init xt_flowoffload_tg_init(void)
++{
++ int ret;
++
++ register_netdevice_notifier(&flow_offload_netdev_notifier);
++
++ ret = init_flowtable(&flowtable[0]);
++ if (ret)
++ return ret;
++
++ ret = init_flowtable(&flowtable[1]);
++ if (ret)
++ goto cleanup;
++
++ flowtable[1].ft.flags |= NF_FLOWTABLE_HW_OFFLOAD;
++
++ ret = xt_register_target(&offload_tg_reg);
++ if (ret)
++ goto cleanup2;
++
++ return 0;
++
++cleanup2:
++ nf_flow_table_free(&flowtable[1].ft);
++cleanup:
++ nf_flow_table_free(&flowtable[0].ft);
++ return ret;
++}
++
++static void __exit xt_flowoffload_tg_exit(void)
++{
++ xt_unregister_target(&offload_tg_reg);
++ unregister_netdevice_notifier(&flow_offload_netdev_notifier);
++ nf_flow_table_free(&flowtable[0].ft);
++ nf_flow_table_free(&flowtable[1].ft);
++}
++
++MODULE_LICENSE("GPL");
++module_init(xt_flowoffload_tg_init);
++module_exit(xt_flowoffload_tg_exit);
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -7,7 +7,6 @@
+ #include <linux/netdevice.h>
+ #include <net/ip.h>
+ #include <net/ip6_route.h>
+-#include <net/netfilter/nf_tables.h>
+ #include <net/netfilter/nf_flow_table.h>
+ #include <net/netfilter/nf_conntrack.h>
+ #include <net/netfilter/nf_conntrack_core.h>
+@@ -380,8 +379,7 @@ flow_offload_lookup(struct nf_flowtable
+ }
+ EXPORT_SYMBOL_GPL(flow_offload_lookup);
+
+-static int
+-nf_flow_table_iterate(struct nf_flowtable *flow_table,
++int nf_flow_table_iterate(struct nf_flowtable *flow_table,
+ void (*iter)(struct nf_flowtable *flowtable,
+ struct flow_offload *flow, void *data),
+ void *data)
+@@ -435,6 +433,7 @@ static void nf_flow_offload_gc_step(stru
+ nf_flow_offload_stats(flow_table, flow);
+ }
+ }
++EXPORT_SYMBOL_GPL(nf_flow_table_iterate);
+
+ void nf_flow_table_gc_run(struct nf_flowtable *flow_table)
+ {
+--- /dev/null
++++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
++#ifndef _XT_FLOWOFFLOAD_H
++#define _XT_FLOWOFFLOAD_H
++
++#include <linux/types.h>
++
++enum {
++ XT_FLOWOFFLOAD_HW = 1 << 0,
++
++ XT_FLOWOFFLOAD_MASK = XT_FLOWOFFLOAD_HW
++};
++
++struct xt_flowoffload_target_info {
++ __u32 flags;
++};
++
++#endif /* _XT_FLOWOFFLOAD_H */
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -276,6 +276,11 @@ void nf_flow_table_free(struct nf_flowta
+
+ void flow_offload_teardown(struct flow_offload *flow);
+
++int nf_flow_table_iterate(struct nf_flowtable *flow_table,
++ void (*iter)(struct nf_flowtable *flowtable,
++ struct flow_offload *flow, void *data),
++ void *data);
++
+ void nf_flow_snat_port(const struct flow_offload *flow,
+ struct sk_buff *skb, unsigned int thoff,
+ u8 protocol, enum flow_offload_tuple_dir dir);
diff --git a/target/linux/generic/hack-6.1/651-wireless_mesh_header.patch b/target/linux/generic/hack-6.1/651-wireless_mesh_header.patch
new file mode 100644
index 0000000000..12a031ec84
--- /dev/null
+++ b/target/linux/generic/hack-6.1/651-wireless_mesh_header.patch
@@ -0,0 +1,24 @@
+From 6d3bc769657b0ee7c7506dad9911111c4226a7ea Mon Sep 17 00:00:00 2001
+From: Imre Kaloz <kaloz@openwrt.org>
+Date: Fri, 7 Jul 2017 17:21:05 +0200
+Subject: mac80211: increase wireless mesh header size
+
+lede-commit 3d4466cfd8f75f717efdb1f96fdde3c70d865fc1
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+---
+ include/linux/netdevice.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -145,8 +145,8 @@ static inline bool dev_xmit_complete(int
+
+ #if defined(CONFIG_HYPERV_NET)
+ # define LL_MAX_HEADER 128
+-#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25)
+-# if defined(CONFIG_MAC80211_MESH)
++#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) || 1
++# if defined(CONFIG_MAC80211_MESH) || 1
+ # define LL_MAX_HEADER 128
+ # else
+ # define LL_MAX_HEADER 96
diff --git a/target/linux/generic/hack-6.1/660-fq_codel_defaults.patch b/target/linux/generic/hack-6.1/660-fq_codel_defaults.patch
new file mode 100644
index 0000000000..a57a045f4a
--- /dev/null
+++ b/target/linux/generic/hack-6.1/660-fq_codel_defaults.patch
@@ -0,0 +1,27 @@
+From a6ccb238939b25851474a279b20367fd24a0e816 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:21:53 +0200
+Subject: hack: net: fq_codel: tune defaults for small devices
+
+Assume that x86_64 devices always have a big memory and do not need this
+optimization compared to devices with only 32 MB or 64 MB RAM.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/sched/sch_fq_codel.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/net/sched/sch_fq_codel.c
++++ b/net/sched/sch_fq_codel.c
+@@ -467,7 +467,11 @@ static int fq_codel_init(struct Qdisc *s
+
+ sch->limit = 10*1024;
+ q->flows_cnt = 1024;
++#ifdef CONFIG_X86_64
+ q->memory_limit = 32 << 20; /* 32 MBytes */
++#else
++ q->memory_limit = 4 << 20; /* 4 MBytes */
++#endif
+ q->drop_batch_size = 64;
+ q->quantum = psched_mtu(qdisc_dev(sch));
+ INIT_LIST_HEAD(&q->new_flows);
diff --git a/target/linux/generic/hack-6.1/661-kernel-ct-size-the-hashtable-more-adequately.patch b/target/linux/generic/hack-6.1/661-kernel-ct-size-the-hashtable-more-adequately.patch
new file mode 100644
index 0000000000..683aa8a8df
--- /dev/null
+++ b/target/linux/generic/hack-6.1/661-kernel-ct-size-the-hashtable-more-adequately.patch
@@ -0,0 +1,25 @@
+From 804fbb3f2ec9283f7b778e057a68bfff440a0be6 Mon Sep 17 00:00:00 2001
+From: Rui Salvaterra <rsalvaterra@gmail.com>
+Date: Wed, 30 Mar 2022 22:51:55 +0100
+Subject: [PATCH] kernel: ct: size the hashtable more adequately
+
+To set the default size of the connection tracking hash table, a divider of
+16384 becomes inadequate for a router handling lots of connections. Divide by
+2048 instead, making the default size scale better with the available RAM.
+
+Signed-off-by: Rui Salvaterra <rsalvaterra@gmail.com>
+---
+ net/netfilter/nf_conntrack_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/net/netfilter/nf_conntrack_core.c
++++ b/net/netfilter/nf_conntrack_core.c
+@@ -2735,7 +2735,7 @@ int nf_conntrack_init_start(void)
+
+ if (!nf_conntrack_htable_size) {
+ nf_conntrack_htable_size
+- = (((nr_pages << PAGE_SHIFT) / 16384)
++ = (((nr_pages << PAGE_SHIFT) / 2048)
+ / sizeof(struct hlist_head));
+ if (BITS_PER_LONG >= 64 &&
+ nr_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE)))
diff --git a/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch
new file mode 100644
index 0000000000..48be440025
--- /dev/null
+++ b/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch
@@ -0,0 +1,131 @@
+From 36e516290611e613aa92996cb4339561452695b4 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:24:23 +0200
+Subject: net: swconfig: adds openwrt switch layer
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/net/phy/Kconfig | 83 +++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/net/phy/Makefile | 15 +++++++++
+ include/uapi/linux/Kbuild | 1 +
+ 3 files changed, 99 insertions(+)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -61,6 +61,80 @@ config SFP
+ depends on HWMON || HWMON=n
+ select MDIO_I2C
+
++comment "Switch configuration API + drivers"
++
++config SWCONFIG
++ tristate "Switch configuration API"
++ help
++ Switch configuration API using netlink. This allows
++ you to configure the VLAN features of certain switches.
++
++config SWCONFIG_LEDS
++ bool "Switch LED trigger support"
++ depends on (SWCONFIG && LEDS_TRIGGERS)
++
++config ADM6996_PHY
++ tristate "Driver for ADM6996 switches"
++ select SWCONFIG
++ help
++ Currently supports the ADM6996FC and ADM6996M switches.
++ Support for FC is very limited.
++
++config AR8216_PHY
++ tristate "Driver for Atheros AR8216/8327 switches"
++ select SWCONFIG
++ select ETHERNET_PACKET_MANGLE
++
++config AR8216_PHY_LEDS
++ bool "Atheros AR8216 switch LED support"
++ depends on (AR8216_PHY && LEDS_CLASS)
++
++source "drivers/net/phy/b53/Kconfig"
++
++config IP17XX_PHY
++ tristate "Driver for IC+ IP17xx switches"
++ select SWCONFIG
++
++config PSB6970_PHY
++ tristate "Lantiq XWAY Tantos (PSB6970) Ethernet switch"
++ select SWCONFIG
++
++config RTL8306_PHY
++ tristate "Driver for Realtek RTL8306S switches"
++ select SWCONFIG
++
++config RTL8366_SMI
++ tristate "Driver for the RTL8366 SMI interface"
++ depends on GPIOLIB
++ help
++ This module implements the SMI interface protocol which is used
++ by some RTL8366 ethernet switch devices via the generic GPIO API.
++
++if RTL8366_SMI
++
++config RTL8366_SMI_DEBUG_FS
++ bool "RTL8366 SMI interface debugfs support"
++ depends on DEBUG_FS
++ default n
++
++config RTL8366S_PHY
++ tristate "Driver for the Realtek RTL8366S switch"
++ select SWCONFIG
++
++config RTL8366RB_PHY
++ tristate "Driver for the Realtek RTL8366RB switch"
++ select SWCONFIG
++
++config RTL8367_PHY
++ tristate "Driver for the Realtek RTL8367R/M switches"
++ select SWCONFIG
++
++config RTL8367B_PHY
++ tristate "Driver fot the Realtek RTL8367R-VB switch"
++ select SWCONFIG
++
++endif # RTL8366_SMI
++
+ comment "MII PHY device drivers"
+
+ config AMD_PHY
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -24,6 +24,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_
+ obj-$(CONFIG_PHYLINK) += phylink.o
+ obj-$(CONFIG_PHYLIB) += libphy.o
+
++obj-$(CONFIG_SWCONFIG) += swconfig.o
++obj-$(CONFIG_ADM6996_PHY) += adm6996.o
++obj-$(CONFIG_AR8216_PHY) += ar8xxx.o
++ar8xxx-y += ar8216.o
++ar8xxx-y += ar8327.o
++obj-$(CONFIG_SWCONFIG_B53) += b53/
++obj-$(CONFIG_IP17XX_PHY) += ip17xx.o
++obj-$(CONFIG_PSB6970_PHY) += psb6970.o
++obj-$(CONFIG_RTL8306_PHY) += rtl8306.o
++obj-$(CONFIG_RTL8366_SMI) += rtl8366_smi.o
++obj-$(CONFIG_RTL8366S_PHY) += rtl8366s.o
++obj-$(CONFIG_RTL8366RB_PHY) += rtl8366rb.o
++obj-$(CONFIG_RTL8367_PHY) += rtl8367.o
++obj-$(CONFIG_RTL8367B_PHY) += rtl8367b.o
++
+ obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o
+
+ obj-$(CONFIG_SFP) += sfp.o
+--- a/include/linux/platform_data/b53.h
++++ b/include/linux/platform_data/b53.h
+@@ -29,6 +29,9 @@ struct b53_platform_data {
+ u32 chip_id;
+ u16 enabled_ports;
+
++ /* allow to specify an ethX alias */
++ const char *alias;
++
+ /* only used by MMAP'd driver */
+ unsigned big_endian:1;
+ void __iomem *regs;
diff --git a/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch
new file mode 100644
index 0000000000..e62f45f3c3
--- /dev/null
+++ b/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch
@@ -0,0 +1,21 @@
+From ebd924d773223593142d417c41d4ee6fa16f1805 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:45:56 +0200
+Subject: [PATCH] net/dsa/mv88e6xxx: disable ATU violation
+
+---
+ drivers/net/dsa/mv88e6xxx/chip.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -2985,6 +2985,9 @@ static int mv88e6xxx_setup_port(struct m
+ else
+ reg = 1 << port;
+
++ /* Disable ATU member violation interrupt */
++ reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG;
++
+ err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR,
+ reg);
+ if (err)
diff --git a/target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch b/target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch
new file mode 100644
index 0000000000..29050d38ea
--- /dev/null
+++ b/target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch
@@ -0,0 +1,142 @@
+From: Birger Koblitz <git@birger-koblitz.de>
+Date: Sun, 5 Sep 2021 15:13:10 +0200
+Subject: [PATCH] kernel: Add AQR113C and AQR813 support
+
+This hack adds support for the Aquantia 4th generation, 10GBit
+PHYs AQR113C and AQR813.
+
+Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -20,8 +20,10 @@
+ #define PHY_ID_AQR105 0x03a1b4a2
+ #define PHY_ID_AQR106 0x03a1b4d0
+ #define PHY_ID_AQR107 0x03a1b4e0
++#define PHY_ID_AQR113C 0x31c31c12
+ #define PHY_ID_AQCS109 0x03a1b5c2
+ #define PHY_ID_AQR405 0x03a1b4b0
++#define PHY_ID_AQR813 0x31c31cb2
+
+ #define MDIO_PHYXS_VEND_IF_STATUS 0xe812
+ #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3)
+@@ -381,6 +383,49 @@ static int aqr107_read_rate(struct phy_d
+ return 0;
+ }
+
++static int aqr113c_read_status(struct phy_device *phydev)
++{
++ int val, ret;
++
++ ret = aqr_read_status(phydev);
++ if (ret)
++ return ret;
++
++ if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE)
++ return 0;
++
++ // On AQR113C, the speed returned by aqr_read_status is wrong
++ aqr107_read_rate(phydev);
++
++ val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS);
++ if (val < 0)
++ return val;
++
++ switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) {
++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR:
++ phydev->interface = PHY_INTERFACE_MODE_10GKR;
++ break;
++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI:
++ phydev->interface = PHY_INTERFACE_MODE_10GBASER;
++ break;
++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII:
++ phydev->interface = PHY_INTERFACE_MODE_USXGMII;
++ break;
++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
++ phydev->interface = PHY_INTERFACE_MODE_SGMII;
++ break;
++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII:
++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
++ break;
++ default:
++ phydev->interface = PHY_INTERFACE_MODE_NA;
++ break;
++ }
++
++ /* Read downshifted rate from vendor register */
++ return aqr107_read_rate(phydev);
++}
++
+ static int aqr107_read_status(struct phy_device *phydev)
+ {
+ int val, ret;
+@@ -511,7 +556,7 @@ static void aqr107_chip_info(struct phy_
+ build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val);
+ prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val);
+
+- phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n",
++ phydev_info(phydev, "FW %u.%u, Build %u, Provisioning %u\n",
+ fw_major, fw_minor, build_id, prov_id);
+ }
+
+@@ -719,6 +764,24 @@ static struct phy_driver aqr_driver[] =
+ .link_change_notify = aqr107_link_change_notify,
+ },
+ {
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
++ .name = "Aquantia AQR113C",
++ .probe = aqr107_probe,
++ .config_init = aqr107_config_init,
++ .config_aneg = aqr_config_aneg,
++ .config_intr = aqr_config_intr,
++ .handle_interrupt = aqr_handle_interrupt,
++ .read_status = aqr113c_read_status,
++ .get_tunable = aqr107_get_tunable,
++ .set_tunable = aqr107_set_tunable,
++ .suspend = aqr107_suspend,
++ .resume = aqr107_resume,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++ .link_change_notify = aqr107_link_change_notify,
++},
++{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
+ .name = "Aquantia AQCS109",
+ .probe = aqr107_probe,
+@@ -744,6 +807,24 @@ static struct phy_driver aqr_driver[] =
+ .handle_interrupt = aqr_handle_interrupt,
+ .read_status = aqr_read_status,
+ },
++{
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
++ .name = "Aquantia AQR813",
++ .probe = aqr107_probe,
++ .config_init = aqr107_config_init,
++ .config_aneg = aqr_config_aneg,
++ .config_intr = aqr_config_intr,
++ .handle_interrupt = aqr_handle_interrupt,
++ .read_status = aqr113c_read_status,
++ .get_tunable = aqr107_get_tunable,
++ .set_tunable = aqr107_set_tunable,
++ .suspend = aqr107_suspend,
++ .resume = aqr107_resume,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++ .link_change_notify = aqr107_link_change_notify,
++},
+ };
+
+ module_phy_driver(aqr_driver);
+@@ -754,8 +835,10 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
+ { }
+ };
+
diff --git a/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch
new file mode 100644
index 0000000000..dbdb141c6d
--- /dev/null
+++ b/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch
@@ -0,0 +1,178 @@
+From ffe387740bbe88dd88bbe04d6375902708003d6e Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:25:00 +0200
+Subject: net: add packet mangeling
+
+ar8216 switches have a hardware bug, which renders normal 802.1q support
+unusable. Packet mangling is required to fix up the vlan for incoming
+packets.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/netdevice.h | 11 +++++++++++
+ include/linux/skbuff.h | 14 ++++----------
+ net/Kconfig | 6 ++++++
+ net/core/dev.c | 20 +++++++++++++++-----
+ net/core/skbuff.c | 17 +++++++++++++++++
+ net/ethernet/eth.c | 6 ++++++
+ 6 files changed, 59 insertions(+), 15 deletions(-)
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -1679,6 +1679,10 @@ enum netdev_priv_flags {
+ IFF_TX_SKB_NO_LINEAR = BIT_ULL(31),
+ };
+
++enum netdev_extra_priv_flags {
++ IFF_NO_IP_ALIGN = 1<<0,
++};
++
+ #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
+ #define IFF_EBRIDGE IFF_EBRIDGE
+ #define IFF_BONDING IFF_BONDING
+@@ -1711,6 +1715,7 @@ enum netdev_priv_flags {
+ #define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER
+ #define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK
+ #define IFF_TX_SKB_NO_LINEAR IFF_TX_SKB_NO_LINEAR
++#define IFF_NO_IP_ALIGN IFF_NO_IP_ALIGN
+
+ /* Specifies the type of the struct net_device::ml_priv pointer */
+ enum netdev_ml_priv_type {
+@@ -2012,6 +2017,7 @@ struct net_device {
+ /* Read-mostly cache-line for fast-path access */
+ unsigned int flags;
+ unsigned int priv_flags;
++ unsigned int extra_priv_flags;
+ const struct net_device_ops *netdev_ops;
+ int ifindex;
+ unsigned short gflags;
+@@ -2072,6 +2078,11 @@ struct net_device {
+ const struct tlsdev_ops *tlsdev_ops;
+ #endif
+
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ void (*eth_mangle_rx)(struct net_device *dev, struct sk_buff *skb);
++ struct sk_buff *(*eth_mangle_tx)(struct net_device *dev, struct sk_buff *skb);
++#endif
++
+ const struct header_ops *header_ops;
+
+ unsigned char operstate;
+@@ -2146,6 +2157,10 @@ struct net_device {
+ struct mctp_dev __rcu *mctp_ptr;
+ #endif
+
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ void *phy_ptr; /* PHY device specific data */
++#endif
++
+ /*
+ * Cache lines mostly used on receive path (including eth_type_trans())
+ */
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -2855,6 +2855,10 @@ static inline int pskb_trim(struct sk_bu
+ return (len < skb->len) ? __pskb_trim(skb, len) : 0;
+ }
+
++extern struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
++ unsigned int length, gfp_t gfp);
++
++
+ /**
+ * pskb_trim_unique - remove end from a paged unique (not cloned) buffer
+ * @skb: buffer to alter
+@@ -3005,16 +3009,6 @@ static inline struct sk_buff *dev_alloc_
+ }
+
+
+-static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
+- unsigned int length, gfp_t gfp)
+-{
+- struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp);
+-
+- if (NET_IP_ALIGN && skb)
+- skb_reserve(skb, NET_IP_ALIGN);
+- return skb;
+-}
+-
+ static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev,
+ unsigned int length)
+ {
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -26,6 +26,12 @@ menuconfig NET
+
+ if NET
+
++config ETHERNET_PACKET_MANGLE
++ bool
++ help
++ This option can be selected by phy drivers that need to mangle
++ packets going in or out of an ethernet device.
++
+ config WANT_COMPAT_NETLINK_MESSAGES
+ bool
+ help
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -3590,6 +3590,11 @@ static int xmit_one(struct sk_buff *skb,
+ if (dev_nit_active(dev))
+ dev_queue_xmit_nit(skb, dev);
+
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ if (dev->eth_mangle_tx && !(skb = dev->eth_mangle_tx(dev, skb)))
++ return NETDEV_TX_OK;
++#endif
++
+ len = skb->len;
+ PRANDOM_ADD_NOISE(skb, dev, txq, len + jiffies);
+ trace_net_dev_start_xmit(skb, dev);
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -61,6 +61,7 @@
+ #include <linux/if_vlan.h>
+ #include <linux/mpls.h>
+ #include <linux/kcov.h>
++#include <linux/if.h>
+
+ #include <net/protocol.h>
+ #include <net/dst.h>
+@@ -602,6 +603,22 @@ skb_fail:
+ }
+ EXPORT_SYMBOL(__napi_alloc_skb);
+
++struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
++ unsigned int length, gfp_t gfp)
++{
++ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp);
++
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ if (dev && (dev->extra_priv_flags & IFF_NO_IP_ALIGN))
++ return skb;
++#endif
++
++ if (NET_IP_ALIGN && skb)
++ skb_reserve(skb, NET_IP_ALIGN);
++ return skb;
++}
++EXPORT_SYMBOL(__netdev_alloc_skb_ip_align);
++
+ void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off,
+ int size, unsigned int truesize)
+ {
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -170,6 +170,12 @@ __be16 eth_type_trans(struct sk_buff *sk
+ const struct ethhdr *eth;
+
+ skb->dev = dev;
++
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++ if (dev->eth_mangle_rx)
++ dev->eth_mangle_rx(dev, skb);
++#endif
++
+ skb_reset_mac_header(skb);
+
+ eth = (struct ethhdr *)skb->data;
diff --git a/target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch b/target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch
new file mode 100644
index 0000000000..211696c568
--- /dev/null
+++ b/target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch
@@ -0,0 +1,154 @@
+From 5f62951fba63a9f9cfff564209426bdea5fcc371 Mon Sep 17 00:00:00 2001
+From: Alex Marginean <alexandru.marginean@nxp.com>
+Date: Tue, 27 Aug 2019 15:16:56 +0300
+Subject: [PATCH] drivers: net: phy: aquantia: enable AQR112 and AQR412
+
+Adds support for AQR112 and AQR412 which is mostly based on existing code
+with the addition of code configuring the protocol on system side.
+This allows changing the system side protocol without having to deploy a
+different firmware on the PHY.
+
+Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com>
+---
+ drivers/net/phy/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 88 insertions(+)
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -20,9 +20,11 @@
+ #define PHY_ID_AQR105 0x03a1b4a2
+ #define PHY_ID_AQR106 0x03a1b4d0
+ #define PHY_ID_AQR107 0x03a1b4e0
++#define PHY_ID_AQR112 0x03a1b662
+ #define PHY_ID_AQR113C 0x31c31c12
+ #define PHY_ID_AQCS109 0x03a1b5c2
+ #define PHY_ID_AQR405 0x03a1b4b0
++#define PHY_ID_AQR412 0x03a1b712
+ #define PHY_ID_AQR813 0x31c31cb2
+
+ #define MDIO_PHYXS_VEND_IF_STATUS 0xe812
+@@ -135,6 +137,29 @@
+ #define AQR107_OP_IN_PROG_SLEEP 1000
+ #define AQR107_OP_IN_PROG_TIMEOUT 100000
+
++/* registers in MDIO_MMD_VEND1 region */
++#define AQUANTIA_VND1_GLOBAL_SC 0x000
++#define AQUANTIA_VND1_GLOBAL_SC_LP BIT(0xb)
++
++/* global start rate, the protocol associated with this speed is used by default
++ * on SI.
++ */
++#define AQUANTIA_VND1_GSTART_RATE 0x31a
++#define AQUANTIA_VND1_GSTART_RATE_OFF 0
++#define AQUANTIA_VND1_GSTART_RATE_100M 1
++#define AQUANTIA_VND1_GSTART_RATE_1G 2
++#define AQUANTIA_VND1_GSTART_RATE_10G 3
++#define AQUANTIA_VND1_GSTART_RATE_2_5G 4
++#define AQUANTIA_VND1_GSTART_RATE_5G 5
++
++/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */
++#define AQUANTIA_VND1_GSYSCFG_BASE 0x31b
++#define AQUANTIA_VND1_GSYSCFG_100M 0
++#define AQUANTIA_VND1_GSYSCFG_1G 1
++#define AQUANTIA_VND1_GSYSCFG_2_5G 2
++#define AQUANTIA_VND1_GSYSCFG_5G 3
++#define AQUANTIA_VND1_GSYSCFG_10G 4
++
+ struct aqr107_hw_stat {
+ const char *name;
+ int reg;
+@@ -266,6 +291,51 @@ static int aqr_config_aneg(struct phy_de
+ return genphy_c45_check_and_restart_aneg(phydev, changed);
+ }
+
++static struct {
++ u16 syscfg;
++ int cnt;
++ u16 start_rate;
++} aquantia_syscfg[PHY_INTERFACE_MODE_MAX] = {
++ [PHY_INTERFACE_MODE_SGMII] = {0x04b, AQUANTIA_VND1_GSYSCFG_1G,
++ AQUANTIA_VND1_GSTART_RATE_1G},
++ [PHY_INTERFACE_MODE_2500BASEX] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G,
++ AQUANTIA_VND1_GSTART_RATE_2_5G},
++ [PHY_INTERFACE_MODE_XGMII] = {0x100, AQUANTIA_VND1_GSYSCFG_10G,
++ AQUANTIA_VND1_GSTART_RATE_10G},
++ [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G,
++ AQUANTIA_VND1_GSTART_RATE_10G},
++};
++
++/* Sets up protocol on system side before calling aqr_config_aneg */
++static int aqr_config_aneg_set_prot(struct phy_device *phydev)
++{
++ int if_type = phydev->interface;
++ int i;
++
++ if (!aquantia_syscfg[if_type].cnt)
++ return 0;
++
++ /* set PHY in low power mode so we can configure protocols */
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC,
++ AQUANTIA_VND1_GLOBAL_SC_LP);
++ mdelay(10);
++
++ /* set the default rate to enable the SI link */
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
++ aquantia_syscfg[if_type].start_rate);
++
++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++)
++ phy_write_mmd(phydev, MDIO_MMD_VEND1,
++ AQUANTIA_VND1_GSYSCFG_BASE + i,
++ aquantia_syscfg[if_type].syscfg);
++
++ /* wake PHY back up */
++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);
++ mdelay(10);
++
++ return aqr_config_aneg(phydev);
++}
++
+ static int aqr_config_intr(struct phy_device *phydev)
+ {
+ bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED;
+@@ -825,6 +895,30 @@ static struct phy_driver aqr_driver[] =
+ .get_stats = aqr107_get_stats,
+ .link_change_notify = aqr107_link_change_notify,
+ },
++{
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
++ .name = "Aquantia AQR112",
++ .probe = aqr107_probe,
++ .config_aneg = aqr_config_aneg_set_prot,
++ .config_intr = aqr_config_intr,
++ .handle_interrupt = aqr_handle_interrupt,
++ .read_status = aqr107_read_status,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++},
++{
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
++ .name = "Aquantia AQR412",
++ .probe = aqr107_probe,
++ .config_aneg = aqr_config_aneg_set_prot,
++ .config_intr = aqr_config_intr,
++ .handle_interrupt = aqr_handle_interrupt,
++ .read_status = aqr107_read_status,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++},
+ };
+
+ module_phy_driver(aqr_driver);
+@@ -835,9 +929,11 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
+ { }
+ };
diff --git a/target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch b/target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch
new file mode 100644
index 0000000000..7d16c8aa28
--- /dev/null
+++ b/target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch
@@ -0,0 +1,34 @@
+From 5f008cb22f60da4e10375f22266c1a4e20b1252e Mon Sep 17 00:00:00 2001
+From: Alex Marginean <alexandru.marginean@nxp.com>
+Date: Fri, 20 Sep 2019 18:22:52 +0300
+Subject: [PATCH] drivers: net: phy: aquantia: fix system side protocol
+ misconfiguration
+
+Do not set up protocols for speeds that are not supported by FW. Enabling
+these protocols leads to link issues on system side.
+
+Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com>
+---
+ drivers/net/phy/aquantia_main.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -324,10 +324,16 @@ static int aqr_config_aneg_set_prot(stru
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
+ aquantia_syscfg[if_type].start_rate);
+
+- for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++)
++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) {
++ u16 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++ AQUANTIA_VND1_GSYSCFG_BASE + i);
++ if (!reg)
++ continue;
++
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ AQUANTIA_VND1_GSYSCFG_BASE + i,
+ aquantia_syscfg[if_type].syscfg);
++ }
+
+ /* wake PHY back up */
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);
diff --git a/target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch b/target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch
new file mode 100644
index 0000000000..d03c3430fa
--- /dev/null
+++ b/target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch
@@ -0,0 +1,43 @@
+From 2e677e4ae8f8330f68013163b060d0fda3a43095 Mon Sep 17 00:00:00 2001
+From: "Langer, Thomas" <tlanger@maxlinear.com>
+Date: Fri, 9 Jul 2021 17:36:46 +0200
+Subject: [PATCH] PONRTSYS-8842: aquantia: Add AQR113 driver support
+
+Add a new entry for AQR113 PHY_ID
+---
+ drivers/net/phy/aquantia_main.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -21,6 +21,7 @@
+ #define PHY_ID_AQR106 0x03a1b4d0
+ #define PHY_ID_AQR107 0x03a1b4e0
+ #define PHY_ID_AQR112 0x03a1b662
++#define PHY_ID_AQR113 0x31c31c40
+ #define PHY_ID_AQR113C 0x31c31c12
+ #define PHY_ID_AQCS109 0x03a1b5c2
+ #define PHY_ID_AQR405 0x03a1b4b0
+@@ -914,6 +915,14 @@ static struct phy_driver aqr_driver[] =
+ .get_stats = aqr107_get_stats,
+ },
+ {
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
++ .name = "Aquantia AQR113",
++ .config_aneg = aqr_config_aneg,
++ .config_intr = aqr_config_intr,
++ .handle_interrupt = aqr_handle_interrupt,
++ .read_status = aqr107_read_status,
++},
++{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
+ .name = "Aquantia AQR412",
+ .probe = aqr107_probe,
+@@ -936,6 +945,7 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
diff --git a/target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch b/target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch
new file mode 100644
index 0000000000..9821c0c86e
--- /dev/null
+++ b/target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch
@@ -0,0 +1,63 @@
+From 3b92ee7b7899b6beffb2b484c58326e36612a873 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 23 Dec 2021 14:52:56 +0000
+Subject: [PATCH] net: phy: aquantia: add PHY_ID for AQR112R
+
+As advised by Ian Chang this PHY is used in Puzzle devices.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/aquantia_main.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/phy/aquantia_main.c
++++ b/drivers/net/phy/aquantia_main.c
+@@ -21,6 +21,8 @@
+ #define PHY_ID_AQR106 0x03a1b4d0
+ #define PHY_ID_AQR107 0x03a1b4e0
+ #define PHY_ID_AQR112 0x03a1b662
++#define PHY_ID_AQR112C 0x03a1b790
++#define PHY_ID_AQR112R 0x31c31d12
+ #define PHY_ID_AQR113 0x31c31c40
+ #define PHY_ID_AQR113C 0x31c31c12
+ #define PHY_ID_AQCS109 0x03a1b5c2
+@@ -915,6 +917,30 @@ static struct phy_driver aqr_driver[] =
+ .get_stats = aqr107_get_stats,
+ },
+ {
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112C),
++ .name = "Aquantia AQR112C",
++ .probe = aqr107_probe,
++ .config_aneg = aqr_config_aneg_set_prot,
++ .config_intr = aqr_config_intr,
++ .handle_interrupt = aqr_handle_interrupt,
++ .read_status = aqr107_read_status,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++},
++{
++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112R),
++ .name = "Aquantia AQR112R",
++ .probe = aqr107_probe,
++ .config_aneg = aqr_config_aneg_set_prot,
++ .config_intr = aqr_config_intr,
++ .handle_interrupt = aqr_handle_interrupt,
++ .read_status = aqr107_read_status,
++ .get_sset_count = aqr107_get_sset_count,
++ .get_strings = aqr107_get_strings,
++ .get_stats = aqr107_get_stats,
++},
++{
+ PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
+ .name = "Aquantia AQR113",
+ .config_aneg = aqr_config_aneg,
+@@ -945,6 +971,8 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112C) },
++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112R) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
diff --git a/target/linux/generic/hack-6.1/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch b/target/linux/generic/hack-6.1/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch
new file mode 100644
index 0000000000..8b7f2f0955
--- /dev/null
+++ b/target/linux/generic/hack-6.1/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch
@@ -0,0 +1,53 @@
+From 880d1311335120f64447ca9d11933872d734e19a Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 27 Mar 2023 18:41:54 +0100
+Subject: [PATCH] generic: pcs-mtk-lynxi: add hack to use 2500Base-X without AN
+
+Using 2500Base-T SFP modules e.g. on the BananaPi R3 requires manually
+disabling auto-negotiation, e.g. using ethtool. While a proper fix
+using SFP quirks is being discussed upstream, bring a work-around to
+restore user experience to what it was before the switch to the
+dedicated SGMII PCS driver.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+
+--- a/drivers/net/pcs/pcs-mtk-lynxi.c
++++ b/drivers/net/pcs/pcs-mtk-lynxi.c
+@@ -92,14 +92,23 @@ static void mtk_pcs_lynxi_get_state(stru
+ struct phylink_link_state *state)
+ {
+ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
+- unsigned int bm, adv;
++ unsigned int bm, bmsr, adv;
+
+ /* Read the BMSR and LPA */
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
+- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
++ bmsr = FIELD_GET(SGMII_BMSR, bm);
++
++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) {
++ state->link = !!(bmsr & BMSR_LSTATUS);
++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
++ state->speed = SPEED_2500;
++ state->duplex = DUPLEX_FULL;
+
+- phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
+- FIELD_GET(SGMII_LPA, adv));
++ return;
++ }
++
++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
++ phylink_mii_c22_pcs_decode_state(state, bmsr, FIELD_GET(SGMII_LPA, adv));
+ }
+
+ static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
+@@ -134,7 +143,8 @@ static int mtk_pcs_lynxi_config(struct p
+ /* 1000base-X or 2500base-X autoneg */
+ sgm_mode = SGMII_REMOTE_FAULT_DIS;
+ use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+- advertising);
++ advertising) &&
++ !(interface == PHY_INTERFACE_MODE_2500BASEX);
+ } else {
+ /* 1000base-X or 2500base-X without autoneg */
+ sgm_mode = 0;
diff --git a/target/linux/generic/hack-6.1/760-net-usb-r8152-add-LED-configuration-from-OF.patch b/target/linux/generic/hack-6.1/760-net-usb-r8152-add-LED-configuration-from-OF.patch
new file mode 100644
index 0000000000..f61c078521
--- /dev/null
+++ b/target/linux/generic/hack-6.1/760-net-usb-r8152-add-LED-configuration-from-OF.patch
@@ -0,0 +1,74 @@
+From 82985725e071f2a5735052f18e109a32aeac3a0b Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sun, 26 Jul 2020 02:38:31 +0200
+Subject: [PATCH] net: usb: r8152: add LED configuration from OF
+
+This adds the ability to configure the LED configuration register using
+OF. This way, the correct value for board specific LED configuration can
+be determined.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/usb/r8152.c | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -11,6 +11,7 @@
+ #include <linux/mii.h>
+ #include <linux/ethtool.h>
+ #include <linux/usb.h>
++#include <linux/of.h>
+ #include <linux/crc32.h>
+ #include <linux/if_vlan.h>
+ #include <linux/uaccess.h>
+@@ -6886,6 +6887,22 @@ static void rtl_tally_reset(struct r8152
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data);
+ }
+
++static int r8152_led_configuration(struct r8152 *tp)
++{
++ u32 led_data;
++ int ret;
++
++ ret = of_property_read_u32(tp->udev->dev.of_node, "realtek,led-data",
++ &led_data);
++
++ if (ret)
++ return ret;
++
++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LEDSEL, led_data);
++
++ return 0;
++}
++
+ static void r8152b_init(struct r8152 *tp)
+ {
+ u32 ocp_data;
+@@ -6927,6 +6944,8 @@ static void r8152b_init(struct r8152 *tp
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+ ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
+ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
++
++ r8152_led_configuration(tp);
+ }
+
+ static void r8153_init(struct r8152 *tp)
+@@ -7067,6 +7086,8 @@ static void r8153_init(struct r8152 *tp)
+ tp->coalesce = COALESCE_SLOW;
+ break;
+ }
++
++ r8152_led_configuration(tp);
+ }
+
+ static void r8153b_init(struct r8152 *tp)
+@@ -7149,6 +7170,8 @@ static void r8153b_init(struct r8152 *tp
+ rtl_tally_reset(tp);
+
+ tp->coalesce = 15000; /* 15 us */
++
++ r8152_led_configuration(tp);
+ }
+
+ static void r8153c_init(struct r8152 *tp)
diff --git a/target/linux/generic/hack-6.1/761-dt-bindings-net-add-RTL8152-binding-documentation.patch b/target/linux/generic/hack-6.1/761-dt-bindings-net-add-RTL8152-binding-documentation.patch
new file mode 100644
index 0000000000..be262b993c
--- /dev/null
+++ b/target/linux/generic/hack-6.1/761-dt-bindings-net-add-RTL8152-binding-documentation.patch
@@ -0,0 +1,54 @@
+From 3ee05f4aa64fc86af3be5bc176ba5808de9260a7 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sun, 26 Jul 2020 15:30:33 +0200
+Subject: [PATCH] dt-bindings: net: add RTL8152 binding documentation
+
+Add binding documentation for the Realtek RTL8152 / RTL8153 USB ethernet
+adapters.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ .../bindings/net/realtek,rtl8152.yaml | 36 +++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/realtek,rtl8152.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/realtek,rtl8152.yaml
+@@ -0,0 +1,36 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/realtek,rtl8152.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Realtek RTL8152/RTL8153 series USB ethernet
++
++maintainers:
++ - David Bauer <mail@david-bauer.net>
++
++properties:
++ compatible:
++ oneOf:
++ - items:
++ - enum:
++ - realtek,rtl8152
++ - realtek,rtl8153
++
++ reg:
++ description: The device number on the USB bus
++
++ realtek,led-data:
++ description: Value to be written to the LED configuration register.
++
++required:
++ - compatible
++ - reg
++
++examples:
++ - |
++ usb-eth@2 {
++ compatible = "realtek,rtl8153";
++ reg = <2>;
++ realtek,led-data = <0x87>;
++ };
+\ No newline at end of file
diff --git a/target/linux/generic/hack-6.1/765-mxl-gpy-control-LED-reg-from-DT.patch b/target/linux/generic/hack-6.1/765-mxl-gpy-control-LED-reg-from-DT.patch
new file mode 100644
index 0000000000..8de3668def
--- /dev/null
+++ b/target/linux/generic/hack-6.1/765-mxl-gpy-control-LED-reg-from-DT.patch
@@ -0,0 +1,101 @@
+From 94b90966095f3fa625897e8f53d215882f6e19b3 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sat, 11 Mar 2023 17:00:01 +0100
+Subject: [PATCH] mxl-gpy: control LED reg from DT
+
+Add dynamic configuration for the LED control registers on MXL PHYs.
+
+This patch has been tested with MaxLinear GPY211C. It is unlikely to be
+accepted upstream, as upstream plans on integrating their own framework
+for handling these LEDs.
+
+For the time being, use this hack to configure PHY driven device-LEDs to
+show the correct state.
+
+A possible alternative might be to expose the LEDs using the kernel LED
+framework and bind it to the netdevice. This might also be upstreamable,
+although it is a considerable extra amount of work.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/mxl-gpy.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 36 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/mxl-gpy.c
++++ b/drivers/net/phy/mxl-gpy.c
+@@ -8,6 +8,7 @@
+
+ #include <linux/module.h>
+ #include <linux/bitfield.h>
++#include <linux/of.h>
+ #include <linux/phy.h>
+ #include <linux/netdevice.h>
+
+@@ -30,6 +31,7 @@
+ #define PHY_MIISTAT 0x18 /* MII state */
+ #define PHY_IMASK 0x19 /* interrupt mask */
+ #define PHY_ISTAT 0x1A /* interrupt status */
++#define PHY_LED 0x1B /* LED control */
+ #define PHY_FWV 0x1E /* firmware version */
+
+ #define PHY_MIISTAT_SPD_MASK GENMASK(2, 0)
+@@ -53,10 +55,15 @@
+ PHY_IMASK_ADSC | \
+ PHY_IMASK_ANC)
+
++#define PHY_LED_NUM_LEDS 4
++
+ #define PHY_FWV_REL_MASK BIT(15)
+ #define PHY_FWV_TYPE_MASK GENMASK(11, 8)
+ #define PHY_FWV_MINOR_MASK GENMASK(7, 0)
+
++/* LED */
++#define VSPEC1_LED(x) (0x1 + x)
++
+ /* SGMII */
+ #define VSPEC1_SGMII_CTRL 0x08
+ #define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */
+@@ -80,6 +87,31 @@ static const struct {
+ {9, 0x73},
+ };
+
++static int gpy_led_write(struct phy_device *phydev)
++{
++ struct device_node *node = phydev->mdio.dev.of_node;
++ u32 led_regs[PHY_LED_NUM_LEDS];
++ int i, ret;
++
++ if (!IS_ENABLED(CONFIG_OF_MDIO))
++ return 0;
++
++ if (of_property_read_u32_array(node, "mxl,led-config", led_regs, PHY_LED_NUM_LEDS))
++ return 0;
++
++ /* Enable LED function handling on all ports*/
++ phy_write(phydev, PHY_LED, 0xFF00);
++
++ /* Write LED register values */
++ for (i = 0; i < PHY_LED_NUM_LEDS; i++) {
++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(i), (u16)led_regs[i]);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
+ static int gpy_config_init(struct phy_device *phydev)
+ {
+ int ret;
+@@ -91,7 +123,10 @@ static int gpy_config_init(struct phy_de
+
+ /* Clear all pending interrupts */
+ ret = phy_read(phydev, PHY_ISTAT);
+- return ret < 0 ? ret : 0;
++ if (ret < 0)
++ return ret;
++
++ return gpy_led_write(phydev);
+ }
+
+ static int gpy_probe(struct phy_device *phydev)
diff --git a/target/linux/generic/hack-6.1/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch b/target/linux/generic/hack-6.1/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch
new file mode 100644
index 0000000000..3405d5c535
--- /dev/null
+++ b/target/linux/generic/hack-6.1/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch
@@ -0,0 +1,72 @@
+From cc225d163b5a4f7a0d1968298bf7927306646a47 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Fri, 28 Apr 2023 01:53:01 +0200
+Subject: [PATCH] net: phy: mediatek-ge: add LED configuration interface
+
+This adds a small hack similar to the one used for ar8xxx switches to
+read a reg:value map for configuring the LED configuration registers.
+
+This allows OpenWrt to write device-specific LED action as well as blink
+configurations. It is unlikely to be accepted upstream, as upstream
+plans on integrating their own framework for handling these LEDs.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/mediatek-ge.c | 33 +++++++++++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+--- a/drivers/net/phy/mediatek-ge.c
++++ b/drivers/net/phy/mediatek-ge.c
+@@ -1,4 +1,5 @@
+ // SPDX-License-Identifier: GPL-2.0+
++#include <linux/of.h>
+ #include <linux/bitfield.h>
+ #include <linux/module.h>
+ #include <linux/phy.h>
+@@ -53,6 +54,36 @@ static int mt7530_phy_config_init(struct
+ return 0;
+ }
+
++static int mt7530_led_config_of(struct phy_device *phydev)
++{
++ struct device_node *np = phydev->mdio.dev.of_node;
++ const __be32 *paddr;
++ int len;
++ int i;
++
++ paddr = of_get_property(np, "mediatek,led-config", &len);
++ if (!paddr)
++ return 0;
++
++ if (len < (2 * sizeof(*paddr)))
++ return -EINVAL;
++
++ len /= sizeof(*paddr);
++
++ phydev_warn(phydev, "Configure LED registers (num=%d)\n", len);
++ for (i = 0; i < len - 1; i += 2) {
++ u32 reg;
++ u32 val;
++
++ reg = be32_to_cpup(paddr + i);
++ val = be32_to_cpup(paddr + i + 1);
++
++ phy_write_mmd(phydev, MDIO_MMD_VEND2, reg, val);
++ }
++
++ return 0;
++}
++
+ static int mt7531_phy_config_init(struct phy_device *phydev)
+ {
+ mtk_gephy_config_init(phydev);
+@@ -65,6 +96,9 @@ static int mt7531_phy_config_init(struct
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404);
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404);
+
++ /* LED Config*/
++ mt7530_led_config_of(phydev);
++
+ return 0;
+ }
+
diff --git a/target/linux/generic/hack-6.1/773-bgmac-add-srab-switch.patch b/target/linux/generic/hack-6.1/773-bgmac-add-srab-switch.patch
new file mode 100644
index 0000000000..7127aa136c
--- /dev/null
+++ b/target/linux/generic/hack-6.1/773-bgmac-add-srab-switch.patch
@@ -0,0 +1,98 @@
+From 3cb240533ab787899dc7f17aa7d6c5b4810e2e58 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Fri, 7 Jul 2017 17:26:01 +0200
+Subject: bcm53xx: bgmac: use srab switch driver
+
+use the srab switch driver on these SoCs.
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma.c | 1 +
+ drivers/net/ethernet/broadcom/bgmac.c | 24 ++++++++++++++++++++++++
+ drivers/net/ethernet/broadcom/bgmac.h | 4 ++++
+ 3 files changed, 29 insertions(+)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -280,6 +280,7 @@ static int bgmac_probe(struct bcma_devic
+ bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST;
+ bgmac->feature_flags |= BGMAC_FEAT_NO_RESET;
+ bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500;
++ bgmac->feature_flags |= BGMAC_FEAT_SRAB;
+ break;
+ default:
+ bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST;
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -12,6 +12,7 @@
+ #include <linux/bcma/bcma.h>
+ #include <linux/etherdevice.h>
+ #include <linux/interrupt.h>
++#include <linux/platform_data/b53.h>
+ #include <linux/bcm47xx_nvram.h>
+ #include <linux/phy.h>
+ #include <linux/phy_fixed.h>
+@@ -1408,6 +1409,17 @@ static const struct ethtool_ops bgmac_et
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+ };
+
++static struct b53_platform_data bgmac_b53_pdata = {
++};
++
++static struct platform_device bgmac_b53_dev = {
++ .name = "b53-srab-switch",
++ .id = -1,
++ .dev = {
++ .platform_data = &bgmac_b53_pdata,
++ },
++};
++
+ /**************************************************
+ * MII
+ **************************************************/
+@@ -1546,6 +1558,14 @@ int bgmac_enet_probe(struct bgmac *bgmac
+
+ bgmac->in_init = false;
+
++ if ((bgmac->feature_flags & BGMAC_FEAT_SRAB) && !bgmac_b53_pdata.regs) {
++ bgmac_b53_pdata.regs = ioremap(0x18007000, 0x1000);
++
++ err = platform_device_register(&bgmac_b53_dev);
++ if (!err)
++ bgmac->b53_device = &bgmac_b53_dev;
++ }
++
+ err = register_netdev(bgmac->net_dev);
+ if (err) {
+ dev_err(bgmac->dev, "Cannot register net device\n");
+@@ -1568,6 +1588,10 @@ EXPORT_SYMBOL_GPL(bgmac_enet_probe);
+
+ void bgmac_enet_remove(struct bgmac *bgmac)
+ {
++ if (bgmac->b53_device)
++ platform_device_unregister(&bgmac_b53_dev);
++ bgmac->b53_device = NULL;
++
+ unregister_netdev(bgmac->net_dev);
+ phy_disconnect(bgmac->net_dev->phydev);
+ netif_napi_del(&bgmac->napi);
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -390,6 +390,7 @@
+ #define BGMAC_FEAT_CC4_IF_SW_TYPE_RGMII BIT(18)
+ #define BGMAC_FEAT_CC7_IF_TYPE_RGMII BIT(19)
+ #define BGMAC_FEAT_IDM_MASK BIT(20)
++#define BGMAC_FEAT_SRAB BIT(21)
+
+ struct bgmac_slot_info {
+ union {
+@@ -497,6 +498,9 @@ struct bgmac {
+ void (*cmn_maskset32)(struct bgmac *bgmac, u16 offset, u32 mask,
+ u32 set);
+ int (*phy_connect)(struct bgmac *bgmac);
++
++ /* platform device for associated switch */
++ struct platform_device *b53_device;
+ };
+
+ struct bgmac *bgmac_alloc(struct device *dev);
diff --git a/target/linux/generic/hack-6.1/780-usb-net-MeigLink_modem_support.patch b/target/linux/generic/hack-6.1/780-usb-net-MeigLink_modem_support.patch
new file mode 100644
index 0000000000..367d66fc66
--- /dev/null
+++ b/target/linux/generic/hack-6.1/780-usb-net-MeigLink_modem_support.patch
@@ -0,0 +1,43 @@
+From f81700b6bb2eda3756247bce472d8eaf6f466f61 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:49:26 +0200
+Subject: [PATCH] net/usb/qmi_wwan: add MeigLink modem support
+
+---
+ drivers/net/usb/qmi_wwan.c | 1 +
+ drivers/usb/serial/option.c | 7 +++++++
+ 2 files changed, 8 insertions(+)
+
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -1086,6 +1086,7 @@ static const struct usb_device_id produc
+ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */
+ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */
+ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0801)}, /* Quectel RM520N */
++ {QMI_MATCH_FF_FF_FF(0x05c6, 0xf601)}, /* MeigLink SLM750 */
+
+ /* 3. Combined interface devices matching on interface number */
+ {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -244,6 +244,8 @@ static void option_instat_callback(struc
+ #define UBLOX_PRODUCT_R410M 0x90b2
+ /* These Yuga products use Qualcomm's vendor ID */
+ #define YUGA_PRODUCT_CLM920_NC5 0x9625
++/* These MeigLink products use Qualcomm's vendor ID */
++#define MEIGLINK_PRODUCT_SLM750 0xf601
+
+ #define QUECTEL_VENDOR_ID 0x2c7c
+ /* These Quectel products use Quectel's vendor ID */
+@@ -1169,6 +1171,11 @@ static const struct usb_device_id option
+ .driver_info = ZLP },
+ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
+ .driver_info = RSVD(4) },
++ /* Meiglink products using Qualcomm vendor ID */
++ // Works OK. In case of some issues check macros that are used by Quectel Products
++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0xff, 0xff),
++ .driver_info = NUMEP2 },
++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
+ .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },
diff --git a/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch b/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch
new file mode 100644
index 0000000000..10af22e52e
--- /dev/null
+++ b/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch
@@ -0,0 +1,65 @@
+From 7cc39a6bedbd85f3ff7e16845f310e4ce8d9833f Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 6 Sep 2022 00:31:19 +0100
+Subject: [PATCH] net: sfp: add quirk for ATS SFP-GE-T 1000Base-TX module
+To: netdev@vger.kernel.org,
+ linux-kernel@vger.kernel.org,
+ Russell King <linux@armlinux.org.uk>,
+ Andrew Lunn <andrew@lunn.ch>,
+ Heiner Kallweit <hkallweit1@gmail.com>
+Cc: David S. Miller <davem@davemloft.net>,
+ Eric Dumazet <edumazet@google.com>,
+ Jakub Kicinski <kuba@kernel.org>,
+ Paolo Abeni <pabeni@redhat.com>,
+ Josef Schlehofer <pepe.schlehofer@gmail.com>
+
+This copper module comes with broken TX_FAULT indicator which must be
+ignored for it to work. Implement ignoring TX_FAULT state bit also
+during reset/insertion and mute the warning telling the user that the
+module indicates TX_FAULT.
+
+Co-authored-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/sfp.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -383,6 +383,11 @@ static const struct sfp_quirk sfp_quirks
+ .modes = sfp_quirk_2500basex,
+ .fixup = sfp_fixup_ignore_tx_fault,
+ }, {
++ // OEM SFP-GE-T is 1000Base-T module
++ .vendor = "OEM",
++ .part = "SFP-GE-T",
++ .fixup = sfp_fixup_ignore_tx_fault,
++ }, {
+ // Lantech 8330-262D-E can operate at 2500base-X, but
+ // incorrectly report 2500MBd NRZ in their EEPROM
+ .vendor = "Lantech",
+@@ -2312,7 +2317,8 @@ static void sfp_sm_main(struct sfp *sfp,
+ * or t_start_up, so assume there is a fault.
+ */
+ sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT,
+- sfp->sm_fault_retries == N_FAULT_INIT);
++ !sfp->tx_fault_ignore &&
++ (sfp->sm_fault_retries == N_FAULT_INIT));
+ } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
+ init_done:
+ sfp->sm_phy_retries = R_PHY_RETRY;
+@@ -2535,10 +2541,12 @@ static void sfp_check_state(struct sfp *
+ mutex_lock(&sfp->st_mutex);
+ state = sfp_get_state(sfp);
+ changed = state ^ sfp->state;
+- if (sfp->tx_fault_ignore)
++ if (sfp->tx_fault_ignore) {
+ changed &= SFP_F_PRESENT | SFP_F_LOS;
+- else
++ state &= ~SFP_F_TX_FAULT;
++ } else {
+ changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
++ }
+
+ for (i = 0; i < GPIO_MAX; i++)
+ if (changed & BIT(i))
diff --git a/target/linux/generic/hack-6.1/795-backport-phylink_pcs-helpers.patch b/target/linux/generic/hack-6.1/795-backport-phylink_pcs-helpers.patch
new file mode 100644
index 0000000000..b1e7065a8b
--- /dev/null
+++ b/target/linux/generic/hack-6.1/795-backport-phylink_pcs-helpers.patch
@@ -0,0 +1,167 @@
+From 027586ae8ecacff49757ed854c020f35d24a599c Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 11 Mar 2023 03:44:41 +0000
+Subject: [PATCH] generic: backport some phylink helper functions
+
+It isn't feasible to literally backport all upstream phylink_pcs changes
+down to Linux 5.15: It's just too many patches, and many downstream
+drivers and hacks are likely to break. We are too close to branching off
+to risk this, and it's also just too much work.
+Instead just add helper functions used by modern PCS drivers while keeping
+the original functions instact as well. While this may add a kilobyte or
+two of extra kernel size, it has the advantage that we get the best of both
+worlds: None of the existing codepaths are touched, but yet we have the
+option to backport singular improvements to Ethernet drivers where needed.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -584,10 +584,37 @@ int phylink_speed_up(struct phylink *pl)
+ #define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode)
+
+ void phylink_set_port_modes(unsigned long *bits);
++
++/**
++ * phylink_get_link_timer_ns - return the PCS link timer value
++ * @interface: link &typedef phy_interface_t mode
++ *
++ * Return the PCS link timer setting in nanoseconds for the PHY @interface
++ * mode, or -EINVAL if not appropriate.
++ */
++static inline int phylink_get_link_timer_ns(phy_interface_t interface)
++{
++ switch (interface) {
++ case PHY_INTERFACE_MODE_SGMII:
++ return 1600000;
++
++ case PHY_INTERFACE_MODE_1000BASEX:
++ case PHY_INTERFACE_MODE_2500BASEX:
++ return 10000000;
++
++ default:
++ return -EINVAL;
++ }
++}
++
+ void phylink_helper_basex_speed(struct phylink_link_state *state);
+
++void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state,
++ u16 bmsr, u16 lpa);
+ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
+ struct phylink_link_state *state);
++int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface,
++ const unsigned long *advertising);
+ int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs,
+ phy_interface_t interface,
+ const unsigned long *advertising);
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -885,7 +885,6 @@ static int phylink_change_inband_advert(
+
+ return 0;
+ }
+-
+ static void phylink_mac_pcs_get_state(struct phylink *pl,
+ struct phylink_link_state *state)
+ {
+@@ -2966,6 +2965,52 @@ void phylink_mii_c22_pcs_get_state(struc
+ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_get_state);
+
+ /**
++ * phylink_mii_c22_pcs_decode_state() - Decode MAC PCS state from MII registers
++ * @state: a pointer to a &struct phylink_link_state.
++ * @bmsr: The value of the %MII_BMSR register
++ * @lpa: The value of the %MII_LPA register
++ *
++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for
++ * clause 37 negotiation and/or SGMII control.
++ *
++ * Parse the Clause 37 or Cisco SGMII link partner negotiation word into
++ * the phylink @state structure. This is suitable to be used for implementing
++ * the mac_pcs_get_state() member of the struct phylink_mac_ops structure if
++ * accessing @bmsr and @lpa cannot be done with MDIO directly.
++ */
++void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state,
++ u16 bmsr, u16 lpa)
++{
++ state->link = !!(bmsr & BMSR_LSTATUS);
++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
++ /* If there is no link or autonegotiation is disabled, the LP advertisement
++ * data is not meaningful, so don't go any further.
++ */
++ if (!state->link || !state->an_enabled)
++ return;
++
++ switch (state->interface) {
++ case PHY_INTERFACE_MODE_1000BASEX:
++ phylink_decode_c37_word(state, lpa, SPEED_1000);
++ break;
++
++ case PHY_INTERFACE_MODE_2500BASEX:
++ phylink_decode_c37_word(state, lpa, SPEED_2500);
++ break;
++
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_QSGMII:
++ phylink_decode_sgmii_word(state, lpa);
++ break;
++
++ default:
++ state->link = false;
++ break;
++ }
++}
++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_decode_state);
++
++/**
+ * phylink_mii_c22_pcs_set_advertisement() - configure the clause 37 PCS
+ * advertisement
+ * @pcs: a pointer to a &struct mdio_device.
+@@ -3037,6 +3082,46 @@ int phylink_mii_c22_pcs_set_advertisemen
+ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_set_advertisement);
+
+ /**
++ * phylink_mii_c22_pcs_encode_advertisement() - configure the clause 37 PCS
++ * advertisement
++ * @interface: the PHY interface mode being configured
++ * @advertising: the ethtool advertisement mask
++ *
++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for
++ * clause 37 negotiation and/or SGMII control.
++ *
++ * Encode the clause 37 PCS advertisement as specified by @interface and
++ * @advertising.
++ *
++ * Return: The new value for @adv, or ``-EINVAL`` if it should not be changed.
++ */
++int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface,
++ const unsigned long *advertising)
++{
++ u16 adv;
++
++ switch (interface) {
++ case PHY_INTERFACE_MODE_1000BASEX:
++ case PHY_INTERFACE_MODE_2500BASEX:
++ adv = ADVERTISE_1000XFULL;
++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
++ advertising))
++ adv |= ADVERTISE_1000XPAUSE;
++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
++ advertising))
++ adv |= ADVERTISE_1000XPSE_ASYM;
++ return adv;
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_QSGMII:
++ return 0x0001;
++ default:
++ /* Nothing to do for other modes */
++ return -EINVAL;
++ }
++}
++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_encode_advertisement);
++
++/**
+ * phylink_mii_c22_pcs_config() - configure clause 22 PCS
+ * @pcs: a pointer to a &struct mdio_device.
+ * @mode: link autonegotiation mode
diff --git a/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch b/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch
new file mode 100644
index 0000000000..0a2c82cacb
--- /dev/null
+++ b/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch
@@ -0,0 +1,162 @@
+From cc809a441d8f2924f785eb863dfa6aef47a25b0b Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 12 Aug 2014 20:49:27 +0200
+Subject: [PATCH 30/36] GPIO: add named gpio exports
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+--- a/drivers/gpio/gpiolib-of.c
++++ b/drivers/gpio/gpiolib-of.c
+@@ -19,6 +19,8 @@
+ #include <linux/pinctrl/pinctrl.h>
+ #include <linux/slab.h>
+ #include <linux/gpio/machine.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
+
+ #include "gpiolib.h"
+ #include "gpiolib-of.h"
+@@ -1059,3 +1061,72 @@ void of_gpio_dev_init(struct gpio_chip *
+ else
+ gc->of_node = gdev->dev.of_node;
+ }
++
++#ifdef CONFIG_GPIO_SYSFS
++
++static struct of_device_id gpio_export_ids[] = {
++ { .compatible = "gpio-export" },
++ { /* sentinel */ }
++};
++
++static int of_gpio_export_probe(struct platform_device *pdev)
++{
++ struct device_node *np = pdev->dev.of_node;
++ struct device_node *cnp;
++ u32 val;
++ int nb = 0;
++
++ for_each_child_of_node(np, cnp) {
++ const char *name = NULL;
++ int gpio;
++ bool dmc;
++ int max_gpio = 1;
++ int i;
++
++ of_property_read_string(cnp, "gpio-export,name", &name);
++
++ if (!name)
++ max_gpio = of_gpio_count(cnp);
++
++ for (i = 0; i < max_gpio; i++) {
++ unsigned flags = 0;
++ enum of_gpio_flags of_flags;
++
++ gpio = of_get_gpio_flags(cnp, i, &of_flags);
++ if (!gpio_is_valid(gpio))
++ return gpio;
++
++ if (of_flags == OF_GPIO_ACTIVE_LOW)
++ flags |= GPIOF_ACTIVE_LOW;
++
++ if (!of_property_read_u32(cnp, "gpio-export,output", &val))
++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
++ else
++ flags |= GPIOF_IN;
++
++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
++ continue;
++
++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
++ gpio_export_with_name(gpio, dmc, name);
++ nb++;
++ }
++ }
++
++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
++
++ return 0;
++}
++
++static struct platform_driver gpio_export_driver = {
++ .driver = {
++ .name = "gpio-export",
++ .owner = THIS_MODULE,
++ .of_match_table = of_match_ptr(gpio_export_ids),
++ },
++ .probe = of_gpio_export_probe,
++};
++
++module_platform_driver(gpio_export_driver);
++
++#endif
+--- a/include/asm-generic/gpio.h
++++ b/include/asm-generic/gpio.h
+@@ -125,6 +125,12 @@ static inline int gpio_export(unsigned g
+ return gpiod_export(gpio_to_desc(gpio), direction_may_change);
+ }
+
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name)
++{
++ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name);
++}
++
+ static inline int gpio_export_link(struct device *dev, const char *name,
+ unsigned gpio)
+ {
+--- a/include/linux/gpio/consumer.h
++++ b/include/linux/gpio/consumer.h
+@@ -712,6 +712,7 @@ static inline void devm_acpi_dev_remove_
+
+ #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
+
++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
+ int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
+ int gpiod_export_link(struct device *dev, const char *name,
+ struct gpio_desc *desc);
+@@ -719,6 +720,13 @@ void gpiod_unexport(struct gpio_desc *de
+
+ #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
+
++static inline int _gpiod_export(struct gpio_desc *desc,
++ bool direction_may_change,
++ const char *name)
++{
++ return -ENOSYS;
++}
++
+ static inline int gpiod_export(struct gpio_desc *desc,
+ bool direction_may_change)
+ {
+--- a/drivers/gpio/gpiolib-sysfs.c
++++ b/drivers/gpio/gpiolib-sysfs.c
+@@ -561,7 +561,7 @@ static struct class gpio_class = {
+ *
+ * Returns zero on success, else an error.
+ */
+-int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name)
+ {
+ struct gpio_chip *chip;
+ struct gpio_device *gdev;
+@@ -623,6 +623,8 @@ int gpiod_export(struct gpio_desc *desc,
+ offset = gpio_chip_hwgpio(desc);
+ if (chip->names && chip->names[offset])
+ ioname = chip->names[offset];
++ if (name)
++ ioname = name;
+
+ dev = device_create_with_groups(&gpio_class, &gdev->dev,
+ MKDEV(0, 0), data, gpio_groups,
+@@ -644,6 +646,12 @@ err_unlock:
+ gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ return status;
+ }
++EXPORT_SYMBOL_GPL(__gpiod_export);
++
++int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
++{
++ return __gpiod_export(desc, direction_may_change, NULL);
++}
+ EXPORT_SYMBOL_GPL(gpiod_export);
+
+ static int match_export(struct device *dev, const void *desc)
diff --git a/target/linux/generic/hack-6.1/810-bcma-ssb-fallback-sprom.patch b/target/linux/generic/hack-6.1/810-bcma-ssb-fallback-sprom.patch
new file mode 100644
index 0000000000..c581a512cb
--- /dev/null
+++ b/target/linux/generic/hack-6.1/810-bcma-ssb-fallback-sprom.patch
@@ -0,0 +1,187 @@
+From e4d708702e6c98f2111e33201a264d6788564cb2 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Fri, 12 May 2023 11:08:43 +0200
+Subject: [PATCH] ssb_sprom: add generic kernel support for Broadcom Fallback SPROMs
+
+---
+ drivers/bcma/Kconfig | 4 ++++
+ drivers/bcma/Makefile | 1 +
+ drivers/bcma/bcma_private.h | 4 ++++
+ drivers/bcma/main.c | 8 ++++++++
+ drivers/bcma/sprom.c | 23 ++++++++++++++---------
+ drivers/ssb/Kconfig | 5 +++++
+ drivers/ssb/Makefile | 1 +
+ drivers/ssb/main.c | 8 ++++++++
+ drivers/ssb/sprom.c | 12 +++++++++++-
+ drivers/ssb/ssb_private.h | 4 ++++
+ 10 files changed, 60 insertions(+), 10 deletions(-)
+
+--- a/drivers/bcma/Kconfig
++++ b/drivers/bcma/Kconfig
+@@ -18,6 +18,10 @@ config BCMA_BLOCKIO
+ bool
+ default y
+
++config BCMA_FALLBACK_SPROM
++ bool
++ default y
++
+ config BCMA_HOST_PCI_POSSIBLE
+ bool
+ depends on PCI = y
+--- a/drivers/bcma/Makefile
++++ b/drivers/bcma/Makefile
+@@ -11,6 +11,7 @@ bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE)
+ bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o
+ bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o
+ bcma-$(CONFIG_BCMA_DRIVER_GPIO) += driver_gpio.o
++bcma-$(CONFIG_BCMA_FALLBACK_SPROM) += fallback-sprom.o
+ bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o
+ bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o
+ obj-$(CONFIG_BCMA) += bcma.o
+--- a/drivers/bcma/bcma_private.h
++++ b/drivers/bcma/bcma_private.h
+@@ -38,6 +38,10 @@ int bcma_bus_resume(struct bcma_bus *bus
+ void bcma_detect_chip(struct bcma_bus *bus);
+ int bcma_bus_scan(struct bcma_bus *bus);
+
++/* fallback-sprom.c */
++int __init bcma_fbs_register(void);
++int bcma_get_fallback_sprom(struct bcma_bus *dev, struct ssb_sprom *out);
++
+ /* sprom.c */
+ int bcma_sprom_get(struct bcma_bus *bus);
+
+--- a/drivers/bcma/main.c
++++ b/drivers/bcma/main.c
+@@ -668,6 +668,14 @@ static int __init bcma_modinit(void)
+ {
+ int err;
+
++#ifdef CONFIG_BCMA_FALLBACK_SPROM
++ err = bcma_fbs_register();
++ if (err) {
++ pr_err("Fallback SPROM initialization failed\n");
++ err = 0;
++ }
++#endif /* CONFIG_BCMA_FALLBACK_SPROM */
++
+ err = bcma_init_bus_register();
+ if (err)
+ return err;
+--- a/drivers/bcma/sprom.c
++++ b/drivers/bcma/sprom.c
+@@ -51,21 +51,26 @@ static int bcma_fill_sprom_with_fallback
+ {
+ int err;
+
+- if (!get_fallback_sprom) {
++ if (get_fallback_sprom)
++ err = get_fallback_sprom(bus, out);
++
++#ifdef CONFIG_BCMA_FALLBACK_SPROM
++ if (!get_fallback_sprom || err)
++ err = bcma_get_fallback_sprom(bus, out);
++#else
++ if (!get_fallback_sprom)
+ err = -ENOENT;
+- goto fail;
+- }
++#endif /* CONFIG_BCMA_FALLBACK_SPROM */
+
+- err = get_fallback_sprom(bus, out);
+- if (err)
+- goto fail;
++ if (err) {
++ bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err);
++ return err;
++ }
+
+ bcma_debug(bus, "Using SPROM revision %d provided by platform.\n",
+ bus->sprom.revision);
++
+ return 0;
+-fail:
+- bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err);
+- return err;
+ }
+
+ /**************************************************
+--- a/drivers/ssb/Kconfig
++++ b/drivers/ssb/Kconfig
+@@ -25,6 +25,11 @@ if SSB
+ config SSB_SPROM
+ bool
+
++config SSB_FALLBACK_SPROM
++ bool
++ depends on SSB_PCIHOST
++ default y
++
+ # Support for Block-I/O. SELECT this from the driver that needs it.
+ config SSB_BLOCKIO
+ bool
+--- a/drivers/ssb/Makefile
++++ b/drivers/ssb/Makefile
+@@ -2,6 +2,7 @@
+ # core
+ ssb-y += main.o scan.o
+ ssb-$(CONFIG_SSB_EMBEDDED) += embedded.o
++ssb-$(CONFIG_SSB_FALLBACK_SPROM) += fallback-sprom.o
+ ssb-$(CONFIG_SSB_SPROM) += sprom.o
+
+ # host support
+--- a/drivers/ssb/main.c
++++ b/drivers/ssb/main.c
+@@ -1287,6 +1287,14 @@ static int __init ssb_modinit(void)
+ {
+ int err;
+
++#ifdef CONFIG_SSB_FALLBACK_SPROM
++ err = ssb_fbs_register();
++ if (err) {
++ pr_err("Fallback SPROM initialization failed\n");
++ err = 0;
++ }
++#endif /* CONFIG_SSB_FALLBACK_SPROM */
++
+ /* See the comment at the ssb_is_early_boot definition */
+ ssb_is_early_boot = 0;
+ err = bus_register(&ssb_bustype);
+--- a/drivers/ssb/sprom.c
++++ b/drivers/ssb/sprom.c
+@@ -180,10 +180,20 @@ int ssb_arch_register_fallback_sprom(int
+
+ int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out)
+ {
++ int err;
++
++ if (get_fallback_sprom)
++ err = get_fallback_sprom(bus, out);
++
++#ifdef CONFIG_SSB_FALLBACK_SPROM
++ if (!get_fallback_sprom || err)
++ err = ssb_get_fallback_sprom(bus, out);
++#else
+ if (!get_fallback_sprom)
+ return -ENOENT;
++#endif /* CONFIG_SSB_FALLBACK_SPROM */
+
+- return get_fallback_sprom(bus, out);
++ return err;
+ }
+
+ /* https://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
+--- a/drivers/ssb/ssb_private.h
++++ b/drivers/ssb/ssb_private.h
+@@ -143,6 +143,10 @@ extern int ssb_bus_scan(struct ssb_bus *
+ extern void ssb_iounmap(struct ssb_bus *ssb);
+
+
++/* fallback-sprom.c */
++int __init ssb_fbs_register(void);
++int ssb_get_fallback_sprom(struct ssb_bus *dev, struct ssb_sprom *out);
++
+ /* sprom.c */
+ extern
+ ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf,
diff --git a/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch b/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch
new file mode 100644
index 0000000000..ac502bdd16
--- /dev/null
+++ b/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch
@@ -0,0 +1,162 @@
+From 3b6115d6b57a263bdc8c9b1df273bd4a7955eead Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 8 Jul 2017 08:16:31 +0200
+Subject: debloat: add some debloat patches, strip down procfs and make O_DIRECT support optional, saves ~15K after lzma on MIPS
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/Kconfig | 3 +++
+ net/core/Makefile | 3 ++-
+ net/core/sock.c | 2 ++
+ net/ipv4/Kconfig | 1 +
+ net/netlink/Kconfig | 1 +
+ net/packet/Kconfig | 1 +
+ net/unix/Kconfig | 1 +
+ 7 files changed, 11 insertions(+), 1 deletion(-)
+
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -104,6 +104,9 @@ source "net/mptcp/Kconfig"
+
+ endif # if INET
+
++config SOCK_DIAG
++ bool
++
+ config NETWORK_SECMARK
+ bool "Security Marking"
+ help
+--- a/net/core/Makefile
++++ b/net/core/Makefile
+@@ -10,9 +10,10 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.
+
+ obj-y += dev.o dev_addr_lists.o dst.o netevent.o \
+ neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
+- sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
++ dev_ioctl.o tso.o sock_reuseport.o \
+ fib_notifier.o xdp.o flow_offload.o
+
++obj-$(CONFIG_SOCK_DIAG) += sock_diag.o
+ obj-y += net-sysfs.o
+ obj-$(CONFIG_PAGE_POOL) += page_pool.o
+ obj-$(CONFIG_PROC_FS) += net-procfs.o
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -114,6 +114,7 @@
+ #include <linux/memcontrol.h>
+ #include <linux/prefetch.h>
+ #include <linux/compat.h>
++#include <linux/cookie.h>
+
+ #include <linux/uaccess.h>
+
+@@ -143,6 +144,7 @@
+
+ static DEFINE_MUTEX(proto_list_mutex);
+ static LIST_HEAD(proto_list);
++DEFINE_COOKIE(sock_cookie);
+
+ static void sock_inuse_add(struct net *net, int val);
+
+@@ -545,6 +547,18 @@ discard_and_relse:
+ }
+ EXPORT_SYMBOL(__sk_receive_skb);
+
++u64 __sock_gen_cookie(struct sock *sk)
++{
++ while (1) {
++ u64 res = atomic64_read(&sk->sk_cookie);
++
++ if (res)
++ return res;
++ res = gen_cookie_next(&sock_cookie);
++ atomic64_cmpxchg(&sk->sk_cookie, 0, res);
++ }
++}
++
+ INDIRECT_CALLABLE_DECLARE(struct dst_entry *ip6_dst_check(struct dst_entry *,
+ u32));
+ INDIRECT_CALLABLE_DECLARE(struct dst_entry *ipv4_dst_check(struct dst_entry *,
+@@ -1983,9 +1997,11 @@ static void __sk_free(struct sock *sk)
+ if (likely(sk->sk_net_refcnt))
+ sock_inuse_add(sock_net(sk), -1);
+
++#ifdef CONFIG_SOCK_DIAG
+ if (unlikely(sk->sk_net_refcnt && sock_diag_has_destroy_listeners(sk)))
+ sock_diag_broadcast_destroy(sk);
+ else
++#endif
+ sk_destruct(sk);
+ }
+
+--- a/net/core/sock_diag.c
++++ b/net/core/sock_diag.c
+@@ -11,7 +11,6 @@
+ #include <linux/tcp.h>
+ #include <linux/workqueue.h>
+ #include <linux/nospec.h>
+-#include <linux/cookie.h>
+ #include <linux/inet_diag.h>
+ #include <linux/sock_diag.h>
+
+@@ -20,20 +19,6 @@ static int (*inet_rcv_compat)(struct sk_
+ static DEFINE_MUTEX(sock_diag_table_mutex);
+ static struct workqueue_struct *broadcast_wq;
+
+-DEFINE_COOKIE(sock_cookie);
+-
+-u64 __sock_gen_cookie(struct sock *sk)
+-{
+- while (1) {
+- u64 res = atomic64_read(&sk->sk_cookie);
+-
+- if (res)
+- return res;
+- res = gen_cookie_next(&sock_cookie);
+- atomic64_cmpxchg(&sk->sk_cookie, 0, res);
+- }
+-}
+-
+ int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie)
+ {
+ u64 res;
+--- a/net/ipv4/Kconfig
++++ b/net/ipv4/Kconfig
+@@ -424,6 +424,7 @@ config INET_TUNNEL
+
+ config INET_DIAG
+ tristate "INET: socket monitoring interface"
++ select SOCK_DIAG
+ default y
+ help
+ Support for INET (TCP, DCCP, etc) socket monitoring interface used by
+--- a/net/netlink/Kconfig
++++ b/net/netlink/Kconfig
+@@ -5,6 +5,7 @@
+
+ config NETLINK_DIAG
+ tristate "NETLINK: socket monitoring interface"
++ select SOCK_DIAG
+ default n
+ help
+ Support for NETLINK socket monitoring interface used by the ss tool.
+--- a/net/packet/Kconfig
++++ b/net/packet/Kconfig
+@@ -19,6 +19,7 @@ config PACKET
+ config PACKET_DIAG
+ tristate "Packet: sockets monitoring interface"
+ depends on PACKET
++ select SOCK_DIAG
+ default n
+ help
+ Support for PF_PACKET sockets monitoring interface used by the ss tool.
+--- a/net/unix/Kconfig
++++ b/net/unix/Kconfig
+@@ -33,6 +33,7 @@ config AF_UNIX_OOB
+ config UNIX_DIAG
+ tristate "UNIX: socket monitoring interface"
+ depends on UNIX
++ select SOCK_DIAG
+ default n
+ help
+ Support for UNIX socket monitoring interface used by the ss tool.
diff --git a/target/linux/generic/hack-6.1/902-debloat_proc.patch b/target/linux/generic/hack-6.1/902-debloat_proc.patch
new file mode 100644
index 0000000000..12e9928028
--- /dev/null
+++ b/target/linux/generic/hack-6.1/902-debloat_proc.patch
@@ -0,0 +1,408 @@
+From 9e3f1d0805b2d919904dd9a4ff0d956314cc3cba Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 8 Jul 2017 08:20:09 +0200
+Subject: debloat: procfs
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ fs/locks.c | 2 ++
+ fs/proc/Kconfig | 5 +++++
+ fs/proc/consoles.c | 3 +++
+ fs/proc/proc_tty.c | 11 ++++++++++-
+ include/net/snmp.h | 18 +++++++++++++++++-
+ ipc/msg.c | 3 +++
+ ipc/sem.c | 2 ++
+ ipc/shm.c | 2 ++
+ ipc/util.c | 3 +++
+ kernel/exec_domain.c | 2 ++
+ kernel/irq/proc.c | 9 +++++++++
+ kernel/time/timer_list.c | 2 ++
+ mm/vmalloc.c | 2 ++
+ mm/vmstat.c | 8 +++++---
+ net/8021q/vlanproc.c | 6 ++++++
+ net/core/net-procfs.c | 18 ++++++++++++------
+ net/core/sock.c | 2 ++
+ net/ipv4/fib_trie.c | 18 ++++++++++++------
+ net/ipv4/proc.c | 3 +++
+ net/ipv4/route.c | 3 +++
+ 20 files changed, 105 insertions(+), 17 deletions(-)
+
+--- a/fs/locks.c
++++ b/fs/locks.c
+@@ -2953,6 +2953,8 @@ static const struct seq_operations locks
+
+ static int __init proc_locks_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ proc_create_seq_private("locks", 0, NULL, &locks_seq_operations,
+ sizeof(struct locks_iterator), NULL);
+ return 0;
+--- a/fs/proc/Kconfig
++++ b/fs/proc/Kconfig
+@@ -100,6 +100,11 @@ config PROC_CHILDREN
+ Say Y if you are running any user-space software which takes benefit from
+ this interface. For example, rkt is such a piece of software.
+
++config PROC_STRIPPED
++ default n
++ depends on EXPERT
++ bool "Strip non-essential /proc functionality to reduce code size"
++
+ config PROC_PID_ARCH_STATUS
+ def_bool n
+ depends on PROC_FS
+--- a/fs/proc/consoles.c
++++ b/fs/proc/consoles.c
+@@ -92,6 +92,9 @@ static const struct seq_operations conso
+
+ static int __init proc_consoles_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
++
+ proc_create_seq("consoles", 0, NULL, &consoles_op);
+ return 0;
+ }
+--- a/fs/proc/proc_tty.c
++++ b/fs/proc/proc_tty.c
+@@ -133,7 +133,10 @@ static const struct seq_operations tty_d
+ void proc_tty_register_driver(struct tty_driver *driver)
+ {
+ struct proc_dir_entry *ent;
+-
++
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ if (!driver->driver_name || driver->proc_entry ||
+ !driver->ops->proc_show)
+ return;
+@@ -150,6 +153,9 @@ void proc_tty_unregister_driver(struct t
+ {
+ struct proc_dir_entry *ent;
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ ent = driver->proc_entry;
+ if (!ent)
+ return;
+@@ -164,6 +170,9 @@ void proc_tty_unregister_driver(struct t
+ */
+ void __init proc_tty_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ if (!proc_mkdir("tty", NULL))
+ return;
+ proc_mkdir("tty/ldisc", NULL); /* Preserved: it's userspace visible */
+--- a/include/net/snmp.h
++++ b/include/net/snmp.h
+@@ -124,6 +124,21 @@ struct linux_tls_mib {
+ #define DECLARE_SNMP_STAT(type, name) \
+ extern __typeof__(type) __percpu *name
+
++#ifdef CONFIG_PROC_STRIPPED
++#define __SNMP_STATS_DUMMY(mib) \
++ do { (void) mib->mibs[0]; } while(0)
++
++#define __SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib)
++#define SNMP_INC_STATS_ATOMIC_LONG(mib, field) __SNMP_STATS_DUMMY(mib)
++#define SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib)
++#define SNMP_DEC_STATS(mib, field) __SNMP_STATS_DUMMY(mib)
++#define __SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib)
++#define SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib)
++#define SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib)
++#define __SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib)
++
++#else
++
+ #define __SNMP_INC_STATS(mib, field) \
+ __this_cpu_inc(mib->mibs[field])
+
+@@ -154,8 +169,9 @@ struct linux_tls_mib {
+ __this_cpu_add(ptr[basefield##OCTETS], addend); \
+ } while (0)
+
++#endif
+
+-#if BITS_PER_LONG==32
++#if (BITS_PER_LONG==32) && !defined(CONFIG_PROC_STRIPPED)
+
+ #define __SNMP_ADD_STATS64(mib, field, addend) \
+ do { \
+--- a/ipc/msg.c
++++ b/ipc/msg.c
+@@ -1350,6 +1350,9 @@ void __init msg_init(void)
+ {
+ msg_init_ns(&init_ipc_ns);
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ ipc_init_proc_interface("sysvipc/msg",
+ " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n",
+ IPC_MSG_IDS, sysvipc_msg_proc_show);
+--- a/ipc/sem.c
++++ b/ipc/sem.c
+@@ -268,6 +268,8 @@ void sem_exit_ns(struct ipc_namespace *n
+ void __init sem_init(void)
+ {
+ sem_init_ns(&init_ipc_ns);
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
+ ipc_init_proc_interface("sysvipc/sem",
+ " key semid perms nsems uid gid cuid cgid otime ctime\n",
+ IPC_SEM_IDS, sysvipc_sem_proc_show);
+--- a/ipc/shm.c
++++ b/ipc/shm.c
+@@ -154,6 +154,8 @@ pure_initcall(ipc_ns_init);
+
+ void __init shm_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
+ ipc_init_proc_interface("sysvipc/shm",
+ #if BITS_PER_LONG <= 32
+ " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n",
+--- a/ipc/util.c
++++ b/ipc/util.c
+@@ -141,6 +141,9 @@ void __init ipc_init_proc_interface(cons
+ struct proc_dir_entry *pde;
+ struct ipc_proc_iface *iface;
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ iface = kmalloc(sizeof(*iface), GFP_KERNEL);
+ if (!iface)
+ return;
+--- a/kernel/exec_domain.c
++++ b/kernel/exec_domain.c
+@@ -29,6 +29,8 @@ static int execdomains_proc_show(struct
+
+ static int __init proc_execdomains_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ proc_create_single("execdomains", 0, NULL, execdomains_proc_show);
+ return 0;
+ }
+--- a/kernel/irq/proc.c
++++ b/kernel/irq/proc.c
+@@ -341,6 +341,9 @@ void register_irq_proc(unsigned int irq,
+ void __maybe_unused *irqp = (void *)(unsigned long) irq;
+ char name [MAX_NAMELEN];
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP))
++ return;
++
+ if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip))
+ return;
+
+@@ -394,6 +397,9 @@ void unregister_irq_proc(unsigned int ir
+ {
+ char name [MAX_NAMELEN];
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP))
++ return;
++
+ if (!root_irq_dir || !desc->dir)
+ return;
+ #ifdef CONFIG_SMP
+@@ -432,6 +438,9 @@ void init_irq_proc(void)
+ unsigned int irq;
+ struct irq_desc *desc;
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP))
++ return;
++
+ /* create /proc/irq */
+ root_irq_dir = proc_mkdir("irq", NULL);
+ if (!root_irq_dir)
+--- a/kernel/time/timer_list.c
++++ b/kernel/time/timer_list.c
+@@ -350,6 +350,8 @@ static int __init init_timer_list_procfs
+ {
+ struct proc_dir_entry *pe;
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ pe = proc_create_seq_private("timer_list", 0400, NULL, &timer_list_sops,
+ sizeof(struct timer_list_iter), NULL);
+ if (!pe)
+--- a/mm/vmalloc.c
++++ b/mm/vmalloc.c
+@@ -3964,6 +3964,8 @@ static const struct seq_operations vmall
+
+ static int __init proc_vmalloc_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ if (IS_ENABLED(CONFIG_NUMA))
+ proc_create_seq_private("vmallocinfo", 0400, NULL,
+ &vmalloc_op,
+--- a/mm/vmstat.c
++++ b/mm/vmstat.c
+@@ -2083,10 +2083,12 @@ void __init init_mm_internals(void)
+ start_shepherd_timer();
+ #endif
+ #ifdef CONFIG_PROC_FS
+- proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op);
+- proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) {
++ proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op);
++ proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op);
++ proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op);
++ }
+ proc_create_seq("vmstat", 0444, NULL, &vmstat_op);
+- proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op);
+ #endif
+ }
+
+--- a/net/8021q/vlanproc.c
++++ b/net/8021q/vlanproc.c
+@@ -93,6 +93,9 @@ void vlan_proc_cleanup(struct net *net)
+ {
+ struct vlan_net *vn = net_generic(net, vlan_net_id);
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return;
++
+ if (vn->proc_vlan_conf)
+ remove_proc_entry(name_conf, vn->proc_vlan_dir);
+
+@@ -112,6 +115,9 @@ int __net_init vlan_proc_init(struct net
+ {
+ struct vlan_net *vn = net_generic(net, vlan_net_id);
+
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
++
+ vn->proc_vlan_dir = proc_net_mkdir(net, name_root, net->proc_net);
+ if (!vn->proc_vlan_dir)
+ goto err;
+--- a/net/core/net-procfs.c
++++ b/net/core/net-procfs.c
+@@ -317,10 +317,12 @@ static int __net_init dev_proc_net_init(
+ if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops,
+ sizeof(struct seq_net_private)))
+ goto out;
+- if (!proc_create_seq("softnet_stat", 0444, net->proc_net,
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) &&
++ !proc_create_seq("softnet_stat", 0444, net->proc_net,
+ &softnet_seq_ops))
+ goto out_dev;
+- if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) &&
++ !proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
+ sizeof(struct seq_net_private)))
+ goto out_softnet;
+
+@@ -330,9 +332,11 @@ static int __net_init dev_proc_net_init(
+ out:
+ return rc;
+ out_ptype:
+- remove_proc_entry("ptype", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED))
++ remove_proc_entry("ptype", net->proc_net);
+ out_softnet:
+- remove_proc_entry("softnet_stat", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED))
++ remove_proc_entry("softnet_stat", net->proc_net);
+ out_dev:
+ remove_proc_entry("dev", net->proc_net);
+ goto out;
+@@ -342,8 +346,10 @@ static void __net_exit dev_proc_net_exit
+ {
+ wext_proc_exit(net);
+
+- remove_proc_entry("ptype", net->proc_net);
+- remove_proc_entry("softnet_stat", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) {
++ remove_proc_entry("ptype", net->proc_net);
++ remove_proc_entry("softnet_stat", net->proc_net);
++ }
+ remove_proc_entry("dev", net->proc_net);
+ }
+
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -3866,6 +3866,8 @@ static __net_initdata struct pernet_oper
+
+ static int __init proto_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
+ return register_pernet_subsys(&proto_net_ops);
+ }
+
+--- a/net/ipv4/fib_trie.c
++++ b/net/ipv4/fib_trie.c
+@@ -3024,11 +3024,13 @@ static const struct seq_operations fib_r
+
+ int __net_init fib_proc_init(struct net *net)
+ {
+- if (!proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops,
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) &&
++ !proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops,
+ sizeof(struct fib_trie_iter)))
+ goto out1;
+
+- if (!proc_create_net_single("fib_triestat", 0444, net->proc_net,
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) &&
++ !proc_create_net_single("fib_triestat", 0444, net->proc_net,
+ fib_triestat_seq_show, NULL))
+ goto out2;
+
+@@ -3039,17 +3041,21 @@ int __net_init fib_proc_init(struct net
+ return 0;
+
+ out3:
+- remove_proc_entry("fib_triestat", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED))
++ remove_proc_entry("fib_triestat", net->proc_net);
+ out2:
+- remove_proc_entry("fib_trie", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED))
++ remove_proc_entry("fib_trie", net->proc_net);
+ out1:
+ return -ENOMEM;
+ }
+
+ void __net_exit fib_proc_exit(struct net *net)
+ {
+- remove_proc_entry("fib_trie", net->proc_net);
+- remove_proc_entry("fib_triestat", net->proc_net);
++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) {
++ remove_proc_entry("fib_trie", net->proc_net);
++ remove_proc_entry("fib_triestat", net->proc_net);
++ }
+ remove_proc_entry("route", net->proc_net);
+ }
+
+--- a/net/ipv4/proc.c
++++ b/net/ipv4/proc.c
+@@ -553,5 +553,8 @@ static __net_initdata struct pernet_oper
+
+ int __init ip_misc_proc_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
++
+ return register_pernet_subsys(&ip_proc_ops);
+ }
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -387,6 +387,9 @@ static struct pernet_operations ip_rt_pr
+
+ static int __init ip_rt_proc_init(void)
+ {
++ if (IS_ENABLED(CONFIG_PROC_STRIPPED))
++ return 0;
++
+ return register_pernet_subsys(&ip_rt_proc_ops);
+ }
+
diff --git a/target/linux/generic/hack-6.1/904-debloat_dma_buf.patch b/target/linux/generic/hack-6.1/904-debloat_dma_buf.patch
new file mode 100644
index 0000000000..0291a5e9bd
--- /dev/null
+++ b/target/linux/generic/hack-6.1/904-debloat_dma_buf.patch
@@ -0,0 +1,92 @@
+From e3692cb2fcd5ba1244512a0f43b8118f65f1c375 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 8 Jul 2017 08:20:43 +0200
+Subject: debloat: dmabuf
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/base/Kconfig | 2 +-
+ drivers/dma-buf/Makefile | 10 +++++++---
+ drivers/dma-buf/dma-buf.c | 4 +++-
+ kernel/sched/core.c | 1 +
+ 4 files changed, 12 insertions(+), 5 deletions(-)
+
+--- a/drivers/base/Kconfig
++++ b/drivers/base/Kconfig
+@@ -187,7 +187,7 @@ config SOC_BUS
+ source "drivers/base/regmap/Kconfig"
+
+ config DMA_SHARED_BUFFER
+- bool
++ tristate
+ default n
+ select IRQ_WORK
+ help
+--- a/drivers/dma-buf/heaps/Makefile
++++ b/drivers/dma-buf/heaps/Makefile
+@@ -1,3 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
+-obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o
+-obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o
++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o
++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o
+--- a/drivers/dma-buf/Makefile
++++ b/drivers/dma-buf/Makefile
+@@ -1,16 +1,20 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+-obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
++obj-$(CONFIG_DMA_SHARED_BUFFER) := dma-shared-buffer.o
++
++dma-buf-objs-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
+ dma-resv.o seqno-fence.o
+-obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
+-obj-$(CONFIG_DMABUF_HEAPS) += heaps/
+-obj-$(CONFIG_SYNC_FILE) += sync_file.o
+-obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
+-obj-$(CONFIG_UDMABUF) += udmabuf.o
+-obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o
++dma-buf-objs-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
++obj-$(CONFIG_DMABUF_HEAPS) += heaps/
++dma-buf-objs-$(CONFIG_SYNC_FILE) += sync_file.o
++dma-buf-objs-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
++dma-buf-objs-$(CONFIG_UDMABUF) += udmabuf.o
++dma-buf-objs-$(CONFIG_DMABUF_SYSFS_STATS) += udmabuf.o
+
+ dmabuf_selftests-y := \
+ selftest.o \
+ st-dma-fence.o \
+ st-dma-fence-chain.o
+
+-obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
++dma-buf-objs-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
++
++dma-shared-buffer-objs := $(dma-buf-objs-y)
+--- a/drivers/dma-buf/dma-buf.c
++++ b/drivers/dma-buf/dma-buf.c
+@@ -1513,4 +1513,5 @@ static void __exit dma_buf_deinit(void)
+ kern_unmount(dma_buf_mnt);
+ dma_buf_uninit_sysfs_statistics();
+ }
+-__exitcall(dma_buf_deinit);
++module_exit(dma_buf_deinit);
++MODULE_LICENSE("GPL");
+--- a/kernel/sched/core.c
++++ b/kernel/sched/core.c
+@@ -4216,6 +4216,7 @@ int wake_up_state(struct task_struct *p,
+ {
+ return try_to_wake_up(p, state, 0);
+ }
++EXPORT_SYMBOL_GPL(wake_up_state);
+
+ /*
+ * Perform scheduler related setup for a newly forked process p.
+--- a/fs/d_path.c
++++ b/fs/d_path.c
+@@ -316,6 +316,7 @@ char *dynamic_dname(struct dentry *dentr
+ buffer += buflen - sz;
+ return memcpy(buffer, temp, sz);
+ }
++EXPORT_SYMBOL_GPL(dynamic_dname);
+
+ char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
+ {
diff --git a/target/linux/generic/hack-6.1/910-kobject_uevent.patch b/target/linux/generic/hack-6.1/910-kobject_uevent.patch
new file mode 100644
index 0000000000..c4c41ca400
--- /dev/null
+++ b/target/linux/generic/hack-6.1/910-kobject_uevent.patch
@@ -0,0 +1,32 @@
+From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 16 Jul 2017 16:56:10 +0200
+Subject: lib: add uevent_next_seqnum()
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/kobject.h | 5 +++++
+ lib/kobject_uevent.c | 37 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 42 insertions(+)
+
+--- a/lib/kobject_uevent.c
++++ b/lib/kobject_uevent.c
+@@ -179,6 +179,18 @@ out:
+ return r;
+ }
+
++u64 uevent_next_seqnum(void)
++{
++ u64 seq;
++
++ mutex_lock(&uevent_sock_mutex);
++ seq = ++uevent_seqnum;
++ mutex_unlock(&uevent_sock_mutex);
++
++ return seq;
++}
++EXPORT_SYMBOL_GPL(uevent_next_seqnum);
++
+ /**
+ * kobject_synth_uevent - send synthetic uevent with arguments
+ *
diff --git a/target/linux/generic/hack-6.1/911-kobject_add_broadcast_uevent.patch b/target/linux/generic/hack-6.1/911-kobject_add_broadcast_uevent.patch
new file mode 100644
index 0000000000..a487d55193
--- /dev/null
+++ b/target/linux/generic/hack-6.1/911-kobject_add_broadcast_uevent.patch
@@ -0,0 +1,76 @@
+From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 16 Jul 2017 16:56:10 +0200
+Subject: lib: add uevent_next_seqnum()
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/kobject.h | 5 +++++
+ lib/kobject_uevent.c | 37 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 42 insertions(+)
+
+--- a/include/linux/kobject.h
++++ b/include/linux/kobject.h
+@@ -32,6 +32,8 @@
+ #define UEVENT_NUM_ENVP 64 /* number of env pointers */
+ #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */
+
++struct sk_buff;
++
+ #ifdef CONFIG_UEVENT_HELPER
+ /* path to the userspace helper executed on an event */
+ extern char uevent_helper[];
+@@ -244,4 +246,7 @@ int kobject_synth_uevent(struct kobject
+ __printf(2, 3)
+ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...);
+
++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group,
++ gfp_t allocation);
++
+ #endif /* _KOBJECT_H_ */
+--- a/lib/kobject_uevent.c
++++ b/lib/kobject_uevent.c
+@@ -691,6 +691,43 @@ int add_uevent_var(struct kobj_uevent_en
+ EXPORT_SYMBOL_GPL(add_uevent_var);
+
+ #if defined(CONFIG_NET)
++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group,
++ gfp_t allocation)
++{
++ struct uevent_sock *ue_sk;
++ int err = 0;
++
++ /* send netlink message */
++ mutex_lock(&uevent_sock_mutex);
++ list_for_each_entry(ue_sk, &uevent_sock_list, list) {
++ struct sock *uevent_sock = ue_sk->sk;
++ struct sk_buff *skb2;
++
++ skb2 = skb_clone(skb, allocation);
++ if (!skb2)
++ break;
++
++ err = netlink_broadcast(uevent_sock, skb2, pid, group,
++ allocation);
++ if (err)
++ break;
++ }
++ mutex_unlock(&uevent_sock_mutex);
++
++ kfree_skb(skb);
++ return err;
++}
++#else
++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group,
++ gfp_t allocation)
++{
++ kfree_skb(skb);
++ return 0;
++}
++#endif
++EXPORT_SYMBOL_GPL(broadcast_uevent);
++
++#if defined(CONFIG_NET)
+ static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb,
+ struct netlink_ext_ack *extack)
+ {
diff --git a/target/linux/generic/hack-6.1/920-device_tree_cmdline.patch b/target/linux/generic/hack-6.1/920-device_tree_cmdline.patch
new file mode 100644
index 0000000000..d1f36e716e
--- /dev/null
+++ b/target/linux/generic/hack-6.1/920-device_tree_cmdline.patch
@@ -0,0 +1,21 @@
+From e08bcbbaa52fcc41f02743fd2e62a33255ce52da Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:52:28 +0200
+Subject: [PATCH] of/ftd: add device tree cmdline
+
+---
+ drivers/of/fdt.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/of/fdt.c
++++ b/drivers/of/fdt.c
+@@ -1158,6 +1158,9 @@ int __init early_init_dt_scan_chosen(uns
+ p = of_get_flat_dt_prop(node, "bootargs", &l);
+ if (p != NULL && l > 0)
+ strlcpy(data, p, min(l, COMMAND_LINE_SIZE));
++ p = of_get_flat_dt_prop(node, "bootargs-append", &l);
++ if (p != NULL && l > 0)
++ strlcat(data, p, min_t(int, strlen(data) + (int)l, COMMAND_LINE_SIZE));
+
+ /*
+ * CONFIG_CMDLINE is meant to be a default in case nothing else
diff --git a/target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch b/target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch
new file mode 100644
index 0000000000..4f4d6c7509
--- /dev/null
+++ b/target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch
@@ -0,0 +1,30 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 19 Jul 2022 06:17:48 +0200
+Subject: [PATCH] Revert "Revert "Revert "driver core: Set fw_devlink=on by
+ default"""
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit ea718c699055c8566eb64432388a04974c43b2ea.
+
+With of_platform_populate() called for MTD partitions that commit breaks
+probing devices which reference MTD in device tree.
+
+Link: https://lore.kernel.org/all/696cb2da-20b9-b3dd-46d9-de4bf91a1506@gmail.com/T/#u
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+ drivers/base/core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/base/core.c
++++ b/drivers/base/core.c
+@@ -1562,7 +1562,7 @@ static void device_links_purge(struct de
+ #define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \
+ DL_FLAG_PM_RUNTIME)
+
+-static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON;
++static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
+ static int __init fw_devlink_setup(char *arg)
+ {
+ if (!arg)
diff --git a/target/linux/generic/pending-6.1/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch b/target/linux/generic/pending-6.1/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch
new file mode 100644
index 0000000000..22f52c1d46
--- /dev/null
+++ b/target/linux/generic/pending-6.1/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch
@@ -0,0 +1,29 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Oct 2020 22:00:03 +0200
+Subject: [PATCH] compiler.h: only include asm/rwonce.h for kernel code
+
+This header file is not in uapi, which makes any user space code that includes
+linux/compiler.h to fail with the error 'asm/rwonce.h: No such file or directory'
+
+Fixes: e506ea451254 ("compiler.h: Split {READ,WRITE}_ONCE definitions out into rwonce.h")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/linux/compiler.h
++++ b/include/linux/compiler.h
+@@ -220,6 +220,8 @@ void ftrace_likely_update(struct ftrace_
+ #define function_nocfi(x) (x)
+ #endif
+
++#include <asm/rwonce.h>
++
+ #endif /* __KERNEL__ */
+
+ /*
+@@ -252,6 +254,4 @@ static inline void *offset_to_ptr(const
+ */
+ #define prevent_tail_call_optimization() mb()
+
+-#include <asm/rwonce.h>
+-
+ #endif /* __LINUX_COMPILER_H */
diff --git a/target/linux/generic/pending-6.1/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch b/target/linux/generic/pending-6.1/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch
new file mode 100644
index 0000000000..95a9656d26
--- /dev/null
+++ b/target/linux/generic/pending-6.1/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch
@@ -0,0 +1,57 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 18 Apr 2018 10:50:05 +0200
+Subject: [PATCH] MIPS: only process negative stack offsets on stack traces
+
+Fixes endless back traces in cases where the compiler emits a stack
+pointer increase in a branch delay slot (probably for some form of
+function return).
+
+[ 3.475442] BUG: MAX_STACK_TRACE_ENTRIES too low!
+[ 3.480070] turning off the locking correctness validator.
+[ 3.485521] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.14.34 #0
+[ 3.491475] Stack : 00000000 00000000 00000000 00000000 80e0fce2 00000034 00000000 00000000
+[ 3.499764] 87c3838c 80696377 8061047c 00000000 00000001 00000001 87c2d850 6534689f
+[ 3.508059] 00000000 00000000 80e10000 00000000 00000000 000000cf 0000000f 00000000
+[ 3.516353] 00000000 806a0000 00076891 00000000 00000000 00000000 ffffffff 00000000
+[ 3.524648] 806c0000 00000004 80e10000 806a0000 00000003 80690000 00000000 80700000
+[ 3.532942] ...
+[ 3.535362] Call Trace:
+[ 3.537818] [<80010a48>] show_stack+0x58/0x100
+[ 3.542207] [<804c2f78>] dump_stack+0xe8/0x170
+[ 3.546613] [<80079f90>] save_trace+0xf0/0x110
+[ 3.551010] [<8007b1ec>] mark_lock+0x33c/0x78c
+[ 3.555413] [<8007bf48>] __lock_acquire+0x2ac/0x1a08
+[ 3.560337] [<8007de60>] lock_acquire+0x64/0x8c
+[ 3.564846] [<804e1570>] _raw_spin_lock_irqsave+0x54/0x78
+[ 3.570186] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.574770] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.579257] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.583839] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.588329] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.592911] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.597401] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.601983] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.606473] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.611055] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.615545] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.620125] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.624619] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.629197] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.633691] [<801b618c>] kernfs_notify+0x94/0xac
+[ 3.638269] [<801b7b10>] sysfs_notify+0x74/0xa0
+[ 3.642763] [<801b618c>] kernfs_notify+0x94/0xac
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/arch/mips/kernel/process.c
++++ b/arch/mips/kernel/process.c
+@@ -393,6 +393,8 @@ static inline int is_sp_move_ins(union m
+
+ if (ip->i_format.opcode == addiu_op ||
+ ip->i_format.opcode == daddiu_op) {
++ if (ip->i_format.simmediate > 0)
++ return 0;
+ *frame_size = -ip->i_format.simmediate;
+ return 1;
+ }
diff --git a/target/linux/generic/pending-6.1/103-kbuild-export-SUBARCH.patch b/target/linux/generic/pending-6.1/103-kbuild-export-SUBARCH.patch
new file mode 100644
index 0000000000..120b6e4cf8
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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
+@@ -534,7 +534,7 @@ KBUILD_LDFLAGS_MODULE :=
+ KBUILD_LDFLAGS :=
+ CLANG_FLAGS :=
+
+-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG
++export ARCH SRCARCH SUBARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG
+ 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-6.1/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch b/target/linux/generic/pending-6.1/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch
new file mode 100644
index 0000000000..5216d6a706
--- /dev/null
+++ b/target/linux/generic/pending-6.1/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch
@@ -0,0 +1,32 @@
+From f7982c726e02001afc19052fe48f642dfcbc00b2 Mon Sep 17 00:00:00 2001
+From: Vincent Tremblay <vincent@vtremblay.dev>
+Date: Mon, 26 Dec 2022 21:10:37 -0500
+Subject: [PATCH 1/2] spidev: Add Silicon Labs EM3581 device compatible
+
+Add compatible string for Silicon Labs EM3581 device.
+
+Note: This patch is adapted from a patch submitted to the for-next branch (v6.3).
+
+Signed-off-by: Vincent Tremblay <vincent@vtremblay.dev>
+---
+ drivers/spi/spidev.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/spi/spidev.c
++++ b/drivers/spi/spidev.c
+@@ -691,6 +691,7 @@ static const struct spi_device_id spidev
+ { .name = "m53cpld" },
+ { .name = "spi-petra" },
+ { .name = "spi-authenta" },
++ { .name = "em3581" },
+ {},
+ };
+ MODULE_DEVICE_TABLE(spi, spidev_spi_ids);
+@@ -705,6 +706,7 @@ static const struct of_device_id spidev_
+ { .compatible = "menlo,m53cpld" },
+ { .compatible = "cisco,spi-petra" },
+ { .compatible = "micron,spi-authenta" },
++ { .compatible = "silabs,em3581" },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, spidev_dt_ids);
diff --git a/target/linux/generic/pending-6.1/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch b/target/linux/generic/pending-6.1/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch
new file mode 100644
index 0000000000..ffbed0c622
--- /dev/null
+++ b/target/linux/generic/pending-6.1/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch
@@ -0,0 +1,32 @@
+From 536581825219e97fa2ae0c4de35605d2f6311416 Mon Sep 17 00:00:00 2001
+From: Vincent Tremblay <vincent@vtremblay.dev>
+Date: Tue, 27 Dec 2022 09:00:58 -0500
+Subject: [PATCH 2/2] spidev: Add Silicon Labs SI3210 device compatible
+
+Add compatible string for Silicon Labs SI3210 device.
+
+Note: This patch is adapted from a patch submitted to the for-next branch (v6.3).
+
+Signed-off-by: Vincent Tremblay <vincent@vtremblay.dev>
+---
+ drivers/spi/spidev.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/spi/spidev.c
++++ b/drivers/spi/spidev.c
+@@ -692,6 +692,7 @@ static const struct spi_device_id spidev
+ { .name = "spi-petra" },
+ { .name = "spi-authenta" },
+ { .name = "em3581" },
++ { .name = "si3210" },
+ {},
+ };
+ MODULE_DEVICE_TABLE(spi, spidev_spi_ids);
+@@ -707,6 +708,7 @@ static const struct of_device_id spidev_
+ { .compatible = "cisco,spi-petra" },
+ { .compatible = "micron,spi-authenta" },
+ { .compatible = "silabs,em3581" },
++ { .compatible = "silabs,si3210" },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, spidev_dt_ids);
diff --git a/target/linux/generic/pending-6.1/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch b/target/linux/generic/pending-6.1/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch
new file mode 100644
index 0000000000..d6b10491f8
--- /dev/null
+++ b/target/linux/generic/pending-6.1/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch
@@ -0,0 +1,75 @@
+From bd1b9f66d5134e518419f4c4dacf1884c1616983 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 28 Apr 2022 11:13:23 +0200
+Subject: [PATCH] watchdog: max63xx_wdt: Add support for specifying WDI logic
+ via GPIO
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+On some boards is WDI logic of max6370 chip connected via GPIO.
+So extend max63xx_wdt driver to allow specifying WDI logic via GPIO.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/watchdog/max63xx_wdt.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/drivers/watchdog/max63xx_wdt.c
++++ b/drivers/watchdog/max63xx_wdt.c
+@@ -27,6 +27,7 @@
+ #include <linux/io.h>
+ #include <linux/slab.h>
+ #include <linux/property.h>
++#include <linux/gpio/consumer.h>
+
+ #define DEFAULT_HEARTBEAT 60
+ #define MAX_HEARTBEAT 60
+@@ -53,6 +54,9 @@ struct max63xx_wdt {
+ void __iomem *base;
+ spinlock_t lock;
+
++ /* GPIOs */
++ struct gpio_desc *gpio_wdi;
++
+ /* WDI and WSET bits write access routines */
+ void (*ping)(struct max63xx_wdt *wdt);
+ void (*set)(struct max63xx_wdt *wdt, u8 set);
+@@ -158,6 +162,17 @@ static const struct watchdog_info max63x
+ .identity = "max63xx Watchdog",
+ };
+
++static void max63xx_gpio_ping(struct max63xx_wdt *wdt)
++{
++ spin_lock(&wdt->lock);
++
++ gpiod_set_value(wdt->gpio_wdi, 1);
++ udelay(1);
++ gpiod_set_value(wdt->gpio_wdi, 0);
++
++ spin_unlock(&wdt->lock);
++}
++
+ static void max63xx_mmap_ping(struct max63xx_wdt *wdt)
+ {
+ u8 val;
+@@ -225,10 +240,19 @@ static int max63xx_wdt_probe(struct plat
+ return -EINVAL;
+ }
+
++ wdt->gpio_wdi = devm_gpiod_get(dev, NULL, GPIOD_FLAGS_BIT_DIR_OUT);
++ if (IS_ERR(wdt->gpio_wdi) && PTR_ERR(wdt->gpio_wdi) != -ENOENT)
++ return dev_err_probe(dev, PTR_ERR(wdt->gpio_wdi),
++ "unable to request gpio: %ld\n",
++ PTR_ERR(wdt->gpio_wdi));
++
+ err = max63xx_mmap_init(pdev, wdt);
+ if (err)
+ return err;
+
++ if (!IS_ERR(wdt->gpio_wdi))
++ wdt->ping = max63xx_gpio_ping;
++
+ platform_set_drvdata(pdev, &wdt->wdd);
+ watchdog_set_drvdata(&wdt->wdd, wdt);
+
diff --git a/target/linux/generic/pending-6.1/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch b/target/linux/generic/pending-6.1/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch
new file mode 100644
index 0000000000..ac5e3a69b8
--- /dev/null
+++ b/target/linux/generic/pending-6.1/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch
@@ -0,0 +1,82 @@
+From: Tobias Wolf <dev-NTEO@vplace.de>
+Subject: mm: Fix alloc_node_mem_map with ARCH_PFN_OFFSET calculation
+
+An rt288x (ralink) based router (Belkin F5D8235 v1) does not boot with any
+kernel beyond version 4.3 resulting in:
+
+BUG: Bad page state in process swapper pfn:086ac
+
+bisect resulted in:
+
+a1c34a3bf00af2cede839879502e12dc68491ad5 is the first bad commit
+commit a1c34a3bf00af2cede839879502e12dc68491ad5
+Author: Laura Abbott <laura@labbott.name>
+Date: Thu Nov 5 18:48:46 2015 -0800
+
+ mm: Don't offset memmap for flatmem
+
+ Srinivas Kandagatla reported bad page messages when trying to remove the
+ bottom 2MB on an ARM based IFC6410 board
+
+ BUG: Bad page state in process swapper pfn:fffa8
+ page:ef7fb500 count:0 mapcount:0 mapping: (null) index:0x0
+ flags: 0x96640253(locked|error|dirty|active|arch_1|reclaim|mlocked)
+ page dumped because: PAGE_FLAGS_CHECK_AT_FREE flag(s) set
+ bad because of flags:
+ flags: 0x200041(locked|active|mlocked)
+ Modules linked in:
+ CPU: 0 PID: 0 Comm: swapper Not tainted 3.19.0-rc3-00007-g412f9ba-dirty
+#816
+ Hardware name: Qualcomm (Flattened Device Tree)
+ unwind_backtrace
+ show_stack
+ dump_stack
+ bad_page
+ free_pages_prepare
+ free_hot_cold_page
+ __free_pages
+ free_highmem_page
+ mem_init
+ start_kernel
+ Disabling lock debugging due to kernel taint
+ [...]
+:040000 040000 2de013c372345fd471cd58f0553c9b38b0ef1cc4
+0a8156f848733dfa21e16c196dfb6c0a76290709 M mm
+
+This fix for ARM does not account ARCH_PFN_OFFSET for mem_map as later used by
+page_to_pfn anymore.
+
+The following output was generated with two hacked in printk statements:
+
+printk("before %p vs. %p or %p\n", mem_map, mem_map - offset, mem_map -
+(pgdat->node_start_pfn - ARCH_PFN_OFFSET));
+ if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
+ mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
+printk("after %p\n", mem_map);
+
+Output:
+
+[ 0.000000] before 8861b280 vs. 8861b280 or 8851b280
+[ 0.000000] after 8851b280
+
+As seen in the first line mem_map with subtraction of offset does not equal the
+mem_map after subtraction of ARCH_PFN_OFFSET.
+
+After adding the offset of ARCH_PFN_OFFSET as well to mem_map as the
+previously calculated offset is zero for the named platform it is able to boot
+4.4 and 4.9-rc7 again.
+
+Signed-off-by: Tobias Wolf <dev-NTEO@vplace.de>
+---
+
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -7620,7 +7620,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)
+- mem_map -= offset;
++ mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
+ }
+ #endif
+ }
diff --git a/target/linux/generic/pending-6.1/130-binfmt_elf-dynamically-allocate-note.data-in-parse_e.patch b/target/linux/generic/pending-6.1/130-binfmt_elf-dynamically-allocate-note.data-in-parse_e.patch
new file mode 100644
index 0000000000..f04007e982
--- /dev/null
+++ b/target/linux/generic/pending-6.1/130-binfmt_elf-dynamically-allocate-note.data-in-parse_e.patch
@@ -0,0 +1,94 @@
+From ca71e00839fcdd26f122fb6d9e97903c9fe198f7 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sat, 6 May 2023 08:08:35 +0200
+Subject: [PATCH] binfmt_elf: dynamically allocate note.data in
+ parse_elf_properties
+
+Dynamically allocate note.data in parse_elf_properties to fix
+compilation warning on some arch.
+
+On some arch note.data exceet the stack limit for a single function and
+this cause the following compilation warning:
+fs/binfmt_elf.c: In function 'parse_elf_properties.isra':
+fs/binfmt_elf.c:821:1: error: the frame size of 1040 bytes is larger than 1024 bytes [-Werror=frame-larger-than=]
+ 821 | }
+ | ^
+cc1: all warnings being treated as errors
+
+Fix this by dynamically allocating the array.
+
+Fixes: 00e19ceec80b ("ELF: Add ELF program property parsing support")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Cc: stable@vger.kernel.org # v5.8+
+---
+ fs/binfmt_elf.c | 32 +++++++++++++++++++++++---------
+ 1 file changed, 23 insertions(+), 9 deletions(-)
+
+--- a/fs/binfmt_elf.c
++++ b/fs/binfmt_elf.c
+@@ -768,7 +768,7 @@ static int parse_elf_properties(struct f
+ {
+ union {
+ struct elf_note nhdr;
+- char data[NOTE_DATA_SZ];
++ char *data;
+ } note;
+ loff_t pos;
+ ssize_t n;
+@@ -788,26 +788,38 @@ static int parse_elf_properties(struct f
+ if (phdr->p_filesz > sizeof(note))
+ return -ENOEXEC;
+
++ note.data = kcalloc(NOTE_DATA_SZ, sizeof(*note.data), GFP_KERNEL);
++ if (!note.data)
++ return -ENOMEM;
++
+ pos = phdr->p_offset;
+ n = kernel_read(f, &note, phdr->p_filesz, &pos);
+
+ BUILD_BUG_ON(sizeof(note) < sizeof(note.nhdr) + NOTE_NAME_SZ);
+- if (n < 0 || n < sizeof(note.nhdr) + NOTE_NAME_SZ)
+- return -EIO;
++ if (n < 0 || n < sizeof(note.nhdr) + NOTE_NAME_SZ) {
++ ret = -EIO;
++ goto exit;
++ }
+
+ if (note.nhdr.n_type != NT_GNU_PROPERTY_TYPE_0 ||
+ note.nhdr.n_namesz != NOTE_NAME_SZ ||
+ strncmp(note.data + sizeof(note.nhdr),
+- GNU_PROPERTY_TYPE_0_NAME, n - sizeof(note.nhdr)))
+- return -ENOEXEC;
++ GNU_PROPERTY_TYPE_0_NAME, n - sizeof(note.nhdr))) {
++ ret = -ENOEXEC;
++ goto exit;
++ }
+
+ off = round_up(sizeof(note.nhdr) + NOTE_NAME_SZ,
+ ELF_GNU_PROPERTY_ALIGN);
+- if (off > n)
+- return -ENOEXEC;
+-
+- if (note.nhdr.n_descsz > n - off)
+- return -ENOEXEC;
++ if (off > n) {
++ ret = -ENOEXEC;
++ goto exit;
++ }
++
++ if (note.nhdr.n_descsz > n - off) {
++ ret = -ENOEXEC;
++ goto exit;
++ }
+ datasz = off + note.nhdr.n_descsz;
+
+ have_prev_type = false;
+@@ -817,6 +829,8 @@ static int parse_elf_properties(struct f
+ have_prev_type = true;
+ } while (!ret);
+
++exit:
++ kfree(note.data);
+ return ret == -ENOENT ? 0 : ret;
+ }
+
diff --git a/target/linux/generic/pending-6.1/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch b/target/linux/generic/pending-6.1/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch
new file mode 100644
index 0000000000..8f40ae3ba2
--- /dev/null
+++ b/target/linux/generic/pending-6.1/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch
@@ -0,0 +1,81 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: jffs2: use .rename2 and add RENAME_WHITEOUT support
+
+It is required for renames on overlayfs
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/fs/jffs2/dir.c
++++ b/fs/jffs2/dir.c
+@@ -614,8 +614,8 @@ static int jffs2_rmdir (struct inode *di
+ return ret;
+ }
+
+-static int jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i,
+- struct dentry *dentry, umode_t mode, dev_t rdev)
++static int __jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i,
++ struct dentry *dentry, umode_t mode, dev_t rdev, bool whiteout)
+ {
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+@@ -754,7 +754,11 @@ static int jffs2_mknod (struct user_name
+ mutex_unlock(&dir_f->sem);
+ jffs2_complete_reservation(c);
+
+- d_instantiate_new(dentry, inode);
++ if (!whiteout)
++ d_instantiate_new(dentry, inode);
++ else
++ unlock_new_inode(inode);
++
+ return 0;
+
+ fail:
+@@ -762,6 +766,19 @@ static int jffs2_mknod (struct user_name
+ return ret;
+ }
+
++static int jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i,
++ struct dentry *dentry, umode_t mode, dev_t rdev)
++{
++ return __jffs2_mknod(mnt_userns, dir_i, dentry, mode, rdev, false);
++}
++
++static int jffs2_whiteout (struct user_namespace *mnt_userns, struct inode *old_dir,
++ struct dentry *old_dentry)
++{
++ return __jffs2_mknod(mnt_userns, old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE,
++ WHITEOUT_DEV, true);
++}
++
+ static int jffs2_rename (struct user_namespace *mnt_userns,
+ struct inode *old_dir_i, struct dentry *old_dentry,
+ struct inode *new_dir_i, struct dentry *new_dentry,
+@@ -773,7 +790,7 @@ static int jffs2_rename (struct user_nam
+ uint8_t type;
+ uint32_t now;
+
+- if (flags & ~RENAME_NOREPLACE)
++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT))
+ return -EINVAL;
+
+ /* The VFS will check for us and prevent trying to rename a
+@@ -839,9 +856,14 @@ static int jffs2_rename (struct user_nam
+ if (d_is_dir(old_dentry) && !victim_f)
+ inc_nlink(new_dir_i);
+
+- /* Unlink the original */
+- ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
+- old_dentry->d_name.name, old_dentry->d_name.len, NULL, now);
++ if (flags & RENAME_WHITEOUT)
++ /* Replace with whiteout */
++ ret = jffs2_whiteout(mnt_userns, old_dir_i, old_dentry);
++ else
++ /* Unlink the original */
++ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
++ old_dentry->d_name.name,
++ old_dentry->d_name.len, NULL, now);
+
+ /* We don't touch inode->i_nlink */
+
diff --git a/target/linux/generic/pending-6.1/141-jffs2-add-RENAME_EXCHANGE-support.patch b/target/linux/generic/pending-6.1/141-jffs2-add-RENAME_EXCHANGE-support.patch
new file mode 100644
index 0000000000..f58fc791d2
--- /dev/null
+++ b/target/linux/generic/pending-6.1/141-jffs2-add-RENAME_EXCHANGE-support.patch
@@ -0,0 +1,73 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: jffs2: add RENAME_EXCHANGE support
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/fs/jffs2/dir.c
++++ b/fs/jffs2/dir.c
+@@ -787,18 +787,31 @@ static int jffs2_rename (struct user_nam
+ int ret;
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
+ struct jffs2_inode_info *victim_f = NULL;
++ struct inode *fst_inode = d_inode(old_dentry);
++ struct inode *snd_inode = d_inode(new_dentry);
+ uint8_t type;
+ uint32_t now;
+
+- if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT))
++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT|RENAME_EXCHANGE))
+ return -EINVAL;
+
++ if ((flags & RENAME_EXCHANGE) && (old_dir_i != new_dir_i)) {
++ if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) {
++ inc_nlink(new_dir_i);
++ drop_nlink(old_dir_i);
++ }
++ else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) {
++ drop_nlink(new_dir_i);
++ inc_nlink(old_dir_i);
++ }
++ }
++
+ /* The VFS will check for us and prevent trying to rename a
+ * file over a directory and vice versa, but if it's a directory,
+ * the VFS can't check whether the victim is empty. The filesystem
+ * needs to do that for itself.
+ */
+- if (d_really_is_positive(new_dentry)) {
++ if (d_really_is_positive(new_dentry) && !(flags & RENAME_EXCHANGE)) {
+ victim_f = JFFS2_INODE_INFO(d_inode(new_dentry));
+ if (d_is_dir(new_dentry)) {
+ struct jffs2_full_dirent *fd;
+@@ -833,7 +846,7 @@ static int jffs2_rename (struct user_nam
+ if (ret)
+ return ret;
+
+- if (victim_f) {
++ if (victim_f && !(flags & RENAME_EXCHANGE)) {
+ /* There was a victim. Kill it off nicely */
+ if (d_is_dir(new_dentry))
+ clear_nlink(d_inode(new_dentry));
+@@ -859,6 +872,12 @@ static int jffs2_rename (struct user_nam
+ if (flags & RENAME_WHITEOUT)
+ /* Replace with whiteout */
+ ret = jffs2_whiteout(mnt_userns, old_dir_i, old_dentry);
++ else if (flags & RENAME_EXCHANGE)
++ /* Replace the original */
++ ret = jffs2_do_link(c, JFFS2_INODE_INFO(old_dir_i),
++ d_inode(new_dentry)->i_ino, type,
++ old_dentry->d_name.name, old_dentry->d_name.len,
++ now);
+ else
+ /* Unlink the original */
+ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
+@@ -890,7 +909,7 @@ static int jffs2_rename (struct user_nam
+ return ret;
+ }
+
+- if (d_is_dir(old_dentry))
++ if (d_is_dir(old_dentry) && !(flags & RENAME_EXCHANGE))
+ drop_nlink(old_dir_i);
+
+ new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now);
diff --git a/target/linux/generic/pending-6.1/142-jffs2-add-splice-ops.patch b/target/linux/generic/pending-6.1/142-jffs2-add-splice-ops.patch
new file mode 100644
index 0000000000..de847a1f5c
--- /dev/null
+++ b/target/linux/generic/pending-6.1/142-jffs2-add-splice-ops.patch
@@ -0,0 +1,20 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: jffs2: add splice ops
+
+Add splice_read using generic_file_splice_read.
+Add splice_write using iter_file_splice_write
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/fs/jffs2/file.c
++++ b/fs/jffs2/file.c
+@@ -53,6 +53,8 @@ const struct file_operations jffs2_file_
+ .open = generic_file_open,
+ .read_iter = generic_file_read_iter,
+ .write_iter = generic_file_write_iter,
++ .splice_read = generic_file_splice_read,
++ .splice_write = iter_file_splice_write,
+ .unlocked_ioctl=jffs2_ioctl,
+ .mmap = generic_file_readonly_mmap,
+ .fsync = jffs2_fsync,
diff --git a/target/linux/generic/pending-6.1/143-jffs2-reduce-stack-usage-in-jffs2_build_xattr_subsys.patch b/target/linux/generic/pending-6.1/143-jffs2-reduce-stack-usage-in-jffs2_build_xattr_subsys.patch
new file mode 100644
index 0000000000..e5a86dd29f
--- /dev/null
+++ b/target/linux/generic/pending-6.1/143-jffs2-reduce-stack-usage-in-jffs2_build_xattr_subsys.patch
@@ -0,0 +1,121 @@
+From eee53f6eb7561f516b9c4bac829ce31c48096130 Mon Sep 17 00:00:00 2001
+From: Fabian Frederick <fabf@skynet.be>
+Date: Tue, 9 May 2017 22:30:03 +0200
+Subject: [PATCH] jffs2: reduce stack usage in jffs2_build_xattr_subsystem()
+
+Use kcalloc() for allocation/flush of 128 pointers table to
+reduce stack usage.
+
+Function now returns -ENOMEM or 0 on success.
+
+stackusage
+Before:
+./fs/jffs2/xattr.c:775 jffs2_build_xattr_subsystem 1208
+dynamic,bounded
+
+After:
+./fs/jffs2/xattr.c:775 jffs2_build_xattr_subsystem 192
+dynamic,bounded
+
+Also update definition when CONFIG_JFFS2_FS_XATTR is not enabled
+
+Tested with an MTD mount point and some user set/getfattr.
+
+Many current target on OpenWRT also suffer from a compilation warning
+(that become an error with CONFIG_WERROR) with the following output:
+
+fs/jffs2/xattr.c: In function 'jffs2_build_xattr_subsystem':
+fs/jffs2/xattr.c:887:1: error: the frame size of 1088 bytes is larger than 1024 bytes [-Werror=frame-larger-than=]
+ 887 | }
+ | ^
+
+Using dynamic allocation fix this compilation warning.
+
+Fixes: c9f700f840bd ("[JFFS2][XATTR] using 'delete marker' for xdatum/xref deletion")
+Reported-by: Tim Gardner <tim.gardner@canonical.com>
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Ron Economos <re@w6rz.net>
+Reported-by: Nathan Chancellor <nathan@kernel.org>
+Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
+Signed-off-by: Fabian Frederick <fabf@skynet.be>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Cc: stable@vger.kernel.org
+---
+ fs/jffs2/build.c | 5 ++++-
+ fs/jffs2/xattr.c | 13 +++++++++----
+ fs/jffs2/xattr.h | 4 ++--
+ 3 files changed, 15 insertions(+), 7 deletions(-)
+
+--- a/fs/jffs2/build.c
++++ b/fs/jffs2/build.c
+@@ -211,7 +211,10 @@ static int jffs2_build_filesystem(struct
+ ic->scan_dents = NULL;
+ cond_resched();
+ }
+- jffs2_build_xattr_subsystem(c);
++ ret = jffs2_build_xattr_subsystem(c);
++ if (ret)
++ goto exit;
++
+ c->flags &= ~JFFS2_SB_FLAG_BUILDING;
+
+ dbg_fsbuild("FS build complete\n");
+--- a/fs/jffs2/xattr.c
++++ b/fs/jffs2/xattr.c
+@@ -772,10 +772,10 @@ void jffs2_clear_xattr_subsystem(struct
+ }
+
+ #define XREF_TMPHASH_SIZE (128)
+-void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c)
++int jffs2_build_xattr_subsystem(struct jffs2_sb_info *c)
+ {
+ struct jffs2_xattr_ref *ref, *_ref;
+- struct jffs2_xattr_ref *xref_tmphash[XREF_TMPHASH_SIZE];
++ struct jffs2_xattr_ref **xref_tmphash;
+ struct jffs2_xattr_datum *xd, *_xd;
+ struct jffs2_inode_cache *ic;
+ struct jffs2_raw_node_ref *raw;
+@@ -784,9 +784,12 @@ void jffs2_build_xattr_subsystem(struct
+
+ BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING));
+
++ xref_tmphash = kcalloc(XREF_TMPHASH_SIZE,
++ sizeof(struct jffs2_xattr_ref *), GFP_KERNEL);
++ if (!xref_tmphash)
++ return -ENOMEM;
++
+ /* Phase.1 : Merge same xref */
+- for (i=0; i < XREF_TMPHASH_SIZE; i++)
+- xref_tmphash[i] = NULL;
+ for (ref=c->xref_temp; ref; ref=_ref) {
+ struct jffs2_xattr_ref *tmp;
+
+@@ -884,6 +887,8 @@ void jffs2_build_xattr_subsystem(struct
+ "%u of xref (%u dead, %u orphan) found.\n",
+ xdatum_count, xdatum_unchecked_count, xdatum_orphan_count,
+ xref_count, xref_dead_count, xref_orphan_count);
++ kfree(xref_tmphash);
++ return 0;
+ }
+
+ struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
+--- a/fs/jffs2/xattr.h
++++ b/fs/jffs2/xattr.h
+@@ -71,7 +71,7 @@ static inline int is_xattr_ref_dead(stru
+ #ifdef CONFIG_JFFS2_FS_XATTR
+
+ extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c);
+-extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c);
++extern int jffs2_build_xattr_subsystem(struct jffs2_sb_info *c);
+ extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c);
+
+ extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
+@@ -103,7 +103,7 @@ extern ssize_t jffs2_listxattr(struct de
+ #else
+
+ #define jffs2_init_xattr_subsystem(c)
+-#define jffs2_build_xattr_subsystem(c)
++#define jffs2_build_xattr_subsystem(c) (0)
+ #define jffs2_clear_xattr_subsystem(c)
+
+ #define jffs2_xattr_do_crccheck_inode(c, ic)
diff --git a/target/linux/generic/pending-6.1/150-bridge_allow_receiption_on_disabled_port.patch b/target/linux/generic/pending-6.1/150-bridge_allow_receiption_on_disabled_port.patch
new file mode 100644
index 0000000000..9968a79699
--- /dev/null
+++ b/target/linux/generic/pending-6.1/150-bridge_allow_receiption_on_disabled_port.patch
@@ -0,0 +1,45 @@
+From: Stephen Hemminger <stephen@networkplumber.org>
+Subject: bridge: allow receiption on disabled port
+
+When an ethernet device is enslaved to a bridge, and the bridge STP
+detects loss of carrier (or operational state down), then normally
+packet receiption is blocked.
+
+This breaks control applications like WPA which maybe expecting to
+receive packets to negotiate to bring link up. The bridge needs to
+block forwarding packets from these disabled ports, but there is no
+hard requirement to not allow local packet delivery.
+
+Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+
+--- a/net/bridge/br_input.c
++++ b/net/bridge/br_input.c
+@@ -204,6 +204,9 @@ static void __br_handle_local_finish(str
+ /* note: already called with rcu_read_lock */
+ static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+ {
++ struct net_bridge_port *p = br_port_get_rcu(skb->dev);
++
++ if (p->state != BR_STATE_DISABLED)
+ __br_handle_local_finish(skb);
+
+ /* return 1 to signal the okfn() was called so it's ok to use the skb */
+@@ -369,6 +372,17 @@ static rx_handler_result_t br_handle_fra
+
+ forward:
+ switch (p->state) {
++ case BR_STATE_DISABLED:
++ if (ether_addr_equal(p->br->dev->dev_addr, dest))
++ skb->pkt_type = PACKET_HOST;
++
++ if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
++ dev_net(skb->dev), NULL, skb, skb->dev, NULL,
++ br_handle_local_finish) == 1) {
++ return RX_HANDLER_PASS;
++ }
++ break;
++
+ case BR_STATE_FORWARDING:
+ case BR_STATE_LEARNING:
+ if (ether_addr_equal(p->br->dev->dev_addr, dest))
diff --git a/target/linux/generic/pending-6.1/190-rtc-rs5c372-support_alarms_up_to_1_week.patch b/target/linux/generic/pending-6.1/190-rtc-rs5c372-support_alarms_up_to_1_week.patch
new file mode 100644
index 0000000000..13b79b5c09
--- /dev/null
+++ b/target/linux/generic/pending-6.1/190-rtc-rs5c372-support_alarms_up_to_1_week.patch
@@ -0,0 +1,94 @@
+From: Daniel González Cabanelas <dgcbueu@gmail.com>
+Subject: [PATCH 1/2] rtc: rs5c372: support alarms up to 1 week
+
+The Ricoh R2221x, R2223x, RS5C372, RV5C387A chips can handle 1 week
+alarms.
+
+Read the "wday" alarm register and convert it to a date to support up 1
+week in our driver.
+
+Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
+---
+ drivers/rtc/rtc-rs5c372.c | 48 ++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 42 insertions(+), 6 deletions(-)
+
+--- a/drivers/rtc/rtc-rs5c372.c
++++ b/drivers/rtc/rtc-rs5c372.c
+@@ -393,7 +393,9 @@ static int rs5c_read_alarm(struct device
+ {
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rs5c372 *rs5c = i2c_get_clientdata(client);
+- int status;
++ int status, wday_offs;
++ struct rtc_time rtc;
++ unsigned long alarm_secs;
+
+ status = rs5c_get_regs(rs5c);
+ if (status < 0)
+@@ -403,6 +405,30 @@ static int rs5c_read_alarm(struct device
+ t->time.tm_sec = 0;
+ t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f);
+ t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]);
++ t->time.tm_wday = ffs(rs5c->regs[RS5C_REG_ALARM_A_WDAY] & 0x7f) - 1;
++
++ /* determine the day, month and year based on alarm wday, taking as a
++ * reference the current time from the rtc
++ */
++ status = rs5c372_rtc_read_time(dev, &rtc);
++ if (status < 0)
++ return status;
++
++ wday_offs = t->time.tm_wday - rtc.tm_wday;
++ alarm_secs = mktime64(rtc.tm_year + 1900,
++ rtc.tm_mon + 1,
++ rtc.tm_mday + wday_offs,
++ t->time.tm_hour,
++ t->time.tm_min,
++ t->time.tm_sec);
++
++ if (wday_offs < 0 || (wday_offs == 0 &&
++ (t->time.tm_hour < rtc.tm_hour ||
++ (t->time.tm_hour == rtc.tm_hour &&
++ t->time.tm_min <= rtc.tm_min))))
++ alarm_secs += 7 * 86400;
++
++ rtc_time64_to_tm(alarm_secs, &t->time);
+
+ /* ... and status */
+ t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE);
+@@ -417,12 +443,20 @@ static int rs5c_set_alarm(struct device
+ struct rs5c372 *rs5c = i2c_get_clientdata(client);
+ int status, addr, i;
+ unsigned char buf[3];
++ struct rtc_time rtc_tm;
++ unsigned long rtc_secs, alarm_secs;
+
+- /* only handle up to 24 hours in the future, like RTC_ALM_SET */
+- if (t->time.tm_mday != -1
+- || t->time.tm_mon != -1
+- || t->time.tm_year != -1)
++ /* chip only can handle alarms up to one week in the future*/
++ status = rs5c372_rtc_read_time(dev, &rtc_tm);
++ if (status)
++ return status;
++ rtc_secs = rtc_tm_to_time64(&rtc_tm);
++ alarm_secs = rtc_tm_to_time64(&t->time);
++ if (alarm_secs >= rtc_secs + 7 * 86400) {
++ dev_err(dev, "%s: alarm maximum is one week in the future (%d)\n",
++ __func__, status);
+ return -EINVAL;
++ }
+
+ /* REVISIT: round up tm_sec */
+
+@@ -443,7 +477,9 @@ static int rs5c_set_alarm(struct device
+ /* set alarm */
+ buf[0] = bin2bcd(t->time.tm_min);
+ buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour);
+- buf[2] = 0x7f; /* any/all days */
++ /* each bit is the day of the week, 0x7f means all days */
++ buf[2] = (t->time.tm_wday >= 0 && t->time.tm_wday < 7) ?
++ BIT(t->time.tm_wday) : 0x7f;
+
+ for (i = 0; i < sizeof(buf); i++) {
+ addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);
diff --git a/target/linux/generic/pending-6.1/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch b/target/linux/generic/pending-6.1/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch
new file mode 100644
index 0000000000..7e9d0e66c0
--- /dev/null
+++ b/target/linux/generic/pending-6.1/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch
@@ -0,0 +1,70 @@
+From: Daniel González Cabanelas <dgcbueu@gmail.com>
+Subject: [PATCH 2/2] rtc: rs5c372: let the alarm to be used as wakeup source
+
+Currently there is no use for the interrupts on the rs5c372 RTC and the
+wakealarm isn't enabled. There are some devices like NASes which use this
+RTC to wake up from the power off state when the INTR pin is activated by
+the alarm clock.
+
+Enable the alarm and let to be used as a wakeup source.
+
+Tested on a Buffalo LS421DE NAS.
+
+Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
+---
+ drivers/rtc/rtc-rs5c372.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/rtc/rtc-rs5c372.c
++++ b/drivers/rtc/rtc-rs5c372.c
+@@ -654,6 +654,7 @@ static int rs5c372_probe(struct i2c_clie
+ int err = 0;
+ int smbus_mode = 0;
+ struct rs5c372 *rs5c372;
++ bool rs5c372_can_wakeup_device = false;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+@@ -689,6 +690,12 @@ static int rs5c372_probe(struct i2c_clie
+ else
+ rs5c372->type = id->driver_data;
+
++#ifdef CONFIG_OF
++ if(of_property_read_bool(client->dev.of_node,
++ "wakeup-source"))
++ rs5c372_can_wakeup_device = true;
++#endif
++
+ /* we read registers 0x0f then 0x00-0x0f; skip the first one */
+ rs5c372->regs = &rs5c372->buf[1];
+ rs5c372->smbus = smbus_mode;
+@@ -722,6 +729,8 @@ static int rs5c372_probe(struct i2c_clie
+ goto exit;
+ }
+
++ rs5c372->has_irq = 1;
++
+ /* if the oscillator lost power and no other software (like
+ * the bootloader) set it up, do it here.
+ *
+@@ -748,6 +757,10 @@ static int rs5c372_probe(struct i2c_clie
+ );
+
+ /* REVISIT use client->irq to register alarm irq ... */
++ if (rs5c372_can_wakeup_device) {
++ device_init_wakeup(&client->dev, true);
++ }
++
+ rs5c372->rtc = devm_rtc_device_register(&client->dev,
+ rs5c372_driver.driver.name,
+ &rs5c372_rtc_ops, THIS_MODULE);
+@@ -761,6 +774,9 @@ static int rs5c372_probe(struct i2c_clie
+ if (err)
+ goto exit;
+
++ /* the rs5c372 alarm only supports a minute accuracy */
++ rs5c372->rtc->uie_unsupported = 1;
++
+ return 0;
+
+ exit:
diff --git a/target/linux/generic/pending-6.1/203-kallsyms_uncompressed.patch b/target/linux/generic/pending-6.1/203-kallsyms_uncompressed.patch
new file mode 100644
index 0000000000..930d0ff0fe
--- /dev/null
+++ b/target/linux/generic/pending-6.1/203-kallsyms_uncompressed.patch
@@ -0,0 +1,119 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: add a config option for keeping the kallsyms table uncompressed, saving ~9kb kernel size after lzma on ar71xx
+
+[john@phrozen.org: added to my upstream queue 30.12.2016]
+lede-commit: e0e3509b5ce2ccf93d4d67ea907613f5f7ec2eed
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ init/Kconfig | 11 +++++++++++
+ kernel/kallsyms.c | 8 ++++++++
+ scripts/kallsyms.c | 12 ++++++++++++
+ scripts/link-vmlinux.sh | 4 ++++
+ 4 files changed, 35 insertions(+)
+
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -1447,6 +1447,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW
+ the unaligned access emulation.
+ see arch/parisc/kernel/unaligned.c for reference
+
++config KALLSYMS_UNCOMPRESSED
++ bool "Keep kallsyms uncompressed"
++ depends on KALLSYMS
++ help
++ Normally kallsyms contains compressed symbols (using a token table),
++ reducing the uncompressed kernel image size. Keeping the symbol table
++ uncompressed significantly improves the size of this part in compressed
++ kernel images.
++
++ Say N unless you need compressed kernel images to be small.
++
+ config HAVE_PCSPKR_PLATFORM
+ bool
+
+--- a/kernel/kallsyms.c
++++ b/kernel/kallsyms.c
+@@ -80,6 +80,11 @@ static unsigned int kallsyms_expand_symb
+ * For every byte on the compressed symbol data, copy the table
+ * entry for that byte.
+ */
++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED
++ memcpy(result, data + 1, len - 1);
++ result += len - 1;
++ len = 0;
++#endif
+ while (len) {
+ tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
+ data++;
+@@ -112,6 +117,9 @@ tail:
+ */
+ static char kallsyms_get_symbol_type(unsigned int off)
+ {
++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED
++ return kallsyms_names[off + 1];
++#endif
+ /*
+ * Get just the first code, look it up in the token table,
+ * and return the first char from this token.
+--- a/scripts/kallsyms.c
++++ b/scripts/kallsyms.c
+@@ -58,6 +58,7 @@ static struct addr_range percpu_range =
+ static struct sym_entry **table;
+ static unsigned int table_size, table_cnt;
+ static int all_symbols;
++static int uncompressed;
+ static int absolute_percpu;
+ static int base_relative;
+
+@@ -486,6 +487,9 @@ static void write_src(void)
+
+ free(markers);
+
++ if (uncompressed)
++ return;
++
+ output_label("kallsyms_token_table");
+ off = 0;
+ for (i = 0; i < 256; i++) {
+@@ -537,6 +541,9 @@ static unsigned char *find_token(unsigne
+ {
+ int i;
+
++ if (uncompressed)
++ return NULL;
++
+ for (i = 0; i < len - 1; i++) {
+ if (str[i] == token[0] && str[i+1] == token[1])
+ return &str[i];
+@@ -609,6 +616,9 @@ static void optimize_result(void)
+ {
+ int i, best;
+
++ if (uncompressed)
++ return;
++
+ /* using the '\0' symbol last allows compress_symbols to use standard
+ * fast string functions */
+ for (i = 255; i >= 0; i--) {
+@@ -773,6 +783,8 @@ int main(int argc, char **argv)
+ absolute_percpu = 1;
+ else if (strcmp(argv[i], "--base-relative") == 0)
+ base_relative = 1;
++ else if (strcmp(argv[i], "--uncompressed") == 0)
++ uncompressed = 1;
+ else
+ usage();
+ }
+--- a/scripts/link-vmlinux.sh
++++ b/scripts/link-vmlinux.sh
+@@ -257,6 +257,10 @@ kallsyms()
+ kallsymopt="${kallsymopt} --base-relative"
+ fi
+
++ if [ -n "${CONFIG_KALLSYMS_UNCOMPRESSED}" ]; then
++ kallsymopt="${kallsymopt} --uncompressed"
++ fi
++
+ info KSYMS ${2}
+ ${NM} -n ${1} | scripts/kallsyms ${kallsymopt} > ${2}
+ }
diff --git a/target/linux/generic/pending-6.1/205-backtrace_module_info.patch b/target/linux/generic/pending-6.1/205-backtrace_module_info.patch
new file mode 100644
index 0000000000..6379ce071d
--- /dev/null
+++ b/target/linux/generic/pending-6.1/205-backtrace_module_info.patch
@@ -0,0 +1,41 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: when KALLSYMS is disabled, print module address + size for matching backtrace entries
+
+[john@phrozen.org: felix will add this to his upstream queue]
+
+lede-commit 53827cdc824556cda910b23ce5030c363b8f1461
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ lib/vsprintf.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+--- a/lib/vsprintf.c
++++ b/lib/vsprintf.c
+@@ -1003,8 +1003,10 @@ char *symbol_string(char *buf, char *end
+ struct printf_spec spec, const char *fmt)
+ {
+ unsigned long value;
+-#ifdef CONFIG_KALLSYMS
+ char sym[KSYM_SYMBOL_LEN];
++#ifndef CONFIG_KALLSYMS
++ struct module *mod;
++ int len;
+ #endif
+
+ if (fmt[1] == 'R')
+@@ -1025,8 +1027,14 @@ char *symbol_string(char *buf, char *end
+
+ return string_nocheck(buf, end, sym, spec);
+ #else
+- return special_hex_number(buf, end, value, sizeof(void *));
++ len = snprintf(sym, sizeof(sym), "0x%lx", value);
++ mod = __module_address(value);
++ if (mod)
++ snprintf(sym + len, sizeof(sym) - len, " [%s@%p+0x%x]",
++ mod->name, mod->core_layout.base,
++ mod->core_layout.size);
+ #endif
++ return string(buf, end, sym, spec);
+ }
+
+ static const struct printf_spec default_str_spec = {
diff --git a/target/linux/generic/pending-6.1/240-remove-unsane-filenames-from-deps_initramfs-list.patch b/target/linux/generic/pending-6.1/240-remove-unsane-filenames-from-deps_initramfs-list.patch
new file mode 100644
index 0000000000..29cfade716
--- /dev/null
+++ b/target/linux/generic/pending-6.1/240-remove-unsane-filenames-from-deps_initramfs-list.patch
@@ -0,0 +1,30 @@
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: usr: sanitize deps_initramfs list
+
+If any filename in the intramfs dependency
+list contains a colon, that causes a kernel
+build error like this:
+
+/devel/openwrt/build_dir/linux-ar71xx_generic/linux-3.6.6/usr/Makefile:58: *** multiple target patterns. Stop.
+make[5]: *** [usr] Error 2
+
+Fix it by removing such filenames from the
+deps_initramfs list.
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ usr/Makefile | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/usr/Makefile
++++ b/usr/Makefile
+@@ -61,6 +61,8 @@ hostprogs := gen_init_cpio
+ # The dependency list is generated by gen_initramfs.sh -l
+ -include $(obj)/.initramfs_data.cpio.d
+
++deps_initramfs := $(foreach v,$(deps_initramfs),$(if $(findstring :,$(v)),,$(v)))
++
+ # do not try to update files included in initramfs
+ $(deps_initramfs): ;
+
diff --git a/target/linux/generic/pending-6.1/261-enable_wilink_platform_without_drivers.patch b/target/linux/generic/pending-6.1/261-enable_wilink_platform_without_drivers.patch
new file mode 100644
index 0000000000..cd31f9d934
--- /dev/null
+++ b/target/linux/generic/pending-6.1/261-enable_wilink_platform_without_drivers.patch
@@ -0,0 +1,20 @@
+From: Imre Kaloz <kaloz@openwrt.org>
+Subject: [PATCH] hack: net: wireless: make the wl12xx glue code available with
+ compat-wireless, too
+
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+---
+ drivers/net/wireless/ti/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/ti/Kconfig
++++ b/drivers/net/wireless/ti/Kconfig
+@@ -20,7 +20,7 @@ source "drivers/net/wireless/ti/wlcore/K
+
+ config WILINK_PLATFORM_DATA
+ bool "TI WiLink platform data"
+- depends on WLCORE_SDIO || WL1251_SDIO
++ depends on WLCORE_SDIO || WL1251_SDIO || ARCH_OMAP2PLUS
+ default y
+ help
+ Small platform data bit needed to pass data to the sdio modules.
diff --git a/target/linux/generic/pending-6.1/270-platform-mikrotik-build-bits.patch b/target/linux/generic/pending-6.1/270-platform-mikrotik-build-bits.patch
new file mode 100644
index 0000000000..99f83bb2c4
--- /dev/null
+++ b/target/linux/generic/pending-6.1/270-platform-mikrotik-build-bits.patch
@@ -0,0 +1,31 @@
+From c2deb5ef01a0ef09088832744cbace9e239a6ee0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org>
+Date: Sat, 28 Mar 2020 12:11:50 +0100
+Subject: [PATCH] generic: platform/mikrotik build bits (5.4)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds platform/mikrotik kernel build bits
+
+Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
+---
+ drivers/platform/Kconfig | 2 ++
+ drivers/platform/Makefile | 1 +
+ 2 files changed, 3 insertions(+)
+
+--- a/drivers/platform/Kconfig
++++ b/drivers/platform/Kconfig
+@@ -15,3 +15,5 @@ source "drivers/platform/mellanox/Kconfi
+ source "drivers/platform/olpc/Kconfig"
+
+ source "drivers/platform/surface/Kconfig"
++
++source "drivers/platform/mikrotik/Kconfig"
+--- a/drivers/platform/Makefile
++++ b/drivers/platform/Makefile
+@@ -10,3 +10,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/
+ obj-$(CONFIG_GOLDFISH) += goldfish/
+ obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
+ obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
++obj-$(CONFIG_MIKROTIK) += mikrotik/
diff --git a/target/linux/generic/pending-6.1/300-mips_expose_boot_raw.patch b/target/linux/generic/pending-6.1/300-mips_expose_boot_raw.patch
new file mode 100644
index 0000000000..05d024925d
--- /dev/null
+++ b/target/linux/generic/pending-6.1/300-mips_expose_boot_raw.patch
@@ -0,0 +1,40 @@
+From: Mark Miller <mark@mirell.org>
+Subject: mips: expose CONFIG_BOOT_RAW
+
+This exposes the CONFIG_BOOT_RAW symbol in Kconfig. This is needed on
+certain Broadcom chipsets running CFE in order to load the kernel.
+
+Signed-off-by: Mark Miller <mark@mirell.org>
+Acked-by: Rob Landley <rob@landley.net>
+---
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -1100,9 +1100,6 @@ config FW_ARC
+ config ARCH_MAY_HAVE_PC_FDC
+ bool
+
+-config BOOT_RAW
+- bool
+-
+ config CEVT_BCM1480
+ bool
+
+@@ -3182,6 +3179,18 @@ choice
+ bool "Extend builtin kernel arguments with bootloader arguments"
+ endchoice
+
++config BOOT_RAW
++ bool "Enable the kernel to be executed from the load address"
++ default n
++ help
++ Allow the kernel to be executed from the load address for
++ bootloaders which cannot read the ELF format. This places
++ a jump to start_kernel at the load address.
++
++ If unsure, say N.
++
++
++
+ endmenu
+
+ config LOCKDEP_SUPPORT
diff --git a/target/linux/generic/pending-6.1/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch b/target/linux/generic/pending-6.1/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch
new file mode 100644
index 0000000000..726c884027
--- /dev/null
+++ b/target/linux/generic/pending-6.1/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch
@@ -0,0 +1,71 @@
+From e6e6ef4275978823ec3a84133fc91f4ffbef5c84 Mon Sep 17 00:00:00 2001
+From: Paul Burton <paul.burton@imgtec.com>
+Date: Mon, 22 Feb 2016 18:09:44 +0000
+Subject: [PATCH] MIPS: Add barriers between dcache & icache flushes
+
+Index-based cache operations may be arbitrarily reordered by out of
+order CPUs. Thus code which writes back the dcache & then invalidates
+the icache using indexed cache ops must include a barrier between
+operating on the 2 caches in order to prevent the scenario in which:
+
+ - icache invalidation occurs.
+
+ - icache fetch occurs, due to speculation.
+
+ - dcache writeback occurs.
+
+If the above were allowed to happen then the icache would contain stale
+data. Forcing the dcache writeback to complete before the icache
+invalidation avoids this.
+
+Signed-off-by: Paul Burton <paul.burton@imgtec.com>
+Cc: James Hogan <james.hogan@imgtec.com>
+---
+ arch/mips/mm/c-r4k.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+--- a/arch/mips/mm/c-r4k.c
++++ b/arch/mips/mm/c-r4k.c
+@@ -515,6 +515,7 @@ static inline void local_r4k___flush_cac
+
+ default:
+ r4k_blast_dcache();
++ mb(); /* cache instructions may be reordered */
+ r4k_blast_icache();
+ break;
+ }
+@@ -595,8 +596,10 @@ static inline void local_r4k_flush_cache
+ if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc))
+ r4k_blast_dcache();
+ /* If executable, blast stale lines from icache */
+- if (exec)
++ if (exec) {
++ mb(); /* cache instructions may be reordered */
+ r4k_blast_icache();
++ }
+ }
+
+ static void r4k_flush_cache_range(struct vm_area_struct *vma,
+@@ -697,8 +700,13 @@ static inline void local_r4k_flush_cache
+ if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) {
+ vaddr ? r4k_blast_dcache_page(addr) :
+ r4k_blast_dcache_user_page(addr);
+- if (exec && !cpu_icache_snoops_remote_store)
++ if (exec)
++ mb(); /* cache instructions may be reordered */
++
++ if (exec && !cpu_icache_snoops_remote_store) {
+ r4k_blast_scache_page(addr);
++ mb(); /* cache instructions may be reordered */
++ }
+ }
+ if (exec) {
+ if (vaddr && cpu_has_vtag_icache && mm == current->active_mm) {
+@@ -765,6 +773,7 @@ static inline void __local_r4k_flush_ica
+ else
+ blast_dcache_range(start, end);
+ }
++ mb(); /* cache instructions may be reordered */
+ }
+
+ if (type == R4K_INDEX ||
diff --git a/target/linux/generic/pending-6.1/302-mips_no_branch_likely.patch b/target/linux/generic/pending-6.1/302-mips_no_branch_likely.patch
new file mode 100644
index 0000000000..271923fca8
--- /dev/null
+++ b/target/linux/generic/pending-6.1/302-mips_no_branch_likely.patch
@@ -0,0 +1,22 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: mips: use -mno-branch-likely for kernel and userspace
+
+saves ~11k kernel size after lzma and ~12k squashfs size in the
+
+lede-commit: 41a039f46450ffae9483d6216422098669da2900
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/mips/Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/mips/Makefile
++++ b/arch/mips/Makefile
+@@ -95,7 +95,7 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin
+ # machines may also. Since BFD is incredibly buggy with respect to
+ # crossformat linking we rely on the elf2ecoff tool for format conversion.
+ #
+-cflags-y += -G 0 -mno-abicalls -fno-pic -pipe
++cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely
+ cflags-y += -msoft-float
+ LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
+ KBUILD_AFLAGS_MODULE += -mlong-calls
diff --git a/target/linux/generic/pending-6.1/305-mips_module_reloc.patch b/target/linux/generic/pending-6.1/305-mips_module_reloc.patch
new file mode 100644
index 0000000000..bbea1f61c1
--- /dev/null
+++ b/target/linux/generic/pending-6.1/305-mips_module_reloc.patch
@@ -0,0 +1,370 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: mips: replace -mlong-calls with -mno-long-calls to make function calls faster in kernel modules to achieve this, try to
+
+lede-commit: 3b3d64743ba2a874df9d70cd19e242205b0a788c
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/mips/Makefile | 5 +
+ arch/mips/include/asm/module.h | 5 +
+ arch/mips/kernel/module.c | 279 ++++++++++++++++++++++++++++++++++++++++-
+ 3 files changed, 284 insertions(+), 5 deletions(-)
+
+--- a/arch/mips/Makefile
++++ b/arch/mips/Makefile
+@@ -98,8 +98,18 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin
+ cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely
+ cflags-y += -msoft-float
+ LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
++ifdef CONFIG_64BIT
+ KBUILD_AFLAGS_MODULE += -mlong-calls
+ KBUILD_CFLAGS_MODULE += -mlong-calls
++else
++ ifdef CONFIG_DYNAMIC_FTRACE
++ KBUILD_AFLAGS_MODULE += -mlong-calls
++ KBUILD_CFLAGS_MODULE += -mlong-calls
++ else
++ KBUILD_AFLAGS_MODULE += -mno-long-calls
++ KBUILD_CFLAGS_MODULE += -mno-long-calls
++ endif
++endif
+
+ ifeq ($(CONFIG_RELOCATABLE),y)
+ LDFLAGS_vmlinux += --emit-relocs
+--- a/arch/mips/include/asm/module.h
++++ b/arch/mips/include/asm/module.h
+@@ -12,6 +12,11 @@ struct mod_arch_specific {
+ const struct exception_table_entry *dbe_start;
+ const struct exception_table_entry *dbe_end;
+ struct mips_hi16 *r_mips_hi16_list;
++
++ void *phys_plt_tbl;
++ void *virt_plt_tbl;
++ unsigned int phys_plt_offset;
++ unsigned int virt_plt_offset;
+ };
+
+ typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */
+--- a/arch/mips/kernel/module.c
++++ b/arch/mips/kernel/module.c
+@@ -31,23 +31,261 @@ struct mips_hi16 {
+ static LIST_HEAD(dbe_list);
+ static DEFINE_SPINLOCK(dbe_lock);
+
+-#ifdef MODULE_START
++/*
++ * Get the potential max trampolines size required of the init and
++ * non-init sections. Only used if we cannot find enough contiguous
++ * physically mapped memory to put the module into.
++ */
++static unsigned int
++get_plt_size(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
++ const char *secstrings, unsigned int symindex, bool is_init)
++{
++ unsigned long ret = 0;
++ unsigned int i, j;
++ Elf_Sym *syms;
++
++ /* Everything marked ALLOC (this includes the exported symbols) */
++ for (i = 1; i < hdr->e_shnum; ++i) {
++ unsigned int info = sechdrs[i].sh_info;
++
++ if (sechdrs[i].sh_type != SHT_REL
++ && sechdrs[i].sh_type != SHT_RELA)
++ continue;
++
++ /* Not a valid relocation section? */
++ if (info >= hdr->e_shnum)
++ continue;
++
++ /* Don't bother with non-allocated sections */
++ if (!(sechdrs[info].sh_flags & SHF_ALLOC))
++ continue;
++
++ /* If it's called *.init*, and we're not init, we're
++ not interested */
++ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0)
++ != is_init)
++ continue;
++
++ syms = (Elf_Sym *) sechdrs[symindex].sh_addr;
++ if (sechdrs[i].sh_type == SHT_REL) {
++ Elf_Mips_Rel *rel = (void *) sechdrs[i].sh_addr;
++ unsigned int size = sechdrs[i].sh_size / sizeof(*rel);
++
++ for (j = 0; j < size; ++j) {
++ Elf_Sym *sym;
++
++ if (ELF_MIPS_R_TYPE(rel[j]) != R_MIPS_26)
++ continue;
++
++ sym = syms + ELF_MIPS_R_SYM(rel[j]);
++ if (!is_init && sym->st_shndx != SHN_UNDEF)
++ continue;
++
++ ret += 4 * sizeof(int);
++ }
++ } else {
++ Elf_Mips_Rela *rela = (void *) sechdrs[i].sh_addr;
++ unsigned int size = sechdrs[i].sh_size / sizeof(*rela);
++
++ for (j = 0; j < size; ++j) {
++ Elf_Sym *sym;
++
++ if (ELF_MIPS_R_TYPE(rela[j]) != R_MIPS_26)
++ continue;
++
++ sym = syms + ELF_MIPS_R_SYM(rela[j]);
++ if (!is_init && sym->st_shndx != SHN_UNDEF)
++ continue;
++
++ ret += 4 * sizeof(int);
++ }
++ }
++ }
++
++ return ret;
++}
++
++#ifndef MODULE_START
++static void *alloc_phys(unsigned long size)
++{
++ unsigned order;
++ struct page *page;
++ struct page *p;
++
++ size = PAGE_ALIGN(size);
++ order = get_order(size);
++
++ page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN |
++ __GFP_THISNODE, order);
++ if (!page)
++ return NULL;
++
++ split_page(page, order);
++
++ /* mark all pages except for the last one */
++ for (p = page; p + 1 < page + (size >> PAGE_SHIFT); ++p)
++ set_bit(PG_owner_priv_1, &p->flags);
++
++ for (p = page + (size >> PAGE_SHIFT); p < page + (1 << order); ++p)
++ __free_page(p);
++
++ return page_address(page);
++}
++#endif
++
++static void free_phys(void *ptr)
++{
++ struct page *page;
++ bool free;
++
++ page = virt_to_page(ptr);
++ do {
++ free = test_and_clear_bit(PG_owner_priv_1, &page->flags);
++ __free_page(page);
++ page++;
++ } while (free);
++}
++
++
+ void *module_alloc(unsigned long size)
+ {
++#ifdef MODULE_START
+ return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END,
+ GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE,
+ __builtin_return_address(0));
++#else
++ void *ptr;
++
++ if (size == 0)
++ return NULL;
++
++ ptr = alloc_phys(size);
++
++ /* If we failed to allocate physically contiguous memory,
++ * fall back to regular vmalloc. The module loader code will
++ * create jump tables to handle long jumps */
++ if (!ptr)
++ return vmalloc(size);
++
++ return ptr;
++#endif
+ }
++
++static inline bool is_phys_addr(void *ptr)
++{
++#ifdef CONFIG_64BIT
++ return (KSEGX((unsigned long)ptr) == CKSEG0);
++#else
++ return (KSEGX(ptr) == KSEG0);
+ #endif
++}
++
++/* Free memory returned from module_alloc */
++void module_memfree(void *module_region)
++{
++ if (is_phys_addr(module_region))
++ free_phys(module_region);
++ else
++ vfree(module_region);
++}
++
++static void *__module_alloc(int size, bool phys)
++{
++ void *ptr;
++
++ if (phys)
++ ptr = kmalloc(size, GFP_KERNEL);
++ else
++ ptr = vmalloc(size);
++ return ptr;
++}
++
++static void __module_free(void *ptr)
++{
++ if (is_phys_addr(ptr))
++ kfree(ptr);
++ else
++ vfree(ptr);
++}
++
++int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
++ char *secstrings, struct module *mod)
++{
++ unsigned int symindex = 0;
++ unsigned int core_size, init_size;
++ int i;
++
++ mod->arch.phys_plt_offset = 0;
++ mod->arch.virt_plt_offset = 0;
++ mod->arch.phys_plt_tbl = NULL;
++ mod->arch.virt_plt_tbl = NULL;
++
++ if (IS_ENABLED(CONFIG_64BIT))
++ return 0;
++
++ for (i = 1; i < hdr->e_shnum; i++)
++ if (sechdrs[i].sh_type == SHT_SYMTAB)
++ symindex = i;
++
++ core_size = get_plt_size(hdr, sechdrs, secstrings, symindex, false);
++ init_size = get_plt_size(hdr, sechdrs, secstrings, symindex, true);
++
++ if ((core_size + init_size) == 0)
++ return 0;
++
++ mod->arch.phys_plt_tbl = __module_alloc(core_size + init_size, 1);
++ if (!mod->arch.phys_plt_tbl)
++ return -ENOMEM;
++
++ mod->arch.virt_plt_tbl = __module_alloc(core_size + init_size, 0);
++ if (!mod->arch.virt_plt_tbl) {
++ __module_free(mod->arch.phys_plt_tbl);
++ mod->arch.phys_plt_tbl = NULL;
++ return -ENOMEM;
++ }
++
++ return 0;
++}
+
+ static void apply_r_mips_32(u32 *location, u32 base, Elf_Addr v)
+ {
+ *location = base + v;
+ }
+
++static Elf_Addr add_plt_entry_to(unsigned *plt_offset,
++ void *start, Elf_Addr v)
++{
++ unsigned *tramp = start + *plt_offset;
++ *plt_offset += 4 * sizeof(int);
++
++ /* adjust carry for addiu */
++ if (v & 0x00008000)
++ v += 0x10000;
++
++ tramp[0] = 0x3c190000 | (v >> 16); /* lui t9, hi16 */
++ tramp[1] = 0x27390000 | (v & 0xffff); /* addiu t9, t9, lo16 */
++ tramp[2] = 0x03200008; /* jr t9 */
++ tramp[3] = 0x00000000; /* nop */
++
++ return (Elf_Addr) tramp;
++}
++
++static Elf_Addr add_plt_entry(struct module *me, void *location, Elf_Addr v)
++{
++ if (is_phys_addr(location))
++ return add_plt_entry_to(&me->arch.phys_plt_offset,
++ me->arch.phys_plt_tbl, v);
++ else
++ return add_plt_entry_to(&me->arch.virt_plt_offset,
++ me->arch.virt_plt_tbl, v);
++
++}
++
+ static int apply_r_mips_26(struct module *me, u32 *location, u32 base,
+ Elf_Addr v)
+ {
++ u32 ofs = base & 0x03ffffff;
++
+ if (v % 4) {
+ pr_err("module %s: dangerous R_MIPS_26 relocation\n",
+ me->name);
+@@ -55,13 +293,17 @@ static int apply_r_mips_26(struct module
+ }
+
+ if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
+- pr_err("module %s: relocation overflow\n",
+- me->name);
+- return -ENOEXEC;
++ v = add_plt_entry(me, location, v + (ofs << 2));
++ if (!v) {
++ pr_err("module %s: relocation overflow\n",
++ me->name);
++ return -ENOEXEC;
++ }
++ ofs = 0;
+ }
+
+ *location = (*location & ~0x03ffffff) |
+- ((base + (v >> 2)) & 0x03ffffff);
++ ((ofs + (v >> 2)) & 0x03ffffff);
+
+ return 0;
+ }
+@@ -441,9 +683,36 @@ int module_finalize(const Elf_Ehdr *hdr,
+ list_add(&me->arch.dbe_list, &dbe_list);
+ spin_unlock_irq(&dbe_lock);
+ }
++
++ /* Get rid of the fixup trampoline if we're running the module
++ * from physically mapped address space */
++ if (me->arch.phys_plt_offset == 0) {
++ __module_free(me->arch.phys_plt_tbl);
++ me->arch.phys_plt_tbl = NULL;
++ }
++ if (me->arch.virt_plt_offset == 0) {
++ __module_free(me->arch.virt_plt_tbl);
++ me->arch.virt_plt_tbl = NULL;
++ }
++
+ return 0;
+ }
+
++void module_arch_freeing_init(struct module *mod)
++{
++ if (mod->state == MODULE_STATE_LIVE)
++ return;
++
++ if (mod->arch.phys_plt_tbl) {
++ __module_free(mod->arch.phys_plt_tbl);
++ mod->arch.phys_plt_tbl = NULL;
++ }
++ if (mod->arch.virt_plt_tbl) {
++ __module_free(mod->arch.virt_plt_tbl);
++ mod->arch.virt_plt_tbl = NULL;
++ }
++}
++
+ void module_arch_cleanup(struct module *mod)
+ {
+ spin_lock_irq(&dbe_lock);
diff --git a/target/linux/generic/pending-6.1/307-mips_highmem_offset.patch b/target/linux/generic/pending-6.1/307-mips_highmem_offset.patch
new file mode 100644
index 0000000000..0529b0c5c8
--- /dev/null
+++ b/target/linux/generic/pending-6.1/307-mips_highmem_offset.patch
@@ -0,0 +1,19 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: adjust mips highmem offset to avoid the need for -mlong-calls on systems with >256M RAM
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/mips/include/asm/mach-generic/spaces.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/mips/include/asm/mach-generic/spaces.h
++++ b/arch/mips/include/asm/mach-generic/spaces.h
+@@ -46,7 +46,7 @@
+ * Memory above this physical address will be considered highmem.
+ */
+ #ifndef HIGHMEM_START
+-#define HIGHMEM_START _AC(0x20000000, UL)
++#define HIGHMEM_START _AC(0x10000000, UL)
+ #endif
+
+ #endif /* CONFIG_32BIT */
diff --git a/target/linux/generic/pending-6.1/308-mips32r2_tune.patch b/target/linux/generic/pending-6.1/308-mips32r2_tune.patch
new file mode 100644
index 0000000000..ef92a5dfb6
--- /dev/null
+++ b/target/linux/generic/pending-6.1/308-mips32r2_tune.patch
@@ -0,0 +1,22 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: add -mtune=34kc to MIPS CFLAGS when building for mips32r2
+
+This provides a good tradeoff across at least 24Kc-74Kc, while also
+producing smaller code.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/mips/Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/mips/Makefile
++++ b/arch/mips/Makefile
+@@ -175,7 +175,7 @@ cflags-$(CONFIG_CPU_VR41XX) += -march=r4
+ cflags-$(CONFIG_CPU_R4X00) += -march=r4600 -Wa,--trap
+ cflags-$(CONFIG_CPU_TX49XX) += -march=r4600 -Wa,--trap
+ cflags-$(CONFIG_CPU_MIPS32_R1) += -march=mips32 -Wa,--trap
+-cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -Wa,--trap
++cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -mtune=34kc -Wa,--trap
+ 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-6.1/310-arm_module_unresolved_weak_sym.patch b/target/linux/generic/pending-6.1/310-arm_module_unresolved_weak_sym.patch
new file mode 100644
index 0000000000..191dc6ac3c
--- /dev/null
+++ b/target/linux/generic/pending-6.1/310-arm_module_unresolved_weak_sym.patch
@@ -0,0 +1,22 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: fix errors in unresolved weak symbols on arm
+
+lede-commit: 570699d4838a907c3ef9f2819bf19eb72997b32f
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ arch/arm/kernel/module.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/arm/kernel/module.c
++++ b/arch/arm/kernel/module.c
+@@ -105,6 +105,10 @@ apply_relocate(Elf32_Shdr *sechdrs, cons
+ return -ENOEXEC;
+ }
+
++ if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) &&
++ ELF_ST_BIND(sym->st_info) == STB_WEAK)
++ continue;
++
+ loc = dstsec->sh_addr + rel->r_offset;
+
+ switch (ELF32_R_TYPE(rel->r_info)) {
diff --git a/target/linux/generic/pending-6.1/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch b/target/linux/generic/pending-6.1/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
new file mode 100644
index 0000000000..3f553b28b3
--- /dev/null
+++ b/target/linux/generic/pending-6.1/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
@@ -0,0 +1,282 @@
+From: Yousong Zhou <yszhou4tech@gmail.com>
+Subject: MIPS: kexec: Accept command line parameters from userspace.
+
+Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
+---
+ arch/mips/kernel/machine_kexec.c | 153 +++++++++++++++++++++++++++++++-----
+ arch/mips/kernel/machine_kexec.h | 20 +++++
+ arch/mips/kernel/relocate_kernel.S | 21 +++--
+ 3 files changed, 167 insertions(+), 27 deletions(-)
+ create mode 100644 arch/mips/kernel/machine_kexec.h
+
+--- a/arch/mips/kernel/machine_kexec.c
++++ b/arch/mips/kernel/machine_kexec.c
+@@ -9,14 +9,11 @@
+ #include <linux/delay.h>
+ #include <linux/libfdt.h>
+
++#include <asm/bootinfo.h>
+ #include <asm/cacheflush.h>
+ #include <asm/page.h>
+-
+-extern const unsigned char relocate_new_kernel[];
+-extern const size_t relocate_new_kernel_size;
+-
+-extern unsigned long kexec_start_address;
+-extern unsigned long kexec_indirection_page;
++#include <linux/uaccess.h>
++#include "machine_kexec.h"
+
+ static unsigned long reboot_code_buffer;
+
+@@ -30,6 +27,101 @@ void (*_crash_smp_send_stop)(void) = NUL
+ void (*_machine_kexec_shutdown)(void) = NULL;
+ void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
+
++static void machine_kexec_print_args(void)
++{
++ unsigned long argc = (int)kexec_args[0];
++ int i;
++
++ pr_info("kexec_args[0] (argc): %lu\n", argc);
++ pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
++ pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
++ pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
++
++ for (i = 0; i < argc; i++) {
++ pr_info("kexec_argv[%d] = %p, %s\n",
++ i, kexec_argv[i], kexec_argv[i]);
++ }
++}
++
++static void machine_kexec_init_argv(struct kimage *image)
++{
++ void __user *buf = NULL;
++ size_t bufsz;
++ size_t size;
++ int i;
++
++ bufsz = 0;
++ for (i = 0; i < image->nr_segments; i++) {
++ struct kexec_segment *seg;
++
++ seg = &image->segment[i];
++ if (seg->bufsz < 6)
++ continue;
++
++ if (strncmp((char *) seg->buf, "kexec ", 6))
++ continue;
++
++ buf = seg->buf;
++ bufsz = seg->bufsz;
++ break;
++ }
++
++ if (!buf)
++ return;
++
++ size = KEXEC_COMMAND_LINE_SIZE;
++ size = min(size, bufsz);
++ if (size < bufsz)
++ pr_warn("kexec command line truncated to %zd bytes\n", size);
++
++ /* Copy to kernel space */
++ if (copy_from_user(kexec_argv_buf, buf, size))
++ pr_warn("kexec command line copy to kernel space failed\n");
++
++ kexec_argv_buf[size - 1] = 0;
++}
++
++static void machine_kexec_parse_argv(struct kimage *image)
++{
++ char *reboot_code_buffer;
++ int reloc_delta;
++ char *ptr;
++ int argc;
++ int i;
++
++ ptr = kexec_argv_buf;
++ argc = 0;
++
++ /*
++ * convert command line string to array of parameters
++ * (as bootloader does).
++ */
++ while (ptr && *ptr && (KEXEC_MAX_ARGC > argc)) {
++ if (*ptr == ' ') {
++ *ptr++ = '\0';
++ continue;
++ }
++
++ kexec_argv[argc++] = ptr;
++ ptr = strchr(ptr, ' ');
++ }
++
++ if (!argc)
++ return;
++
++ kexec_args[0] = argc;
++ kexec_args[1] = (unsigned long)kexec_argv;
++ kexec_args[2] = 0;
++ kexec_args[3] = 0;
++
++ reboot_code_buffer = page_address(image->control_code_page);
++ reloc_delta = reboot_code_buffer - (char *)kexec_relocate_new_kernel;
++
++ kexec_args[1] += reloc_delta;
++ for (i = 0; i < argc; i++)
++ kexec_argv[i] += reloc_delta;
++}
++
+ static void kexec_image_info(const struct kimage *kimage)
+ {
+ unsigned long i;
+@@ -99,6 +191,18 @@ machine_kexec_prepare(struct kimage *kim
+ #endif
+
+ kexec_image_info(kimage);
++ /*
++ * Whenever arguments passed from kexec-tools, Init the arguments as
++ * the original ones to try avoiding booting failure.
++ */
++
++ kexec_args[0] = fw_arg0;
++ kexec_args[1] = fw_arg1;
++ kexec_args[2] = fw_arg2;
++ kexec_args[3] = fw_arg3;
++
++ machine_kexec_init_argv(kimage);
++ machine_kexec_parse_argv(kimage);
+
+ if (_machine_kexec_prepare)
+ return _machine_kexec_prepare(kimage);
+@@ -161,7 +265,7 @@ machine_crash_shutdown(struct pt_regs *r
+ void kexec_nonboot_cpu_jump(void)
+ {
+ local_flush_icache_range((unsigned long)relocated_kexec_smp_wait,
+- reboot_code_buffer + relocate_new_kernel_size);
++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE);
+
+ relocated_kexec_smp_wait(NULL);
+ }
+@@ -199,7 +303,7 @@ void kexec_reboot(void)
+ * machine_kexec() CPU.
+ */
+ local_flush_icache_range(reboot_code_buffer,
+- reboot_code_buffer + relocate_new_kernel_size);
++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE);
+
+ do_kexec = (void *)reboot_code_buffer;
+ do_kexec();
+@@ -212,10 +316,12 @@ machine_kexec(struct kimage *image)
+ unsigned long *ptr;
+
+ reboot_code_buffer =
+- (unsigned long)page_address(image->control_code_page);
++ (unsigned long)page_address(image->control_code_page);
++ pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer);
+
+ kexec_start_address =
+ (unsigned long) phys_to_virt(image->start);
++ pr_info("kexec_start_address = %p\n", (void *)kexec_start_address);
+
+ if (image->type == KEXEC_TYPE_DEFAULT) {
+ kexec_indirection_page =
+@@ -223,9 +329,19 @@ machine_kexec(struct kimage *image)
+ } else {
+ kexec_indirection_page = (unsigned long)&image->head;
+ }
++ pr_info("kexec_indirection_page = %p\n", (void *)kexec_indirection_page);
+
+- memcpy((void*)reboot_code_buffer, relocate_new_kernel,
+- relocate_new_kernel_size);
++ pr_info("Where is memcpy: %p\n", memcpy);
++ pr_info("kexec_relocate_new_kernel = %p, kexec_relocate_new_kernel_end = %p\n",
++ (void *)kexec_relocate_new_kernel, &kexec_relocate_new_kernel_end);
++ pr_info("Copy %lu bytes from %p to %p\n", KEXEC_RELOCATE_NEW_KERNEL_SIZE,
++ (void *)kexec_relocate_new_kernel, (void *)reboot_code_buffer);
++ memcpy((void*)reboot_code_buffer, kexec_relocate_new_kernel,
++ KEXEC_RELOCATE_NEW_KERNEL_SIZE);
++
++ pr_info("Before _print_args().\n");
++ machine_kexec_print_args();
++ pr_info("Before eval loop.\n");
+
+ /*
+ * The generic kexec code builds a page list with physical
+@@ -256,7 +372,7 @@ machine_kexec(struct kimage *image)
+ #ifdef CONFIG_SMP
+ /* All secondary cpus now may jump to kexec_wait cycle */
+ relocated_kexec_smp_wait = reboot_code_buffer +
+- (void *)(kexec_smp_wait - relocate_new_kernel);
++ (void *)(kexec_smp_wait - kexec_relocate_new_kernel);
+ smp_wmb();
+ atomic_set(&kexec_ready_to_reboot, 1);
+ #endif
+--- /dev/null
++++ b/arch/mips/kernel/machine_kexec.h
+@@ -0,0 +1,20 @@
++#ifndef _MACHINE_KEXEC_H
++#define _MACHINE_KEXEC_H
++
++#ifndef __ASSEMBLY__
++extern const unsigned char kexec_relocate_new_kernel[];
++extern unsigned long kexec_relocate_new_kernel_end;
++extern unsigned long kexec_start_address;
++extern unsigned long kexec_indirection_page;
++
++extern char kexec_argv_buf[];
++extern char *kexec_argv[];
++
++#define KEXEC_RELOCATE_NEW_KERNEL_SIZE ((unsigned long)&kexec_relocate_new_kernel_end - (unsigned long)kexec_relocate_new_kernel)
++#endif /* !__ASSEMBLY__ */
++
++#define KEXEC_COMMAND_LINE_SIZE 256
++#define KEXEC_ARGV_SIZE (KEXEC_COMMAND_LINE_SIZE / 16)
++#define KEXEC_MAX_ARGC (KEXEC_ARGV_SIZE / sizeof(long))
++
++#endif
+--- a/arch/mips/kernel/relocate_kernel.S
++++ b/arch/mips/kernel/relocate_kernel.S
+@@ -10,10 +10,11 @@
+ #include <asm/mipsregs.h>
+ #include <asm/stackframe.h>
+ #include <asm/addrspace.h>
++#include "machine_kexec.h"
+
+ #include <kernel-entry-init.h>
+
+-LEAF(relocate_new_kernel)
++LEAF(kexec_relocate_new_kernel)
+ PTR_L a0, arg0
+ PTR_L a1, arg1
+ PTR_L a2, arg2
+@@ -98,7 +99,7 @@ done:
+ #endif
+ /* jump to kexec_start_address */
+ j s1
+- END(relocate_new_kernel)
++ END(kexec_relocate_new_kernel)
+
+ #ifdef CONFIG_SMP
+ /*
+@@ -177,8 +178,15 @@ EXPORT(kexec_indirection_page)
+ PTR_WD 0
+ .size kexec_indirection_page, PTRSIZE
+
+-relocate_new_kernel_end:
++kexec_argv_buf:
++ EXPORT(kexec_argv_buf)
++ .skip KEXEC_COMMAND_LINE_SIZE
++ .size kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE
++
++kexec_argv:
++ EXPORT(kexec_argv)
++ .skip KEXEC_ARGV_SIZE
++ .size kexec_argv, KEXEC_ARGV_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:
++ EXPORT(kexec_relocate_new_kernel_end)
diff --git a/target/linux/generic/pending-6.1/332-arc-add-OWRTDTB-section.patch b/target/linux/generic/pending-6.1/332-arc-add-OWRTDTB-section.patch
new file mode 100644
index 0000000000..30158cf399
--- /dev/null
+++ b/target/linux/generic/pending-6.1/332-arc-add-OWRTDTB-section.patch
@@ -0,0 +1,84 @@
+From bb0c3b0175240bf152fd7c644821a0cf9f77c37c Mon Sep 17 00:00:00 2001
+From: Evgeniy Didin <Evgeniy.Didin@synopsys.com>
+Date: Fri, 15 Mar 2019 18:53:38 +0300
+Subject: [PATCH] arc add OWRTDTB section
+
+This change allows OpenWRT to patch resulting kernel binary with
+external .dtb.
+
+That allows us to re-use exactky the same vmlinux on different boards
+given its ARC core configurations match (at least cache line sizes etc).
+
+""patch-dtb" searches for ASCII "OWRTDTB:" strign and copies external
+.dtb right after it, keeping the string in place.
+
+Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
+Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
+Signed-off-by: Evgeniy Didin <Evgeniy.Didin@synopsys.com>
+---
+ arch/arc/kernel/head.S | 10 ++++++++++
+ arch/arc/kernel/setup.c | 4 +++-
+ arch/arc/kernel/vmlinux.lds.S | 13 +++++++++++++
+ 3 files changed, 26 insertions(+), 1 deletion(-)
+
+--- a/arch/arc/kernel/head.S
++++ b/arch/arc/kernel/head.S
+@@ -88,6 +88,16 @@
+ DSP_EARLY_INIT
+ .endm
+
++ ; Here "patch-dtb" will embed external .dtb
++ ; Note "patch-dtb" searches for ASCII "OWRTDTB:" string
++ ; and pastes .dtb right after it, hense the string precedes
++ ; __image_dtb symbol.
++ .section .owrt, "aw",@progbits
++ .ascii "OWRTDTB:"
++ENTRY(__image_dtb)
++ .fill 0x4000
++END(__image_dtb)
++
+ .section .init.text, "ax",@progbits
+
+ ;----------------------------------------------------------------
+--- a/arch/arc/kernel/setup.c
++++ b/arch/arc/kernel/setup.c
+@@ -495,6 +495,8 @@ static inline bool uboot_arg_invalid(uns
+ /* We always pass 0 as magic from U-boot */
+ #define UBOOT_MAGIC_VALUE 0
+
++extern struct boot_param_header __image_dtb;
++
+ void __init handle_uboot_args(void)
+ {
+ bool use_embedded_dtb = true;
+@@ -533,7 +535,7 @@ void __init handle_uboot_args(void)
+ ignore_uboot_args:
+
+ if (use_embedded_dtb) {
+- machine_desc = setup_machine_fdt(__dtb_start);
++ machine_desc = setup_machine_fdt(&__image_dtb);
+ if (!machine_desc)
+ panic("Embedded DT invalid\n");
+ }
+--- a/arch/arc/kernel/vmlinux.lds.S
++++ b/arch/arc/kernel/vmlinux.lds.S
+@@ -27,6 +27,19 @@ SECTIONS
+
+ . = CONFIG_LINUX_LINK_BASE;
+
++ /*
++ * In OpenWRT we want to patch built binary embedding .dtb of choice.
++ * This is implemented with "patch-dtb" utility which searches for
++ * "OWRTDTB:" string in first 16k of image and if it is found
++ * copies .dtb right after mentioned string.
++ *
++ * Note: "OWRTDTB:" won't be overwritten with .dtb, .dtb will follow it.
++ */
++ .owrt : {
++ *(.owrt)
++ . = ALIGN(PAGE_SIZE);
++ }
++
+ _int_vec_base_lds = .;
+ .vector : {
+ *(.vector)
diff --git a/target/linux/generic/pending-6.1/333-arc-enable-unaligned-access-in-kernel-mode.patch b/target/linux/generic/pending-6.1/333-arc-enable-unaligned-access-in-kernel-mode.patch
new file mode 100644
index 0000000000..1848a84cc4
--- /dev/null
+++ b/target/linux/generic/pending-6.1/333-arc-enable-unaligned-access-in-kernel-mode.patch
@@ -0,0 +1,24 @@
+From: Alexey Brodkin <abrodkin@synopsys.com>
+Subject: arc: enable unaligned access in kernel mode
+
+This enables misaligned access handling even in kernel mode.
+Some wireless drivers (ath9k-htc and mt7601u) use misaligned accesses
+here and there and to cope with that without fixing stuff in the drivers
+we're just gracefully handling it on ARC.
+
+Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
+---
+ arch/arc/kernel/unaligned.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arc/kernel/unaligned.c
++++ b/arch/arc/kernel/unaligned.c
+@@ -202,7 +202,7 @@ int misaligned_fixup(unsigned long addre
+ char buf[TASK_COMM_LEN];
+
+ /* handle user mode only and only if enabled by sysadmin */
+- if (!user_mode(regs) || !unaligned_enabled)
++ if (!unaligned_enabled)
+ return 1;
+
+ if (no_unaligned_warning) {
diff --git a/target/linux/generic/pending-6.1/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch b/target/linux/generic/pending-6.1/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch
new file mode 100644
index 0000000000..082b122cb4
--- /dev/null
+++ b/target/linux/generic/pending-6.1/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch
@@ -0,0 +1,25 @@
+From 66770a004afe10df11d3902e16eaa0c2c39436bb Mon Sep 17 00:00:00 2001
+From: Pawel Dembicki <paweldembicki@gmail.com>
+Date: Fri, 24 May 2019 17:56:19 +0200
+Subject: [PATCH] powerpc: Enable kernel XZ compression option on PPC_85xx
+
+Enable kernel XZ compression option on PPC_85xx. Tested with
+simpleImage on TP-Link TL-WDR4900 (Freescale P1014 processor).
+
+Suggested-by: Christian Lamparter <chunkeey@gmail.com>
+Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
+---
+ arch/powerpc/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/powerpc/Kconfig
++++ b/arch/powerpc/Kconfig
+@@ -221,7 +221,7 @@ config PPC
+ select HAVE_KERNEL_GZIP
+ select HAVE_KERNEL_LZMA if DEFAULT_UIMAGE
+ select HAVE_KERNEL_LZO if DEFAULT_UIMAGE
+- select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x
++ select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x || PPC_85xx
+ select HAVE_KPROBES
+ select HAVE_KPROBES_ON_FTRACE
+ select HAVE_KRETPROBES
diff --git a/target/linux/generic/pending-6.1/351-irqchip-bcm-6345-l1-request-memory-region.patch b/target/linux/generic/pending-6.1/351-irqchip-bcm-6345-l1-request-memory-region.patch
new file mode 100644
index 0000000000..9fad4f62c6
--- /dev/null
+++ b/target/linux/generic/pending-6.1/351-irqchip-bcm-6345-l1-request-memory-region.patch
@@ -0,0 +1,113 @@
+From patchwork Thu Mar 16 19:28:33 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
+ <noltari@gmail.com>
+X-Patchwork-Id: 13178238
+Return-Path: <linux-mips-owner@vger.kernel.org>
+X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
+ aws-us-west-2-korg-lkml-1.web.codeaurora.org
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by smtp.lore.kernel.org (Postfix) with ESMTP id 5EF2AC6FD19
+ for <linux-mips@archiver.kernel.org>; Thu, 16 Mar 2023 19:28:43 +0000 (UTC)
+Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
+ id S230076AbjCPT2l (ORCPT <rfc822;linux-mips@archiver.kernel.org>);
+ Thu, 16 Mar 2023 15:28:41 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56412 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S230039AbjCPT2k (ORCPT
+ <rfc822;linux-mips@vger.kernel.org>); Thu, 16 Mar 2023 15:28:40 -0400
+Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com
+ [IPv6:2a00:1450:4864:20::42f])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7259B7D9F;
+ Thu, 16 Mar 2023 12:28:38 -0700 (PDT)
+Received: by mail-wr1-x42f.google.com with SMTP id y14so2539231wrq.4;
+ Thu, 16 Mar 2023 12:28:38 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=gmail.com; s=20210112; t=1678994917;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:from:to:cc:subject:date
+ :message-id:reply-to;
+ bh=j8afldfRZftLeVmekmQfoh01jVdumsVP7nkKoPaU3Q0=;
+ b=FzMRr5ekh/fDiJqTlezNj6nLjzvn5z92FtYeB8MquVSMB8PuvarccnyqAzsXiccf+v
+ uwRFIomnTWNLGVjzc1xrB2hGiCKD3jBo5n1u8p/yEV6rpolbxVjfM7eTHXyAHXGXz7ZJ
+ TPeVbWfAlxiSD6+BPtXr/efehcdI64fIoL6G/U1WHNMo01Tzr/Obf3y5tug17N0fGcXg
+ CH6E5a2HguZUtwrm26LcK9IOV/7xEx5eIE1cOvTLMxPbGWaZwEjjP16HylJr06xRLhaf
+ RpiYBT3mXwwuOx0jLOhqavY/2kZ9GVbZRWMMwZrZv9xNO13SBwc1VUVgD4k3FntnSk7Z
+ AaOQ==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20210112; t=1678994917;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
+ :subject:date:message-id:reply-to;
+ bh=j8afldfRZftLeVmekmQfoh01jVdumsVP7nkKoPaU3Q0=;
+ b=OaA5DMgqalrfqO5iOtmmxFPsH90MkN7l4EJpyVnzuiO1Wd6rSCpqPOR7xpxZno8OPP
+ tdfm4vzn9Ie4AUDbFKDTUlPG+tgkmIruo3K9C0VnY9DD2PRZMEYBbWaJKU1otqKt0NKu
+ IAAHNvxvQvCESKzbXFLYwWbRKFScOSMGmGBTDfgThz51A18Ff1hJy/BmnuZk7M2TLgHO
+ wQpy9t7oeB/Hkxl41y46emLc/nESsvwvAG/fx/zPzCe9UiaQLrdZq+BKeOwSBedktzK5
+ U/ZTfgzU2UGSI67aGRqqGnI0uXq+MAJMK18qzM0VByxj6W+AXJ6BJr5P0quljeQ8upSg
+ bEUg==
+X-Gm-Message-State: AO0yUKWnqTlccBDnqwCSRdqOBGc2FyfiLy1Tg7EjPENlISpzXuDYwW/R
+ lJSI06rrfq+Vel/SigfpGJI=
+X-Google-Smtp-Source:
+ AK7set/jYfYl9ttVzIXJO+ZQVfa6cE/yOsP8fx4teiTmGNNWyVlIJRzMAlF3IUGqRAXAmY3hAabIuQ==
+X-Received: by 2002:a5d:40ce:0:b0:2cd:ceab:df1a with SMTP id
+ b14-20020a5d40ce000000b002cdceabdf1amr381006wrq.32.1678994916642;
+ Thu, 16 Mar 2023 12:28:36 -0700 (PDT)
+Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
+ [79.146.124.255])
+ by smtp.gmail.com with ESMTPSA id
+ l10-20020a5d4bca000000b002cfea3c49d5sm180041wrt.52.2023.03.16.12.28.35
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Thu, 16 Mar 2023 12:28:35 -0700 (PDT)
+From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+To: f.fainelli@gmail.com, jonas.gorski@gmail.com,
+ bcm-kernel-feedback-list@broadcom.com, tglx@linutronix.de,
+ maz@kernel.org, linux-mips@vger.kernel.org,
+ linux-kernel@vger.kernel.org
+Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+Subject: [PATCH v2] irqchip/bcm-6345-l1: request memory region
+Date: Thu, 16 Mar 2023 20:28:33 +0100
+Message-Id: <20230316192833.1603149-1-noltari@gmail.com>
+X-Mailer: git-send-email 2.30.2
+In-Reply-To: <20230316180701.783785-1-noltari@gmail.com>
+References: <20230316180701.783785-1-noltari@gmail.com>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <linux-mips.vger.kernel.org>
+X-Mailing-List: linux-mips@vger.kernel.org
+
+Request memory region in order to display it in /proc/iomem.
+Also stop printing the MMIO address since it just displays (ptrval).
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ v2: request memory region and stop displaying MMIO address.
+
+ drivers/irqchip/irq-bcm6345-l1.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/irqchip/irq-bcm6345-l1.c
++++ b/drivers/irqchip/irq-bcm6345-l1.c
+@@ -261,6 +261,9 @@ static int __init bcm6345_l1_init_one(st
+ if (!cpu->map_base)
+ return -ENOMEM;
+
++ if (!request_mem_region(res.start, sz, res.name))
++ pr_err("failed to request intc memory");
++
+ for (i = 0; i < n_words; i++) {
+ cpu->enable_cache[i] = 0;
+ __raw_writel(0, cpu->map_base + reg_enable(intc, i));
+@@ -339,8 +342,7 @@ static int __init bcm6345_l1_of_init(str
+ for_each_cpu(idx, &intc->cpumask) {
+ struct bcm6345_l1_cpu *cpu = intc->cpus[idx];
+
+- pr_info(" CPU%u at MMIO 0x%p (irq = %d)\n", idx,
+- cpu->map_base, cpu->parent_irq);
++ pr_info(" CPU%u (irq = %d)\n", idx, cpu->parent_irq);
+ }
+
+ return 0;
diff --git a/target/linux/generic/pending-6.1/400-mtd-mtdsplit-support.patch b/target/linux/generic/pending-6.1/400-mtd-mtdsplit-support.patch
new file mode 100644
index 0000000000..46ef15d127
--- /dev/null
+++ b/target/linux/generic/pending-6.1/400-mtd-mtdsplit-support.patch
@@ -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
+Subject: [PATCH] mtd: mtdsplit support
+
+---
+ drivers/mtd/Kconfig | 19 ++++
+ drivers/mtd/Makefile | 2 +
+ drivers/mtd/mtdpart.c | 169 ++++++++++++++++++++++++++++-----
+ include/linux/mtd/mtd.h | 25 +++++
+ include/linux/mtd/partitions.h | 7 ++
+ 5 files changed, 197 insertions(+), 25 deletions(-)
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -12,6 +12,25 @@ menuconfig MTD
+
+ if MTD
+
++menu "OpenWrt specific MTD options"
++
++config MTD_ROOTFS_ROOT_DEV
++ bool "Automatically set 'rootfs' partition to be root filesystem"
++ default y
++
++config MTD_SPLIT_FIRMWARE
++ bool "Automatically split firmware partition for kernel+rootfs"
++ default y
++
++config MTD_SPLIT_FIRMWARE_NAME
++ string "Firmware partition name"
++ depends on MTD_SPLIT_FIRMWARE
++ default "firmware"
++
++source "drivers/mtd/mtdsplit/Kconfig"
++
++endmenu
++
+ config MTD_TESTS
+ tristate "MTD tests support (DANGEROUS)"
+ depends on m
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc
+
+ obj-y += parsers/
+
++obj-$(CONFIG_MTD_SPLIT) += mtdsplit/
++
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
+ obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -15,11 +15,13 @@
+ #include <linux/kmod.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/magic.h>
+ #include <linux/err.h>
+ #include <linux/of.h>
+ #include <linux/of_platform.h>
+
+ #include "mtdcore.h"
++#include "mtdsplit/mtdsplit.h"
+
+ /*
+ * MTD methods which simply translate the effective address and pass through
+@@ -236,6 +238,147 @@ static int mtd_add_partition_attrs(struc
+ return ret;
+ }
+
++static DEFINE_SPINLOCK(part_parser_lock);
++static LIST_HEAD(part_parsers);
++
++static struct mtd_part_parser *mtd_part_parser_get(const char *name)
++{
++ struct mtd_part_parser *p, *ret = NULL;
++
++ spin_lock(&part_parser_lock);
++
++ list_for_each_entry(p, &part_parsers, list)
++ if (!strcmp(p->name, name) && try_module_get(p->owner)) {
++ ret = p;
++ break;
++ }
++
++ spin_unlock(&part_parser_lock);
++
++ return ret;
++}
++
++static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
++{
++ module_put(p->owner);
++}
++
++static struct mtd_part_parser *
++get_partition_parser_by_type(enum mtd_parser_type type,
++ struct mtd_part_parser *start)
++{
++ struct mtd_part_parser *p, *ret = NULL;
++
++ spin_lock(&part_parser_lock);
++
++ p = list_prepare_entry(start, &part_parsers, list);
++ if (start)
++ mtd_part_parser_put(start);
++
++ list_for_each_entry_continue(p, &part_parsers, list) {
++ if (p->type == type && try_module_get(p->owner)) {
++ ret = p;
++ break;
++ }
++ }
++
++ spin_unlock(&part_parser_lock);
++
++ return ret;
++}
++
++static int parse_mtd_partitions_by_type(struct mtd_info *master,
++ enum mtd_parser_type type,
++ const struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ struct mtd_part_parser *prev = NULL;
++ int ret = 0;
++
++ while (1) {
++ struct mtd_part_parser *parser;
++
++ parser = get_partition_parser_by_type(type, prev);
++ if (!parser)
++ break;
++
++ ret = (*parser->parse_fn)(master, pparts, data);
++
++ if (ret > 0) {
++ mtd_part_parser_put(parser);
++ printk(KERN_NOTICE
++ "%d %s partitions found on MTD device %s\n",
++ ret, parser->name, master->name);
++ break;
++ }
++
++ prev = parser;
++ }
++
++ return ret;
++}
++
++static int
++run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
++{
++ struct mtd_partition *parts;
++ int nr_parts;
++ int i;
++
++ nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
++ NULL);
++ if (nr_parts <= 0)
++ return nr_parts;
++
++ if (WARN_ON(!parts))
++ return 0;
++
++ for (i = 0; i < nr_parts; i++) {
++ /* adjust partition offsets */
++ parts[i].offset += child->part.offset;
++
++ mtd_add_partition(child->parent,
++ parts[i].name,
++ parts[i].offset,
++ parts[i].size);
++ }
++
++ kfree(parts);
++
++ return nr_parts;
++}
++
++#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
++#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME
++#else
++#define SPLIT_FIRMWARE_NAME "unused"
++#endif
++
++static void split_firmware(struct mtd_info *master, struct mtd_info *part)
++{
++ run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
++}
++
++static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
++{
++ static int rootfs_found = 0;
++
++ if (rootfs_found)
++ return;
++
++ 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;
++ }
++
++ if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
++ !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
++ !of_find_property(mtd_get_of_node(part), "compatible", NULL))
++ split_firmware(master, part);
++}
++
+ int mtd_add_partition(struct mtd_info *parent, const char *name,
+ long long offset, long long length)
+ {
+@@ -274,6 +417,7 @@ int mtd_add_partition(struct mtd_info *p
+ if (ret)
+ goto err_remove_part;
+
++ mtd_partition_split(parent, child);
+ mtd_add_partition_attrs(child);
+
+ return 0;
+@@ -422,6 +566,7 @@ int add_mtd_partitions(struct mtd_info *
+ goto err_del_partitions;
+ }
+
++ mtd_partition_split(master, child);
+ mtd_add_partition_attrs(child);
+
+ /* Look for subpartitions */
+@@ -438,31 +583,6 @@ err_del_partitions:
+ return ret;
+ }
+
+-static DEFINE_SPINLOCK(part_parser_lock);
+-static LIST_HEAD(part_parsers);
+-
+-static struct mtd_part_parser *mtd_part_parser_get(const char *name)
+-{
+- struct mtd_part_parser *p, *ret = NULL;
+-
+- spin_lock(&part_parser_lock);
+-
+- list_for_each_entry(p, &part_parsers, list)
+- if (!strcmp(p->name, name) && try_module_get(p->owner)) {
+- ret = p;
+- break;
+- }
+-
+- spin_unlock(&part_parser_lock);
+-
+- return ret;
+-}
+-
+-static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
+-{
+- module_put(p->owner);
+-}
+-
+ /*
+ * Many partition parsers just expected the core to kfree() all their data in
+ * one chunk. Do that by default.
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -620,6 +620,24 @@ static inline void mtd_align_erase_req(s
+ req->len += mtd->erasesize - mod;
+ }
+
++static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
++{
++ if (mtd_mod_by_eb(sz, mtd) == 0)
++ return sz;
++
++ /* Round up to next erase block */
++ return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
++}
++
++static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
++{
++ if (mtd_mod_by_eb(sz, mtd) == 0)
++ return sz;
++
++ /* Round down to the start of the current erase block */
++ return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
++}
++
+ static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
+ {
+ if (mtd->writesize_shift)
+@@ -693,6 +711,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);
+
++static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
++{
++ if (!mtd_is_partition(mtd))
++ return 0;
++
++ return mtd->part.offset;
++}
+
+ struct mtd_notifier {
+ void (*add)(struct mtd_info *mtd);
+--- a/include/linux/mtd/partitions.h
++++ b/include/linux/mtd/partitions.h
+@@ -75,6 +75,12 @@ struct mtd_part_parser_data {
+ * Functions dealing with the various ways of partitioning the space
+ */
+
++enum mtd_parser_type {
++ MTD_PARSER_TYPE_DEVICE = 0,
++ MTD_PARSER_TYPE_ROOTFS,
++ MTD_PARSER_TYPE_FIRMWARE,
++};
++
+ struct mtd_part_parser {
+ struct list_head list;
+ struct module *owner;
+@@ -83,6 +89,7 @@ struct mtd_part_parser {
+ int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
+ struct mtd_part_parser_data *);
+ void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
++ enum mtd_parser_type type;
+ };
+
+ /* Container for passing around a set of parsed partitions */
diff --git a/target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch
new file mode 100644
index 0000000000..d12bc9c3d5
--- /dev/null
+++ b/target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch
@@ -0,0 +1,245 @@
+From acacdac272927ae1d96e0bca51eb82899671eaea Mon Sep 17 00:00:00 2001
+From: John Thomson <git@johnthomson.fastmail.com.au>
+Date: Fri, 25 Dec 2020 18:50:08 +1000
+Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Do not prevent writing to mtd partitions where a partition boundary sits
+on a minor erasesize boundary.
+This addresses a FIXME that has been present since the start of the
+linux git history:
+/* Doesn't start on a boundary of major erase size */
+/* FIXME: Let it be writable if it is on a boundary of
+ * _minor_ erase size though */
+
+Allow a uniform erase region spi-nor device to be configured
+to use the non-uniform erase regions code path for an erase with:
+CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
+
+On supporting hardware (SECT_4K: majority of current SPI-NOR device)
+provide the facility for an erase to use the least number
+of SPI-NOR operations, as well as access to 4K erase without
+requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+
+Introduce erasesize_minor to the mtd struct,
+the smallest erasesize supported by the device
+
+On existing devices, this is useful where write support is wanted
+for data on a 4K partition, such as some u-boot-env partitions,
+or RouterBoot soft_config, while still netting the performance
+benefits of using 64K sectors
+
+Performance:
+time mtd erase firmware
+OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length
+
+Without this patch
+MTD_SPI_NOR_USE_4K_SECTORS=y |n
+real 2m 11.66s |0m 50.86s
+user 0m 0.00s |0m 0.00s
+sys 1m 56.20s |0m 50.80s
+
+With this patch
+MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y
+real 0m 51.68s |0m 50.85s |2m 12.89s
+user 0m 0.00s |0m 0.00s |0m 0.01s
+sys 0m 46.94s |0m 50.38s |2m 12.46s
+
+Signed-off-by: John Thomson <git@johnthomson.fastmail.com.au>
+Signed-off-by: Thibaut VARÈNE <hacks+kernel@slashdirt.org>
+
+---
+
+checkpatch does not like the printk(KERN_WARNING
+these should be changed separately beforehand?
+
+Changes v1 -> v2:
+Added mtdcore sysfs for erasesize_minor
+Removed finding minor erasesize for variable erase regions device,
+as untested and no responses regarding it.
+Moved IF_ENABLED for SPINOR variable erase to guard setting
+erasesize_minor in spi-nor/core.c
+Removed setting erasesize to minor where partition boundaries require
+minor erase to be writable
+Simplified minor boundary check by relying on minor being a factor of
+major
+
+Changes RFC -> v1:
+Fix uninitialized variable smatch warning
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
+---
+ drivers/mtd/mtdcore.c | 10 ++++++++++
+ drivers/mtd/mtdpart.c | 35 +++++++++++++++++++++++++----------
+ drivers/mtd/spi-nor/Kconfig | 10 ++++++++++
+ drivers/mtd/spi-nor/core.c | 11 +++++++++--
+ include/linux/mtd/mtd.h | 2 ++
+ 5 files changed, 56 insertions(+), 12 deletions(-)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -169,6 +169,15 @@ static ssize_t mtd_erasesize_show(struct
+ }
+ MTD_DEVICE_ATTR_RO(erasesize);
+
++static ssize_t mtd_erasesize_minor_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct mtd_info *mtd = dev_get_drvdata(dev);
++
++ return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize_minor);
++}
++MTD_DEVICE_ATTR_RO(erasesize_minor);
++
+ static ssize_t mtd_writesize_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+ {
+@@ -314,6 +323,7 @@ static struct attribute *mtd_attrs[] = {
+ &dev_attr_flags.attr,
+ &dev_attr_size.attr,
+ &dev_attr_erasesize.attr,
++ &dev_attr_erasesize_minor.attr,
+ &dev_attr_writesize.attr,
+ &dev_attr_subpagesize.attr,
+ &dev_attr_oobsize.attr,
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -41,6 +41,7 @@ static struct mtd_info *allocate_partiti
+ struct mtd_info *master = mtd_get_master(parent);
+ int wr_alignment = (parent->flags & MTD_NO_ERASE) ?
+ master->writesize : master->erasesize;
++ int wr_alignment_minor = 0;
+ u64 parent_size = mtd_is_partition(parent) ?
+ parent->part.size : parent->size;
+ struct mtd_info *child;
+@@ -165,6 +166,7 @@ static struct mtd_info *allocate_partiti
+ } else {
+ /* Single erase size */
+ child->erasesize = master->erasesize;
++ child->erasesize_minor = master->erasesize_minor;
+ }
+
+ /*
+@@ -172,26 +174,39 @@ static struct mtd_info *allocate_partiti
+ * exposes several regions with different erasesize. Adjust
+ * wr_alignment accordingly.
+ */
+- if (!(child->flags & MTD_NO_ERASE))
++ if (!(child->flags & MTD_NO_ERASE)) {
+ wr_alignment = child->erasesize;
++ wr_alignment_minor = child->erasesize_minor;
++ }
+
+ tmp = mtd_get_master_ofs(child, 0);
+ remainder = do_div(tmp, wr_alignment);
+ if ((child->flags & MTD_WRITEABLE) && remainder) {
+- /* Doesn't start on a boundary of major erase size */
+- /* FIXME: Let it be writable if it is on a boundary of
+- * _minor_ erase size though */
+- child->flags &= ~MTD_WRITEABLE;
+- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
+- part->name);
++ if (wr_alignment_minor) {
++ /* rely on minor being a factor of major erasesize */
++ tmp = remainder;
++ remainder = do_div(tmp, wr_alignment_minor);
++ }
++ if (remainder) {
++ child->flags &= ~MTD_WRITEABLE;
++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
++ part->name);
++ }
+ }
+
+ tmp = mtd_get_master_ofs(child, 0) + child->part.size;
+ remainder = do_div(tmp, wr_alignment);
+ if ((child->flags & MTD_WRITEABLE) && remainder) {
+- child->flags &= ~MTD_WRITEABLE;
+- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
+- part->name);
++ if (wr_alignment_minor) {
++ tmp = remainder;
++ remainder = do_div(tmp, wr_alignment_minor);
++ }
++
++ if (remainder) {
++ child->flags &= ~MTD_WRITEABLE;
++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
++ part->name);
++ }
+ }
+
+ child->size = child->part.size;
+--- a/drivers/mtd/spi-nor/Kconfig
++++ b/drivers/mtd/spi-nor/Kconfig
+@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR
+
+ if MTD_SPI_NOR
+
++config MTD_SPI_NOR_USE_VARIABLE_ERASE
++ bool "Disable uniform_erase to allow use of all hardware supported erasesizes"
++ depends on !MTD_SPI_NOR_USE_4K_SECTORS
++ default n
++ help
++ Allow mixed use of all hardware supported erasesizes,
++ by forcing spi_nor to use the multiple eraseregions code path.
++ For example: A 68K erase will use one 64K erase, and one 4K erase
++ on supporting hardware.
++
+ config MTD_SPI_NOR_USE_4K_SECTORS
+ bool "Use small 4096 B erase sectors"
+ default y
+--- a/drivers/mtd/spi-nor/core.c
++++ b/drivers/mtd/spi-nor/core.c
+@@ -1271,6 +1271,8 @@ static u8 spi_nor_convert_3to4_erase(u8
+
+ static bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
+ {
++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE))
++ return false;
+ return !!nor->params->erase_map.uniform_erase_type;
+ }
+
+@@ -2400,6 +2402,7 @@ static int spi_nor_select_erase(struct s
+ {
+ struct spi_nor_erase_map *map = &nor->params->erase_map;
+ const struct spi_nor_erase_type *erase = NULL;
++ const struct spi_nor_erase_type *erase_minor = NULL;
+ struct mtd_info *mtd = &nor->mtd;
+ u32 wanted_size = nor->info->sector_size;
+ int i;
+@@ -2432,8 +2435,9 @@ static int spi_nor_select_erase(struct s
+ */
+ for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
+ if (map->erase_type[i].size) {
+- erase = &map->erase_type[i];
+- break;
++ if (!erase)
++ erase = &map->erase_type[i];
++ erase_minor = &map->erase_type[i];
+ }
+ }
+
+@@ -2441,6 +2445,9 @@ static int spi_nor_select_erase(struct s
+ return -EINVAL;
+
+ mtd->erasesize = erase->size;
++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) &&
++ erase_minor && erase_minor->size < erase->size)
++ mtd->erasesize_minor = erase_minor->size;
+ return 0;
+ }
+
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -250,6 +250,8 @@ struct mtd_info {
+ * information below if they desire
+ */
+ uint32_t erasesize;
++ /* "Minor" (smallest) erase size supported by the whole device */
++ uint32_t erasesize_minor;
+ /* Minimal writable flash unit size. In case of NOR flash it is 1 (even
+ * though individual bits can be cleared), in case of NAND flash it is
+ * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
diff --git a/target/linux/generic/pending-6.1/420-mtd-redboot_space.patch b/target/linux/generic/pending-6.1/420-mtd-redboot_space.patch
new file mode 100644
index 0000000000..5518ea71dd
--- /dev/null
+++ b/target/linux/generic/pending-6.1/420-mtd-redboot_space.patch
@@ -0,0 +1,41 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: add patch for including unpartitioned space in the rootfs partition for redboot devices (if applicable)
+
+[john@phrozen.org: used by ixp and others]
+
+lede-commit: 394918851f84e4d00fa16eb900e7700e95091f00
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/redboot.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+--- a/drivers/mtd/parsers/redboot.c
++++ b/drivers/mtd/parsers/redboot.c
+@@ -278,14 +278,21 @@ nogood:
+ #endif
+ names += strlen(names) + 1;
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+ if (fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
+- i++;
+- parts[i].offset = parts[i - 1].size + parts[i - 1].offset;
+- parts[i].size = fl->next->img->flash_base - parts[i].offset;
+- parts[i].name = nullname;
+- }
++ if (!strcmp(parts[i].name, "rootfs")) {
++ parts[i].size = fl->next->img->flash_base;
++ parts[i].size &= ~(master->erasesize - 1);
++ parts[i].size -= parts[i].offset;
++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
++ nrparts--;
++ } else {
++ i++;
++ parts[i].offset = parts[i-1].size + parts[i-1].offset;
++ parts[i].size = fl->next->img->flash_base - parts[i].offset;
++ parts[i].name = nullname;
+ #endif
++ }
++ }
+ tmp_fl = fl;
+ fl = fl->next;
+ kfree(tmp_fl);
diff --git a/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch b/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch
new file mode 100644
index 0000000000..8a6e630530
--- /dev/null
+++ b/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch
@@ -0,0 +1,229 @@
+From: Florian Fainelli <f.fainelli@gmail.com>
+Subject: Add myloader partition table parser
+
+[john@phozen.org: shoud be upstreamable]
+
+lede-commit: d8bf22859b51faa09d22c056fe221a45d2f7a3b8
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+[adjust for kernel 5.4, add myloader.c to patch]
+Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de>
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -57,6 +57,22 @@ config MTD_CMDLINE_PARTS
+
+ If unsure, say 'N'.
+
++config MTD_MYLOADER_PARTS
++ tristate "MyLoader partition parsing"
++ depends on ADM5120 || ATH25 || ATH79
++ 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
++ offsets and lengths of the user defined partitions.
++
++ If you need code which can detect and parse these tables, and
++ register MTD 'partitions' corresponding to each image detected,
++ enable this option.
++
++ You will still need the parsing functions to be called by the driver
++ for your particular device. It won't happen automatically.
++
+ config MTD_OF_PARTS
+ tristate "OpenFirmware (device tree) partitioning parser"
+ default y
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -3,6 +3,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.
+ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
+ obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
+ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
++obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
+ obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
+ ofpart-y += ofpart_core.o
+ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o
+--- /dev/null
++++ b/drivers/mtd/parsers/myloader.c
+@@ -0,0 +1,181 @@
++/*
++ * Parse MyLoader-style flash partition tables and produce a Linux partition
++ * array to match.
++ *
++ * Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
++ *
++ * This file was based on drivers/mtd/redboot.c
++ * Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/vmalloc.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/byteorder/generic.h>
++#include <linux/myloader.h>
++
++#define BLOCK_LEN_MIN 0x10000
++#define PART_NAME_LEN 32
++
++struct part_data {
++ struct mylo_partition_table tab;
++ char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN];
++};
++
++static int myloader_parse_partitions(struct mtd_info *master,
++ const struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ struct part_data *buf;
++ struct mylo_partition_table *tab;
++ struct mylo_partition *part;
++ struct mtd_partition *mtd_parts;
++ struct mtd_partition *mtd_part;
++ int num_parts;
++ int ret, i;
++ size_t retlen;
++ char *names;
++ unsigned long offset;
++ unsigned long blocklen;
++
++ buf = vmalloc(sizeof(*buf));
++ if (!buf) {
++ return -ENOMEM;
++ goto out;
++ }
++ tab = &buf->tab;
++
++ blocklen = master->erasesize;
++ if (blocklen < BLOCK_LEN_MIN)
++ blocklen = BLOCK_LEN_MIN;
++
++ offset = blocklen;
++
++ /* Find the partition table */
++ for (i = 0; i < 4; i++, offset += blocklen) {
++ printk(KERN_DEBUG "%s: searching for MyLoader partition table"
++ " at offset 0x%lx\n", master->name, offset);
++
++ ret = mtd_read(master, offset, sizeof(*buf), &retlen,
++ (void *)buf);
++ if (ret)
++ goto out_free_buf;
++
++ if (retlen != sizeof(*buf)) {
++ ret = -EIO;
++ goto out_free_buf;
++ }
++
++ /* Check for Partition Table magic number */
++ if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS))
++ break;
++
++ }
++
++ if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) {
++ printk(KERN_DEBUG "%s: no MyLoader partition table found\n",
++ master->name);
++ ret = 0;
++ goto out_free_buf;
++ }
++
++ /* The MyLoader and the Partition Table is always present */
++ num_parts = 2;
++
++ /* Detect number of used partitions */
++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
++ part = &tab->partitions[i];
++
++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
++ continue;
++
++ num_parts++;
++ }
++
++ mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) +
++ num_parts * PART_NAME_LEN), GFP_KERNEL);
++
++ if (!mtd_parts) {
++ ret = -ENOMEM;
++ goto out_free_buf;
++ }
++
++ mtd_part = mtd_parts;
++ names = (char *)&mtd_parts[num_parts];
++
++ strncpy(names, "myloader", PART_NAME_LEN);
++ mtd_part->name = names;
++ mtd_part->offset = 0;
++ mtd_part->size = offset;
++ mtd_part->mask_flags = MTD_WRITEABLE;
++ mtd_part++;
++ names += PART_NAME_LEN;
++
++ strncpy(names, "partition_table", PART_NAME_LEN);
++ mtd_part->name = names;
++ mtd_part->offset = offset;
++ mtd_part->size = blocklen;
++ mtd_part->mask_flags = MTD_WRITEABLE;
++ mtd_part++;
++ names += PART_NAME_LEN;
++
++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
++ part = &tab->partitions[i];
++
++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
++ continue;
++
++ if ((buf->names[i][0]) && (buf->names[i][0] != '\xff'))
++ strncpy(names, buf->names[i], PART_NAME_LEN);
++ else
++ snprintf(names, PART_NAME_LEN, "partition%d", i);
++
++ mtd_part->offset = le32_to_cpu(part->addr);
++ mtd_part->size = le32_to_cpu(part->size);
++ mtd_part->name = names;
++ mtd_part++;
++ names += PART_NAME_LEN;
++ }
++
++ *pparts = mtd_parts;
++ ret = num_parts;
++
++ out_free_buf:
++ vfree(buf);
++ out:
++ return ret;
++}
++
++static struct mtd_part_parser myloader_mtd_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = myloader_parse_partitions,
++ .name = "MyLoader",
++};
++
++static int __init myloader_mtd_parser_init(void)
++{
++ register_mtd_parser(&myloader_mtd_parser);
++
++ return 0;
++}
++
++static void __exit myloader_mtd_parser_exit(void)
++{
++ deregister_mtd_parser(&myloader_mtd_parser);
++}
++
++module_init(myloader_mtd_parser_init);
++module_exit(myloader_mtd_parser_exit);
++
++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
++MODULE_DESCRIPTION("Parsing code for MyLoader partition tables");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/pending-6.1/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch b/target/linux/generic/pending-6.1/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch
new file mode 100644
index 0000000000..bcea45d009
--- /dev/null
+++ b/target/linux/generic/pending-6.1/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch
@@ -0,0 +1,68 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Subject: [PATCH] mtd: bcm47xxpart: check for bad blocks when calculating offsets
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+
+--- a/drivers/mtd/parsers/parser_trx.c
++++ b/drivers/mtd/parsers/parser_trx.c
+@@ -25,6 +25,33 @@ struct trx_header {
+ uint32_t offset[3];
+ } __packed;
+
++/*
++ * Calculate real end offset (address) for a given amount of data. It checks
++ * all blocks skipping bad ones.
++ */
++static size_t parser_trx_real_offset(struct mtd_info *mtd, size_t bytes)
++{
++ size_t real_offset = 0;
++
++ if (mtd_block_isbad(mtd, real_offset))
++ pr_warn("Base offset shouldn't be at bad block");
++
++ while (bytes >= mtd->erasesize) {
++ bytes -= mtd->erasesize;
++ real_offset += mtd->erasesize;
++ while (mtd_block_isbad(mtd, real_offset)) {
++ real_offset += mtd->erasesize;
++
++ if (real_offset >= mtd->size)
++ return real_offset - mtd->erasesize;
++ }
++ }
++
++ real_offset += bytes;
++
++ return real_offset;
++}
++
+ static const char *parser_trx_data_part_name(struct mtd_info *master,
+ size_t offset)
+ {
+@@ -86,21 +113,21 @@ static int parser_trx_parse(struct mtd_i
+ if (trx.offset[2]) {
+ part = &parts[curr_part++];
+ part->name = "loader";
+- part->offset = trx.offset[i];
++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
+ i++;
+ }
+
+ if (trx.offset[i]) {
+ part = &parts[curr_part++];
+ part->name = "linux";
+- part->offset = trx.offset[i];
++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
+ i++;
+ }
+
+ if (trx.offset[i]) {
+ part = &parts[curr_part++];
+- part->name = parser_trx_data_part_name(mtd, trx.offset[i]);
+- part->offset = trx.offset[i];
++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
++ part->name = parser_trx_data_part_name(mtd, part->offset);
+ i++;
+ }
+
diff --git a/target/linux/generic/pending-6.1/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch b/target/linux/generic/pending-6.1/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch
new file mode 100644
index 0000000000..852654d924
--- /dev/null
+++ b/target/linux/generic/pending-6.1/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch
@@ -0,0 +1,37 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Subject: mtd: bcm47xxpart: detect T_Meter partition
+
+It can be found on many Netgear devices. It consists of many 0x30 blocks
+starting with 4D 54.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+ drivers/mtd/bcm47xxpart.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/parsers/bcm47xxpart.c
++++ b/drivers/mtd/parsers/bcm47xxpart.c
+@@ -35,6 +35,7 @@
+ #define NVRAM_HEADER 0x48534C46 /* FLSH */
+ #define POT_MAGIC1 0x54544f50 /* POTT */
+ #define POT_MAGIC2 0x504f /* OP */
++#define T_METER_MAGIC 0x4D540000 /* MT */
+ #define ML_MAGIC1 0x39685a42
+ #define ML_MAGIC2 0x26594131
+ #define TRX_MAGIC 0x30524448
+@@ -178,6 +179,15 @@ static int bcm47xxpart_parse(struct mtd_
+ MTD_WRITEABLE);
+ continue;
+ }
++
++ /* T_Meter */
++ if ((le32_to_cpu(buf[0x000 / 4]) & 0xFFFF0000) == T_METER_MAGIC &&
++ (le32_to_cpu(buf[0x030 / 4]) & 0xFFFF0000) == T_METER_MAGIC &&
++ (le32_to_cpu(buf[0x060 / 4]) & 0xFFFF0000) == T_METER_MAGIC) {
++ bcm47xxpart_add_part(&parts[curr_part++], "T_Meter", offset,
++ MTD_WRITEABLE);
++ continue;
++ }
+
+ /* TRX */
+ if (buf[0x000 / 4] == TRX_MAGIC) {
diff --git a/target/linux/generic/pending-6.1/435-mtd-add-routerbootpart-parser-config.patch b/target/linux/generic/pending-6.1/435-mtd-add-routerbootpart-parser-config.patch
new file mode 100644
index 0000000000..ee949f73c0
--- /dev/null
+++ b/target/linux/generic/pending-6.1/435-mtd-add-routerbootpart-parser-config.patch
@@ -0,0 +1,38 @@
+From 4437e01fb6bca63fccdba5d6c44888b0935885c2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org>
+Date: Tue, 24 Mar 2020 11:45:07 +0100
+Subject: [PATCH] generic: routerboot partition build bits (5.4)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds routerbootpart kernel build bits
+
+Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
+---
+ drivers/mtd/parsers/Kconfig | 9 +++++++++
+ drivers/mtd/parsers/Makefile | 1 +
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -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.
++
++config MTD_ROUTERBOOT_PARTS
++ tristate "RouterBoot flash partition parser"
++ depends on MTD && OF
++ help
++ MikroTik RouterBoot is implemented as a multi segment system on the
++ flash, some of which are fixed and some of which are located at
++ variable offsets. This parser handles both cases via properly
++ formatted DTS.
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -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
++obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o
diff --git a/target/linux/generic/pending-6.1/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch b/target/linux/generic/pending-6.1/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch
new file mode 100644
index 0000000000..0be74a5977
--- /dev/null
+++ b/target/linux/generic/pending-6.1/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch
@@ -0,0 +1,25 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: disable cfi cmdset 0002 erase suspend
+
+on some platforms, erase suspend leads to data corruption and lockups when write
+ops collide with erase ops. this has been observed on the buffalo wzr-hp-g300nh.
+rather than play whack-a-mole with a hard to reproduce issue on a variety of devices,
+simply disable erase suspend, as it will usually not produce any useful gain on
+the small filesystems used on embedded hardware.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/chips/cfi_cmdset_0002.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/chips/cfi_cmdset_0002.c
++++ b/drivers/mtd/chips/cfi_cmdset_0002.c
+@@ -907,7 +907,7 @@ static int get_chip(struct map_info *map
+ return 0;
+
+ case FL_ERASING:
+- if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
++ if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
+ !(mode == FL_READY || mode == FL_POINT ||
+ (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
+ goto sleep;
diff --git a/target/linux/generic/pending-6.1/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch b/target/linux/generic/pending-6.1/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch
new file mode 100644
index 0000000000..ca56de8271
--- /dev/null
+++ b/target/linux/generic/pending-6.1/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch
@@ -0,0 +1,17 @@
+From: George Kashperko <george@znau.edu.ua>
+Subject: Issue map read after Write Buffer Load command to ensure chip is ready to receive data.
+
+Signed-off-by: George Kashperko <george@znau.edu.ua>
+---
+ drivers/mtd/chips/cfi_cmdset_0002.c | 1 +
+ 1 file changed, 1 insertion(+)
+--- a/drivers/mtd/chips/cfi_cmdset_0002.c
++++ b/drivers/mtd/chips/cfi_cmdset_0002.c
+@@ -2051,6 +2051,7 @@ static int __xipram do_write_buffer(stru
+
+ /* Write Buffer Load */
+ map_write(map, CMD(0x25), cmd_adr);
++ (void) map_read(map, cmd_adr);
+
+ chip->state = FL_WRITING_TO_BUFFER;
+
diff --git a/target/linux/generic/pending-6.1/465-m25p80-mx-disable-software-protection.patch b/target/linux/generic/pending-6.1/465-m25p80-mx-disable-software-protection.patch
new file mode 100644
index 0000000000..f58d5452ab
--- /dev/null
+++ b/target/linux/generic/pending-6.1/465-m25p80-mx-disable-software-protection.patch
@@ -0,0 +1,18 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: Disable software protection bits for Macronix flashes.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/macronix.c
++++ b/drivers/mtd/spi-nor/macronix.c
+@@ -93,6 +93,7 @@ static void macronix_default_init(struct
+ {
+ nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
+ nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode;
++ nor->flags |= SNOR_F_HAS_LOCK;
+ }
+
+ static const struct spi_nor_fixups macronix_fixups = {
diff --git a/target/linux/generic/pending-6.1/476-mtd-spi-nor-add-eon-en25q128.patch b/target/linux/generic/pending-6.1/476-mtd-spi-nor-add-eon-en25q128.patch
new file mode 100644
index 0000000000..9383e48856
--- /dev/null
+++ b/target/linux/generic/pending-6.1/476-mtd-spi-nor-add-eon-en25q128.patch
@@ -0,0 +1,18 @@
+From: Piotr Dymacz <pepe2k@gmail.com>
+Subject: kernel/mtd: add support for EON EN25Q128
+
+Signed-off-by: Piotr Dymacz <pepe2k@gmail.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/eon.c
++++ b/drivers/mtd/spi-nor/eon.c
+@@ -25,6 +25,7 @@ static const struct flash_info eon_parts
+ { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
+ { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
+ { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
++ { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256, SECT_4K) },
+ { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16,
+ SECT_4K | SPI_NOR_DUAL_READ) },
+ { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32,
diff --git a/target/linux/generic/pending-6.1/477-mtd-spi-nor-add-eon-en25qx128a.patch b/target/linux/generic/pending-6.1/477-mtd-spi-nor-add-eon-en25qx128a.patch
new file mode 100644
index 0000000000..3c579e55e3
--- /dev/null
+++ b/target/linux/generic/pending-6.1/477-mtd-spi-nor-add-eon-en25qx128a.patch
@@ -0,0 +1,21 @@
+From: Christian Marangi <ansuelsmth@gmail.com>
+Subject: kernel/mtd: add support for EON EN25QX128A
+
+Add support for EON EN25QX128A with no flags as it does
+support SFDP parsing.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/eon.c
++++ b/drivers/mtd/spi-nor/eon.c
+@@ -26,6 +26,7 @@ static const struct flash_info eon_parts
+ { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
+ { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
+ { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256, SECT_4K) },
++ { "en25qx128a", INFO(0x1c7118, 0, 64 * 1024, 256, 0) },
+ { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16,
+ SECT_4K | SPI_NOR_DUAL_READ) },
+ { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32,
diff --git a/target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch b/target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch
new file mode 100644
index 0000000000..5a064b714b
--- /dev/null
+++ b/target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch
@@ -0,0 +1,79 @@
+From patchwork Thu Feb 6 17:19:41 2020
+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: 1234465
+Date: Thu, 6 Feb 2020 19:19:41 +0200
+From: Daniel Golle <daniel@makrotopia.org>
+To: linux-mtd@lists.infradead.org
+Subject: [PATCH v2] mtd: spi-nor: Add support for xt25f128b chip
+Message-ID: <20200206171941.GA2398@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+List-Subscribe: <http://lists.infradead.org/mailman/listinfo/linux-mtd>,
+ <mailto:linux-mtd-request@lists.infradead.org?subject=subscribe>
+Cc: Eitan Cohen <eitan@neot-semadar.com>, Piotr Dymacz <pepe2k@gmail.com>,
+ Tudor Ambarus <tudor.ambarus@microchip.com>
+Sender: "linux-mtd" <linux-mtd-bounces@lists.infradead.org>
+Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org
+
+Add XT25F128B made by XTX Technology (Shenzhen) Limited.
+This chip supports dual and quad read and uniform 4K-byte erase.
+Verified on Teltonika RUT955 which comes with XT25F128B in recent
+versions of the device.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/mtd/spi-nor/Makefile
++++ b/drivers/mtd/spi-nor/Makefile
+@@ -17,6 +17,7 @@ spi-nor-objs += sst.o
+ spi-nor-objs += winbond.o
+ spi-nor-objs += xilinx.o
+ spi-nor-objs += xmc.o
++spi-nor-objs += xtx.o
+ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
+
+ obj-$(CONFIG_MTD_SPI_NOR) += controllers/
+--- /dev/null
++++ b/drivers/mtd/spi-nor/xtx.c
+@@ -0,0 +1,15 @@
++// SPDX-License-Identifier: GPL-2.0
++#include <linux/mtd/spi-nor.h>
++
++#include "core.h"
++
++static const struct flash_info xtx_parts[] = {
++ /* XTX Technology (Shenzhen) Limited */
++ { "xt25f128b", INFO(0x0B4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++};
++
++const struct spi_nor_manufacturer spi_nor_xtx = {
++ .name = "xtx",
++ .parts = xtx_parts,
++ .nparts = ARRAY_SIZE(xtx_parts),
++};
+--- a/drivers/mtd/spi-nor/core.c
++++ b/drivers/mtd/spi-nor/core.c
+@@ -1859,6 +1859,7 @@ static const struct spi_nor_manufacturer
+ &spi_nor_winbond,
+ &spi_nor_xilinx,
+ &spi_nor_xmc,
++ &spi_nor_xtx,
+ };
+
+ static const struct flash_info *
+--- a/drivers/mtd/spi-nor/core.h
++++ b/drivers/mtd/spi-nor/core.h
+@@ -489,6 +489,7 @@ extern const struct spi_nor_manufacturer
+ extern const struct spi_nor_manufacturer spi_nor_winbond;
+ extern const struct spi_nor_manufacturer spi_nor_xilinx;
+ extern const struct spi_nor_manufacturer spi_nor_xmc;
++extern const struct spi_nor_manufacturer spi_nor_xtx;
+
+ extern const struct attribute_group *spi_nor_sysfs_groups[];
+
diff --git a/target/linux/generic/pending-6.1/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch b/target/linux/generic/pending-6.1/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch
new file mode 100644
index 0000000000..c32cde559d
--- /dev/null
+++ b/target/linux/generic/pending-6.1/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch
@@ -0,0 +1,22 @@
+From d68b4aa22e8c625685bfad642dd7337948dc0ad1 Mon Sep 17 00:00:00 2001
+From: Koen Vandeputte <koen.vandeputte@ncentric.com>
+Date: Mon, 6 Jan 2020 13:07:56 +0100
+Subject: [PATCH] mtd: spi-nor: add support for Gigadevice GD25D05
+
+Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/mtd/spi-nor/gigadevice.c
++++ b/drivers/mtd/spi-nor/gigadevice.c
+@@ -24,6 +24,9 @@ static struct spi_nor_fixups gd25q256_fi
+ };
+
+ static const struct flash_info gigadevice_parts[] = {
++ { "gd25q05", INFO(0xc84010, 0, 64 * 1024, 1,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
+ { "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) },
diff --git a/target/linux/generic/pending-6.1/482-mtd-spi-nor-add-gd25q512.patch b/target/linux/generic/pending-6.1/482-mtd-spi-nor-add-gd25q512.patch
new file mode 100644
index 0000000000..7c8b686277
--- /dev/null
+++ b/target/linux/generic/pending-6.1/482-mtd-spi-nor-add-gd25q512.patch
@@ -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
+Subject: [PATCH] spi-nor/gigadevic: add gd25q512
+
+---
+ drivers/mtd/spi-nor/gigadevice.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/mtd/spi-nor/gigadevice.c
++++ b/drivers/mtd/spi-nor/gigadevice.c
+@@ -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 },
++ { "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4B_OPCODES) },
+ };
+
+ const struct spi_nor_manufacturer spi_nor_gigadevice = {
diff --git a/target/linux/generic/pending-6.1/484-mtd-spi-nor-add-esmt-f25l16pa.patch b/target/linux/generic/pending-6.1/484-mtd-spi-nor-add-esmt-f25l16pa.patch
new file mode 100644
index 0000000000..ab402e622a
--- /dev/null
+++ b/target/linux/generic/pending-6.1/484-mtd-spi-nor-add-esmt-f25l16pa.patch
@@ -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
+Subject: [PATCH] spi-nor/esmt.c: add esmt f25l16pa
+
+This fixes support for Dongwon T&I DW02-412H which uses F25L16PA(2S)
+flash.
+
+---
+ drivers/mtd/spi-nor/esmt.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/mtd/spi-nor/esmt.c
++++ b/drivers/mtd/spi-nor/esmt.c
+@@ -10,6 +10,8 @@
+
+ static const struct flash_info esmt_parts[] = {
+ /* ESMT */
++ { "f25l16pa-2s", INFO(0x8c2115, 0, 64 * 1024, 32,
++ SECT_4K | SPI_NOR_HAS_LOCK) },
+ { "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-6.1/485-mtd-spi-nor-add-xmc-xm25qh128c.patch b/target/linux/generic/pending-6.1/485-mtd-spi-nor-add-xmc-xm25qh128c.patch
new file mode 100644
index 0000000000..68e373ea23
--- /dev/null
+++ b/target/linux/generic/pending-6.1/485-mtd-spi-nor-add-xmc-xm25qh128c.patch
@@ -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
+Subject: [PATCH] spi-nor/xmc.c: add xm25qh128c
+
+The XMC XM25QH128C is a 16MB SPI NOR chip. The patch is verified on
+Ruijie RG-EW3200GX PRO.
+Datasheet available at https://www.xmcwh.com/uploads/435/XM25QH128C.pdf
+
+---
+ drivers/mtd/spi-nor/xmc.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- 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
+ 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) },
++ { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ };
+
+ const struct spi_nor_manufacturer spi_nor_xmc = {
diff --git a/target/linux/generic/pending-6.1/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch b/target/linux/generic/pending-6.1/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch
new file mode 100644
index 0000000000..d117cfe0a3
--- /dev/null
+++ b/target/linux/generic/pending-6.1/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch
@@ -0,0 +1,143 @@
+From a43b844cb40bf1b783055fdc81b7f991e21e7e76 Mon Sep 17 00:00:00 2001
+From: Chuanhong Guo <gch981213@gmail.com>
+Date: Wed, 13 Apr 2022 11:58:17 +0800
+Subject: [PATCH] mtd: spinand: add support for ESMT F50x1G41LB
+
+This patch adds support for ESMT F50L1G41LB and F50D1G41LB.
+It seems that ESMT likes to use random JEDEC ID from other vendors.
+Their 1G chips uses 0xc8 from GigaDevice and 2G/4G chips uses 0x2c from
+Micron. For this reason, the ESMT entry is named esmt_c8 with explicit
+JEDEC ID in variable name.
+
+Datasheets:
+https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50L1G41LB(2M).pdf
+https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50D1G41LB(2M).pdf
+
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+ drivers/mtd/nand/spi/Makefile | 2 +-
+ drivers/mtd/nand/spi/core.c | 1 +
+ drivers/mtd/nand/spi/esmt.c | 89 +++++++++++++++++++++++++++++++++++
+ include/linux/mtd/spinand.h | 1 +
+ 4 files changed, 92 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/mtd/nand/spi/esmt.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 xtx.o
++spinand-objs := core.o esmt.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
+@@ -906,6 +906,7 @@ static const struct nand_ops spinand_ops
+ };
+
+ static const struct spinand_manufacturer *spinand_manufacturers[] = {
++ &esmt_c8_spinand_manufacturer,
+ &gigadevice_spinand_manufacturer,
+ &macronix_spinand_manufacturer,
+ &micron_spinand_manufacturer,
+--- /dev/null
++++ b/drivers/mtd/nand/spi/esmt.c
+@@ -0,0 +1,89 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Author:
++ * Chuanhong Guo <gch981213@gmail.com>
++ */
++
++#include <linux/device.h>
++#include <linux/kernel.h>
++#include <linux/mtd/spinand.h>
++
++/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
++#define SPINAND_MFR_ESMT_C8 0xc8
++
++static SPINAND_OP_VARIANTS(read_cache_variants,
++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, 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 f50l1g41lb_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = 16 * section + 8;
++ region->length = 8;
++
++ return 0;
++}
++
++static int f50l1g41lb_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = 16 * section + 2;
++ region->length = 6;
++
++ return 0;
++}
++
++static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = {
++ .ecc = f50l1g41lb_ooblayout_ecc,
++ .free = f50l1g41lb_ooblayout_free,
++};
++
++static const struct spinand_info esmt_c8_spinand_table[] = {
++ SPINAND_INFO("F50L1G41LB",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
++ NAND_ECCREQ(1, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ 0,
++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
++ SPINAND_INFO("F50D1G41LB",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
++ NAND_ECCREQ(1, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ 0,
++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
++};
++
++static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
++};
++
++const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
++ .id = SPINAND_MFR_ESMT_C8,
++ .name = "ESMT",
++ .chips = esmt_c8_spinand_table,
++ .nchips = ARRAY_SIZE(esmt_c8_spinand_table),
++ .ops = &esmt_spinand_manuf_ops,
++};
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -260,6 +260,7 @@ struct spinand_manufacturer {
+ };
+
+ /* SPI NAND manufacturers */
++extern const struct spinand_manufacturer esmt_c8_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-6.1/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch b/target/linux/generic/pending-6.1/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch
new file mode 100644
index 0000000000..7e20e14bb6
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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
+@@ -908,6 +908,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-6.1/488-mtd-spi-nor-add-xmc-xm25qh64c.patch b/target/linux/generic/pending-6.1/488-mtd-spi-nor-add-xmc-xm25qh64c.patch
new file mode 100644
index 0000000000..236d1c2755
--- /dev/null
+++ b/target/linux/generic/pending-6.1/488-mtd-spi-nor-add-xmc-xm25qh64c.patch
@@ -0,0 +1,22 @@
+From: Joe Mullally <jwmullally@gmail.com>
+Subject: mtd/spi-nor/xmc: add support for XMC XM25QH64C
+
+The XMC XM25QH64C is a 8MB SPI NOR chip. The patch is verified on TL-WPA8631P v3.
+Datasheet available at https://www.xmcwh.com/uploads/442/XM25QH64C.pdf
+
+Signed-off-by: Joe Mullally <jwmullally@gmail.com>
+---
+ drivers/mtd/spi-nor/xmc.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/mtd/spi-nor/xmc.c
++++ b/drivers/mtd/spi-nor/xmc.c
+@@ -12,6 +12,8 @@ static const struct flash_info xmc_parts
+ /* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */
+ { "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++ { "XM25QH64C", INFO(0x204017, 0, 64 * 1024, 128,
++ 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) },
+ { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256,
diff --git a/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch b/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch
new file mode 100644
index 0000000000..2751da541a
--- /dev/null
+++ b/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch
@@ -0,0 +1,97 @@
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: ubi: auto-attach mtd device named "ubi" or "data" on boot
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/build.c | 36 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+--- a/drivers/mtd/ubi/build.c
++++ b/drivers/mtd/ubi/build.c
+@@ -1200,6 +1200,73 @@ static struct mtd_info * __init open_mtd
+ return mtd;
+ }
+
++/*
++ * This function tries attaching mtd partitions named either "ubi" or "data"
++ * during boot.
++ */
++static void __init ubi_auto_attach(void)
++{
++ int err;
++ struct mtd_info *mtd;
++ loff_t offset = 0;
++ size_t len;
++ char magic[4];
++
++ /* try attaching mtd device named "ubi" or "data" */
++ mtd = open_mtd_device("ubi");
++ if (IS_ERR(mtd))
++ mtd = open_mtd_device("data");
++
++ if (IS_ERR(mtd))
++ return;
++
++ /* get the first not bad block */
++ if (mtd_can_have_bb(mtd))
++ while (mtd_block_isbad(mtd, offset)) {
++ offset += mtd->erasesize;
++
++ if (offset > mtd->size) {
++ pr_err("UBI error: Failed to find a non-bad "
++ "block on mtd%d\n", mtd->index);
++ goto cleanup;
++ }
++ }
++
++ /* check if the read from flash was successful */
++ err = mtd_read(mtd, offset, 4, &len, (void *) magic);
++ if ((err && !mtd_is_bitflip(err)) || len != 4) {
++ pr_err("UBI error: unable to read from mtd%d\n", mtd->index);
++ goto cleanup;
++ }
++
++ /* check for a valid ubi magic */
++ if (strncmp(magic, "UBI#", 4)) {
++ pr_err("UBI error: no valid UBI magic found inside mtd%d\n", mtd->index);
++ goto cleanup;
++ }
++
++ /* don't auto-add media types where UBI doesn't makes sense */
++ if (mtd->type != MTD_NANDFLASH &&
++ mtd->type != MTD_NORFLASH &&
++ mtd->type != MTD_DATAFLASH &&
++ mtd->type != MTD_MLCNANDFLASH)
++ goto cleanup;
++
++ mutex_lock(&ubi_devices_mutex);
++ pr_notice("UBI: auto-attach mtd%d\n", mtd->index);
++ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0);
++ mutex_unlock(&ubi_devices_mutex);
++ if (err < 0) {
++ pr_err("UBI error: cannot attach mtd%d\n", mtd->index);
++ goto cleanup;
++ }
++
++ return;
++
++cleanup:
++ put_mtd_device(mtd);
++}
++
+ static int __init ubi_init(void)
+ {
+ int err, i, k;
+@@ -1283,6 +1350,12 @@ static int __init ubi_init(void)
+ }
+ }
+
++ /* auto-attach mtd devices only if built-in to the kernel and no ubi.mtd
++ * parameter was given */
++ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
++ !ubi_is_module() && !mtd_devs)
++ ubi_auto_attach();
++
+ err = ubiblock_init();
+ if (err) {
+ pr_err("UBI error: block: cannot initialize, error %d\n", err);
diff --git a/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch b/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch
new file mode 100644
index 0000000000..ae53770c11
--- /dev/null
+++ b/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch
@@ -0,0 +1,69 @@
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: ubi: auto-create ubiblock device for rootfs
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/block.c | 42 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -642,6 +642,47 @@ static void __init ubiblock_create_from_
+ }
+ }
+
++#define UBIFS_NODE_MAGIC 0x06101831
++static inline int ubi_vol_is_ubifs(struct ubi_volume_desc *desc)
++{
++ int ret;
++ uint32_t magic_of, magic;
++ ret = ubi_read(desc, 0, (char *)&magic_of, 0, 4);
++ if (ret)
++ return 0;
++ magic = le32_to_cpu(magic_of);
++ return magic == UBIFS_NODE_MAGIC;
++}
++
++static void __init ubiblock_create_auto_rootfs(void)
++{
++ int ubi_num, ret, is_ubifs;
++ struct ubi_volume_desc *desc;
++ struct ubi_volume_info vi;
++
++ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) {
++ desc = ubi_open_volume_nm(ubi_num, "rootfs", UBI_READONLY);
++ if (IS_ERR(desc))
++ desc = ubi_open_volume_nm(ubi_num, "fit", UBI_READONLY);;
++
++ if (IS_ERR(desc))
++ continue;
++
++ ubi_get_volume_info(desc, &vi);
++ is_ubifs = ubi_vol_is_ubifs(desc);
++ ubi_close_volume(desc);
++ if (is_ubifs)
++ break;
++
++ ret = ubiblock_create(&vi);
++ if (ret)
++ pr_err("UBI error: block: can't add '%s' volume, err=%d\n",
++ vi.name, ret);
++ /* always break if we get here */
++ break;
++ }
++}
++
+ static void ubiblock_remove_all(void)
+ {
+ struct ubiblock *next;
+@@ -674,6 +715,10 @@ int __init ubiblock_init(void)
+ */
+ ubiblock_create_from_param();
+
++ /* auto-attach "rootfs" volume if existing and non-ubifs */
++ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV))
++ ubiblock_create_auto_rootfs();
++
+ /*
+ * Block devices are only created upon user requests, so we ignore
+ * existing volumes.
diff --git a/target/linux/generic/pending-6.1/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch b/target/linux/generic/pending-6.1/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch
new file mode 100644
index 0000000000..cf41c8cad3
--- /dev/null
+++ b/target/linux/generic/pending-6.1/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch
@@ -0,0 +1,53 @@
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: try auto-mounting ubi0:rootfs in init/do_mounts.c
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ init/do_mounts.c | 26 +++++++++++++++++++++++++-
+ 1 file changed, 25 insertions(+), 1 deletion(-)
+
+--- a/init/do_mounts.c
++++ b/init/do_mounts.c
+@@ -447,7 +447,30 @@ retry:
+ out:
+ put_page(page);
+ }
+-
++
++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
++static int __init mount_ubi_rootfs(void)
++{
++ int flags = MS_SILENT;
++ int err, tried = 0;
++
++ while (tried < 2) {
++ err = do_mount_root("ubi0:rootfs", "ubifs", flags, \
++ root_mount_data);
++ switch (err) {
++ case -EACCES:
++ flags |= MS_RDONLY;
++ tried++;
++ break;
++ default:
++ return err;
++ }
++ }
++
++ return -EINVAL;
++}
++#endif
++
+ #ifdef CONFIG_ROOT_NFS
+
+ #define NFSROOT_TIMEOUT_MIN 5
+@@ -580,6 +603,10 @@ void __init mount_root(void)
+ return;
+ }
+ #endif
++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
++ if (!mount_ubi_rootfs())
++ return;
++#endif
+ if (ROOT_DEV == 0 && root_device_name && root_fs_names) {
+ if (mount_nodev_root() == 0)
+ return;
diff --git a/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch b/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch
new file mode 100644
index 0000000000..266a6331c2
--- /dev/null
+++ b/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch
@@ -0,0 +1,34 @@
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: ubi: set ROOT_DEV to ubiblock "rootfs" if unset
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/ubi/block.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -42,6 +42,7 @@
+ #include <linux/scatterlist.h>
+ #include <linux/idr.h>
+ #include <asm/div64.h>
++#include <linux/root_dev.h>
+
+ #include "ubi-media.h"
+ #include "ubi.h"
+@@ -451,6 +452,15 @@ int ubiblock_create(struct ubi_volume_in
+ dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)",
+ dev->ubi_num, dev->vol_id, vi->name);
+ mutex_unlock(&devices_mutex);
++
++ if (!strcmp(vi->name, "rootfs") &&
++ IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
++ ROOT_DEV == 0) {
++ pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n",
++ dev->ubi_num, dev->vol_id, vi->name);
++ ROOT_DEV = MKDEV(gd->major, gd->first_minor);
++ }
++
+ return 0;
+
+ out_remove_minor:
diff --git a/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch b/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch
new file mode 100644
index 0000000000..413431755f
--- /dev/null
+++ b/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch
@@ -0,0 +1,60 @@
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: mtd: add EOF marker support to the UBI layer
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/mtd/ubi/attach.c | 25 ++++++++++++++++++++++---
+ drivers/mtd/ubi/ubi.h | 1 +
+ 2 files changed, 23 insertions(+), 3 deletions(-)
+
+--- a/drivers/mtd/ubi/attach.c
++++ b/drivers/mtd/ubi/attach.c
+@@ -926,6 +926,13 @@ static bool vol_ignored(int vol_id)
+ #endif
+ }
+
++static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech)
++{
++ return ech->padding1[0] == 'E' &&
++ ech->padding1[1] == 'O' &&
++ ech->padding1[2] == 'F';
++}
++
+ /**
+ * scan_peb - scan and process UBI headers of a PEB.
+ * @ubi: UBI device description object
+@@ -958,9 +965,21 @@ static int scan_peb(struct ubi_device *u
+ return 0;
+ }
+
+- err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
+- if (err < 0)
+- return err;
++ if (!ai->eof_found) {
++ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
++ if (err < 0)
++ return err;
++
++ if (ec_hdr_has_eof(ech)) {
++ pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n",
++ pnum);
++ ai->eof_found = true;
++ }
++ }
++
++ if (ai->eof_found)
++ err = UBI_IO_FF_BITFLIPS;
++
+ switch (err) {
+ case 0:
+ break;
+--- a/drivers/mtd/ubi/ubi.h
++++ b/drivers/mtd/ubi/ubi.h
+@@ -778,6 +778,7 @@ struct ubi_attach_info {
+ int mean_ec;
+ uint64_t ec_sum;
+ int ec_count;
++ bool eof_found;
+ struct kmem_cache *aeb_slab_cache;
+ struct ubi_ec_hdr *ech;
+ struct ubi_vid_io_buf *vidb;
diff --git a/target/linux/generic/pending-6.1/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/target/linux/generic/pending-6.1/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch
new file mode 100644
index 0000000000..01f3b9ec2d
--- /dev/null
+++ b/target/linux/generic/pending-6.1/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch
@@ -0,0 +1,52 @@
+From 5734c6669fba7ddb5ef491ccff7159d15dba0b59 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Wed, 5 Sep 2018 01:32:51 +0200
+Subject: [PATCH 496/497] dt-bindings: add bindings for mtd-concat devices
+
+Document virtual mtd-concat device bindings.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+---
+ .../devicetree/bindings/mtd/mtd-concat.txt | 36 +++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/mtd-concat.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/mtd-concat.txt
+@@ -0,0 +1,36 @@
++Virtual MTD concat device
++
++Requires properties:
++- devices: list of phandles to mtd nodes that should be concatenated
++
++Example:
++
++&spi {
++ flash0: flash@0 {
++ ...
++ };
++ flash1: flash@1 {
++ ...
++ };
++};
++
++flash {
++ compatible = "mtd-concat";
++
++ devices = <&flash0 &flash1>;
++
++ partitions {
++ compatible = "fixed-partitions";
++
++ partition@0 {
++ label = "boot";
++ reg = <0x0000000 0x0040000>;
++ read-only;
++ };
++
++ partition@40000 {
++ label = "firmware";
++ reg = <0x0040000 0x1fc0000>;
++ };
++ }
++}
diff --git a/target/linux/generic/pending-6.1/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/target/linux/generic/pending-6.1/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch
new file mode 100644
index 0000000000..e0cbc4508b
--- /dev/null
+++ b/target/linux/generic/pending-6.1/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch
@@ -0,0 +1,216 @@
+From e53f712d8eac71f54399b61038ccf87d2cee99d7 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Sat, 25 Aug 2018 12:35:22 +0200
+Subject: [PATCH 497/497] mtd: mtdconcat: add dt driver for concat devices
+
+Some mtd drivers like physmap variants have support for concatenating
+multiple mtd devices, but there is no generic way to define such a
+concat device from within the device tree.
+
+This is useful for some SoC boards that use multiple flash chips as
+memory banks of a single mtd device, with partitions spanning chip
+borders.
+
+This commit adds a driver for creating virtual mtd-concat devices. They
+must have a compatible = "mtd-concat" line, and define a list of devices
+to concat in the 'devices' property, for example:
+
+flash {
+ compatible = "mtd-concat";
+
+ devices = <&flash0 &flash1>;
+
+ partitions {
+ ...
+ };
+};
+
+The driver is added to the very end of the mtd Makefile to increase the
+likelyhood of all child devices already being loaded at the time of
+probing, preventing unnecessary deferred probes.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+---
+ drivers/mtd/Kconfig | 2 +
+ drivers/mtd/Makefile | 3 +
+ drivers/mtd/composite/Kconfig | 12 +++
+ drivers/mtd/composite/Makefile | 6 ++
+ drivers/mtd/composite/virt_concat.c | 128 ++++++++++++++++++++++++++++
+ 5 files changed, 151 insertions(+)
+ create mode 100644 drivers/mtd/composite/Kconfig
+ create mode 100644 drivers/mtd/composite/Makefile
+ create mode 100644 drivers/mtd/composite/virt_concat.c
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -241,4 +241,6 @@ source "drivers/mtd/ubi/Kconfig"
+
+ source "drivers/mtd/hyperbus/Kconfig"
+
++source "drivers/mtd/composite/Kconfig"
++
+ endif # MTD
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -33,3 +33,6 @@ obj-y += chips/ lpddr/ maps/ devices/ n
+ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
+ obj-$(CONFIG_MTD_UBI) += ubi/
+ obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/
++
++# Composite drivers must be loaded last
++obj-y += composite/
+--- /dev/null
++++ b/drivers/mtd/composite/Kconfig
+@@ -0,0 +1,12 @@
++menu "Composite MTD device drivers"
++ depends on MTD!=n
++
++config MTD_VIRT_CONCAT
++ tristate "Virtual concat MTD device"
++ help
++ This driver allows creation of a virtual MTD concat device, which
++ concatenates multiple underlying MTD devices to a single device.
++ This is required by some SoC boards where multiple memory banks are
++ used as one device with partitions spanning across device boundaries.
++
++endmenu
+--- /dev/null
++++ b/drivers/mtd/composite/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# linux/drivers/mtd/composite/Makefile
++#
++
++obj-$(CONFIG_MTD_VIRT_CONCAT) += virt_concat.o
+--- /dev/null
++++ b/drivers/mtd/composite/virt_concat.c
+@@ -0,0 +1,128 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Virtual concat MTD device driver
++ *
++ * Copyright (C) 2018 Bernhard Frauendienst
++ * Author: Bernhard Frauendienst, kernel@nospam.obeliks.de
++ */
++
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/mtd/concat.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/slab.h>
++
++/*
++ * struct of_virt_concat - platform device driver data.
++ * @cmtd the final mtd_concat device
++ * @num_devices the number of devices in @devices
++ * @devices points to an array of devices already loaded
++ */
++struct of_virt_concat {
++ struct mtd_info *cmtd;
++ int num_devices;
++ struct mtd_info **devices;
++};
++
++static int virt_concat_remove(struct platform_device *pdev)
++{
++ struct of_virt_concat *info;
++ int i;
++
++ info = platform_get_drvdata(pdev);
++ if (!info)
++ return 0;
++
++ // unset data for when this is called after a probe error
++ platform_set_drvdata(pdev, NULL);
++
++ if (info->cmtd) {
++ mtd_device_unregister(info->cmtd);
++ mtd_concat_destroy(info->cmtd);
++ }
++
++ if (info->devices) {
++ for (i = 0; i < info->num_devices; i++)
++ put_mtd_device(info->devices[i]);
++ }
++
++ return 0;
++}
++
++static int virt_concat_probe(struct platform_device *pdev)
++{
++ struct device_node *node = pdev->dev.of_node;
++ struct of_phandle_iterator it;
++ struct of_virt_concat *info;
++ struct mtd_info *mtd;
++ int err = 0, count;
++
++ count = of_count_phandle_with_args(node, "devices", NULL);
++ if (count <= 0)
++ return -EINVAL;
++
++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return -ENOMEM;
++ info->devices = devm_kcalloc(&pdev->dev, count,
++ sizeof(*(info->devices)), GFP_KERNEL);
++ if (!info->devices) {
++ err = -ENOMEM;
++ goto err_remove;
++ }
++
++ platform_set_drvdata(pdev, info);
++
++ of_for_each_phandle(&it, err, node, "devices", NULL, 0) {
++ mtd = of_get_mtd_device_by_node(it.node);
++ if (IS_ERR(mtd)) {
++ of_node_put(it.node);
++ err = -EPROBE_DEFER;
++ goto err_remove;
++ }
++
++ info->devices[info->num_devices++] = mtd;
++ }
++
++ info->cmtd = mtd_concat_create(info->devices, info->num_devices,
++ dev_name(&pdev->dev));
++ if (!info->cmtd) {
++ err = -ENXIO;
++ goto err_remove;
++ }
++
++ info->cmtd->dev.parent = &pdev->dev;
++ mtd_set_of_node(info->cmtd, node);
++ mtd_device_register(info->cmtd, NULL, 0);
++
++ return 0;
++
++err_remove:
++ virt_concat_remove(pdev);
++
++ return err;
++}
++
++static const struct of_device_id virt_concat_of_match[] = {
++ { .compatible = "mtd-concat", },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, virt_concat_of_match);
++
++static struct platform_driver virt_concat_driver = {
++ .probe = virt_concat_probe,
++ .remove = virt_concat_remove,
++ .driver = {
++ .name = "virt-mtdconcat",
++ .of_match_table = virt_concat_of_match,
++ },
++};
++
++module_platform_driver(virt_concat_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Bernhard Frauendienst <kernel@nospam.obeliks.de>");
++MODULE_DESCRIPTION("Virtual concat MTD device driver");
diff --git a/target/linux/generic/pending-6.1/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch b/target/linux/generic/pending-6.1/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch
new file mode 100644
index 0000000000..81de764876
--- /dev/null
+++ b/target/linux/generic/pending-6.1/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch
@@ -0,0 +1,34 @@
+From 8bf2ce6ea4ee840b70f55a27f80e1cd308051b13 Mon Sep 17 00:00:00 2001
+From: Nick Hainke <vincent@systemli.org>
+Date: Mon, 27 Dec 2021 00:38:13 +0100
+Subject: [PATCH 1/2] mtd: spi-nor: locking support for MX25L6405D
+
+Macronix MX25L6405D supports locking with four block-protection bits.
+Currently, the driver only sets three bits. If the bootloader does not
+sustain the flash chip in an unlocked state, the flash might be
+non-writeable. Add the corresponding flag to enable locking support with
+four bits in the status register.
+
+Tested on Nanostation M2 XM.
+
+Similar to commit 7ea40b54e83b ("mtd: spi-nor: enable locking support for
+MX25L12805D")
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+Signed-off-by: Nick Hainke <vincent@systemli.org>
+---
+ drivers/mtd/spi-nor/macronix.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi-nor/macronix.c
++++ b/drivers/mtd/spi-nor/macronix.c
+@@ -41,7 +41,8 @@ static const struct flash_info macronix_
+ { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) },
+ { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) },
+ { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
+- { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
++ { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K |
++ SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP) },
+ { "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) },
+ { "mx25u3235f", INFO(0xc22536, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ |
diff --git a/target/linux/generic/pending-6.1/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch b/target/linux/generic/pending-6.1/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch
new file mode 100644
index 0000000000..ec14f6341c
--- /dev/null
+++ b/target/linux/generic/pending-6.1/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch
@@ -0,0 +1,30 @@
+From 245224608b5368c10407da07557e546743d3c489 Mon Sep 17 00:00:00 2001
+From: Nick Hainke <vincent@systemli.org>
+Date: Mon, 27 Dec 2021 09:33:13 +0100
+Subject: [PATCH 2/2] mtd: spi-nor: disable 16-bit-sr for macronix
+
+Macronix flash chips seem to consist of only one status register.
+These chips will not work with the "16-bit Write Status (01h) Command".
+Disable SNOR_F_HAS_16BIT_SR for all Macronix chips.
+
+Tested with MX25L6405D.
+
+Fixes: 39d1e3340c73 ("mtd: spi-nor: Fix clearing of QE bit on
+lock()/unlock()")
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+Signed-off-by: Nick Hainke <vincent@systemli.org>
+---
+ drivers/mtd/spi-nor/macronix.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/macronix.c
++++ b/drivers/mtd/spi-nor/macronix.c
+@@ -94,6 +94,7 @@ static void macronix_default_init(struct
+ {
+ nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
+ nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode;
++ nor->flags &= ~SNOR_F_HAS_16BIT_SR;
+ nor->flags |= SNOR_F_HAS_LOCK;
+ }
+
diff --git a/target/linux/generic/pending-6.1/500-fs_cdrom_dependencies.patch b/target/linux/generic/pending-6.1/500-fs_cdrom_dependencies.patch
new file mode 100644
index 0000000000..2053c0fbe2
--- /dev/null
+++ b/target/linux/generic/pending-6.1/500-fs_cdrom_dependencies.patch
@@ -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
+Subject: [PATCH] fs: add cdrom dependency
+
+---
+ fs/hfs/Kconfig | 1 +
+ fs/hfsplus/Kconfig | 1 +
+ fs/isofs/Kconfig | 1 +
+ fs/udf/Kconfig | 1 +
+ 4 files changed, 4 insertions(+)
+
+--- a/fs/hfs/Kconfig
++++ b/fs/hfs/Kconfig
+@@ -2,6 +2,7 @@
+ config HFS_FS
+ tristate "Apple Macintosh file system support"
+ depends on BLOCK
++ select CDROM
+ select NLS
+ help
+ If you say Y here, you will be able to mount Macintosh-formatted
+--- a/fs/hfsplus/Kconfig
++++ b/fs/hfsplus/Kconfig
+@@ -2,6 +2,7 @@
+ config HFSPLUS_FS
+ tristate "Apple Extended HFS file system support"
+ depends on BLOCK
++ select CDROM
+ select NLS
+ select NLS_UTF8
+ help
+--- a/fs/isofs/Kconfig
++++ b/fs/isofs/Kconfig
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config ISO9660_FS
+ tristate "ISO 9660 CDROM file system support"
++ select CDROM
+ 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
+--- a/fs/udf/Kconfig
++++ b/fs/udf/Kconfig
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config UDF_FS
+ tristate "UDF file system support"
++ select CDROM
+ select CRC_ITU_T
+ select NLS
+ help
diff --git a/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch b/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch
new file mode 100644
index 0000000000..ac784d0b0b
--- /dev/null
+++ b/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch
@@ -0,0 +1,4581 @@
+From: Alexandros C. Couloumbis <alex@ozo.com>
+Subject: fs: add jffs2/lzma support (not activated by default yet)
+
+lede-commit: c2c88d315fa0e881f8b19da07b62859b915b11b2
+Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com>
+---
+ fs/jffs2/Kconfig | 9 +
+ fs/jffs2/Makefile | 3 +
+ fs/jffs2/compr.c | 6 +
+ fs/jffs2/compr.h | 10 +-
+ fs/jffs2/compr_lzma.c | 128 +++
+ fs/jffs2/super.c | 33 +-
+ include/linux/lzma.h | 62 ++
+ include/linux/lzma/LzFind.h | 115 +++
+ include/linux/lzma/LzHash.h | 54 +
+ include/linux/lzma/LzmaDec.h | 231 +++++
+ include/linux/lzma/LzmaEnc.h | 80 ++
+ include/linux/lzma/Types.h | 226 +++++
+ include/uapi/linux/jffs2.h | 1 +
+ lib/Kconfig | 6 +
+ lib/Makefile | 12 +
+ lib/lzma/LzFind.c | 761 ++++++++++++++
+ lib/lzma/LzmaDec.c | 999 +++++++++++++++++++
+ lib/lzma/LzmaEnc.c | 2271 ++++++++++++++++++++++++++++++++++++++++++
+ lib/lzma/Makefile | 7 +
+ 19 files changed, 5008 insertions(+), 6 deletions(-)
+ create mode 100644 fs/jffs2/compr_lzma.c
+ create mode 100644 include/linux/lzma.h
+ create mode 100644 include/linux/lzma/LzFind.h
+ create mode 100644 include/linux/lzma/LzHash.h
+ create mode 100644 include/linux/lzma/LzmaDec.h
+ create mode 100644 include/linux/lzma/LzmaEnc.h
+ create mode 100644 include/linux/lzma/Types.h
+ create mode 100644 lib/lzma/LzFind.c
+ create mode 100644 lib/lzma/LzmaDec.c
+ create mode 100644 lib/lzma/LzmaEnc.c
+ create mode 100644 lib/lzma/Makefile
+
+--- a/fs/jffs2/Kconfig
++++ b/fs/jffs2/Kconfig
+@@ -136,6 +136,15 @@ config JFFS2_LZO
+ This feature was added in July, 2007. Say 'N' if you need
+ compatibility with older bootloaders or kernels.
+
++config JFFS2_LZMA
++ bool "JFFS2 LZMA compression support" if JFFS2_COMPRESSION_OPTIONS
++ select LZMA_COMPRESS
++ select LZMA_DECOMPRESS
++ depends on JFFS2_FS
++ default n
++ help
++ JFFS2 wrapper to the LZMA C SDK
++
+ config JFFS2_RTIME
+ bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
+ depends on JFFS2_FS
+--- a/fs/jffs2/Makefile
++++ b/fs/jffs2/Makefile
+@@ -19,4 +19,7 @@ jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rub
+ jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
+ jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
+ jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o
++jffs2-$(CONFIG_JFFS2_LZMA) += compr_lzma.o
+ jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o
++
++CFLAGS_compr_lzma.o += -Iinclude/linux -Ilib/lzma
+--- a/fs/jffs2/compr.c
++++ b/fs/jffs2/compr.c
+@@ -378,6 +378,9 @@ int __init jffs2_compressors_init(void)
+ #ifdef CONFIG_JFFS2_LZO
+ jffs2_lzo_init();
+ #endif
++#ifdef CONFIG_JFFS2_LZMA
++ jffs2_lzma_init();
++#endif
+ /* Setting default compression mode */
+ #ifdef CONFIG_JFFS2_CMODE_NONE
+ jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+@@ -401,6 +404,9 @@ int __init jffs2_compressors_init(void)
+ int jffs2_compressors_exit(void)
+ {
+ /* Unregistering compressors */
++#ifdef CONFIG_JFFS2_LZMA
++ jffs2_lzma_exit();
++#endif
+ #ifdef CONFIG_JFFS2_LZO
+ jffs2_lzo_exit();
+ #endif
+--- a/fs/jffs2/compr.h
++++ b/fs/jffs2/compr.h
+@@ -29,9 +29,9 @@
+ #define JFFS2_DYNRUBIN_PRIORITY 20
+ #define JFFS2_LZARI_PRIORITY 30
+ #define JFFS2_RTIME_PRIORITY 50
+-#define JFFS2_ZLIB_PRIORITY 60
+-#define JFFS2_LZO_PRIORITY 80
+-
++#define JFFS2_LZMA_PRIORITY 70
++#define JFFS2_ZLIB_PRIORITY 80
++#define JFFS2_LZO_PRIORITY 90
+
+ #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
+ #define JFFS2_DYNRUBIN_DISABLED /* for decompression */
+@@ -101,5 +101,9 @@ void jffs2_zlib_exit(void);
+ int jffs2_lzo_init(void);
+ void jffs2_lzo_exit(void);
+ #endif
++#ifdef CONFIG_JFFS2_LZMA
++int jffs2_lzma_init(void);
++void jffs2_lzma_exit(void);
++#endif
+
+ #endif /* __JFFS2_COMPR_H__ */
+--- /dev/null
++++ b/fs/jffs2/compr_lzma.c
+@@ -0,0 +1,128 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * JFFS2 wrapper to the LZMA C SDK
++ *
++ */
++
++#include <linux/lzma.h>
++#include "compr.h"
++
++#ifdef __KERNEL__
++ static DEFINE_MUTEX(deflate_mutex);
++#endif
++
++CLzmaEncHandle *p;
++Byte propsEncoded[LZMA_PROPS_SIZE];
++SizeT propsSize = sizeof(propsEncoded);
++
++STATIC void lzma_free_workspace(void)
++{
++ LzmaEnc_Destroy(p, &lzma_alloc, &lzma_alloc);
++}
++
++STATIC int INIT lzma_alloc_workspace(CLzmaEncProps *props)
++{
++ if ((p = (CLzmaEncHandle *)LzmaEnc_Create(&lzma_alloc)) == NULL)
++ {
++ PRINT_ERROR("Failed to allocate lzma deflate workspace\n");
++ return -ENOMEM;
++ }
++
++ if (LzmaEnc_SetProps(p, props) != SZ_OK)
++ {
++ lzma_free_workspace();
++ return -1;
++ }
++
++ if (LzmaEnc_WriteProperties(p, propsEncoded, &propsSize) != SZ_OK)
++ {
++ lzma_free_workspace();
++ return -1;
++ }
++
++ return 0;
++}
++
++STATIC int jffs2_lzma_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
++{
++ SizeT compress_size = (SizeT)(*dstlen);
++ int ret;
++
++ #ifdef __KERNEL__
++ mutex_lock(&deflate_mutex);
++ #endif
++
++ ret = LzmaEnc_MemEncode(p, cpage_out, &compress_size, data_in, *sourcelen,
++ 0, NULL, &lzma_alloc, &lzma_alloc);
++
++ #ifdef __KERNEL__
++ mutex_unlock(&deflate_mutex);
++ #endif
++
++ if (ret != SZ_OK)
++ return -1;
++
++ *dstlen = (uint32_t)compress_size;
++
++ return 0;
++}
++
++STATIC int jffs2_lzma_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t srclen, uint32_t destlen)
++{
++ int ret;
++ SizeT dl = (SizeT)destlen;
++ SizeT sl = (SizeT)srclen;
++ ELzmaStatus status;
++
++ ret = LzmaDecode(cpage_out, &dl, data_in, &sl, propsEncoded,
++ propsSize, LZMA_FINISH_ANY, &status, &lzma_alloc);
++
++ if (ret != SZ_OK || status == LZMA_STATUS_NOT_FINISHED || dl != (SizeT)destlen)
++ return -1;
++
++ return 0;
++}
++
++static struct jffs2_compressor jffs2_lzma_comp = {
++ .priority = JFFS2_LZMA_PRIORITY,
++ .name = "lzma",
++ .compr = JFFS2_COMPR_LZMA,
++ .compress = &jffs2_lzma_compress,
++ .decompress = &jffs2_lzma_decompress,
++ .disabled = 0,
++};
++
++int INIT jffs2_lzma_init(void)
++{
++ int ret;
++ CLzmaEncProps props;
++ LzmaEncProps_Init(&props);
++
++ props.dictSize = LZMA_BEST_DICT(0x2000);
++ props.level = LZMA_BEST_LEVEL;
++ props.lc = LZMA_BEST_LC;
++ props.lp = LZMA_BEST_LP;
++ props.pb = LZMA_BEST_PB;
++ props.fb = LZMA_BEST_FB;
++
++ ret = lzma_alloc_workspace(&props);
++ if (ret < 0)
++ return ret;
++
++ ret = jffs2_register_compressor(&jffs2_lzma_comp);
++ if (ret)
++ lzma_free_workspace();
++
++ return ret;
++}
++
++void jffs2_lzma_exit(void)
++{
++ jffs2_unregister_compressor(&jffs2_lzma_comp);
++ lzma_free_workspace();
++}
+--- a/fs/jffs2/super.c
++++ b/fs/jffs2/super.c
+@@ -374,14 +374,41 @@ static int __init init_jffs2_fs(void)
+ BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68);
+ BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32);
+
+- pr_info("version 2.2."
++ pr_info("version 2.2"
+ #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+ " (NAND)"
+ #endif
+ #ifdef CONFIG_JFFS2_SUMMARY
+- " (SUMMARY) "
++ " (SUMMARY)"
+ #endif
+- " © 2001-2006 Red Hat, Inc.\n");
++#ifdef CONFIG_JFFS2_ZLIB
++ " (ZLIB)"
++#endif
++#ifdef CONFIG_JFFS2_LZO
++ " (LZO)"
++#endif
++#ifdef CONFIG_JFFS2_LZMA
++ " (LZMA)"
++#endif
++#ifdef CONFIG_JFFS2_RTIME
++ " (RTIME)"
++#endif
++#ifdef CONFIG_JFFS2_RUBIN
++ " (RUBIN)"
++#endif
++#ifdef CONFIG_JFFS2_CMODE_NONE
++ " (CMODE_NONE)"
++#endif
++#ifdef CONFIG_JFFS2_CMODE_PRIORITY
++ " (CMODE_PRIORITY)"
++#endif
++#ifdef CONFIG_JFFS2_CMODE_SIZE
++ " (CMODE_SIZE)"
++#endif
++#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO
++ " (CMODE_FAVOURLZO)"
++#endif
++ " (c) 2001-2006 Red Hat, Inc.\n");
+
+ jffs2_inode_cachep = kmem_cache_create("jffs2_i",
+ sizeof(struct jffs2_inode_info),
+--- /dev/null
++++ b/include/linux/lzma.h
+@@ -0,0 +1,62 @@
++#ifndef __LZMA_H__
++#define __LZMA_H__
++
++#ifdef __KERNEL__
++ #include <linux/kernel.h>
++ #include <linux/sched.h>
++ #include <linux/slab.h>
++ #include <linux/vmalloc.h>
++ #include <linux/init.h>
++ #define LZMA_MALLOC vmalloc
++ #define LZMA_FREE vfree
++ #define PRINT_ERROR(msg) printk(KERN_WARNING #msg)
++ #define INIT __init
++ #define STATIC static
++#else
++ #include <stdint.h>
++ #include <stdlib.h>
++ #include <stdio.h>
++ #include <unistd.h>
++ #include <string.h>
++ #include <asm/types.h>
++ #include <errno.h>
++ #include <linux/jffs2.h>
++ #ifndef PAGE_SIZE
++ extern int page_size;
++ #define PAGE_SIZE page_size
++ #endif
++ #define LZMA_MALLOC malloc
++ #define LZMA_FREE free
++ #define PRINT_ERROR(msg) fprintf(stderr, msg)
++ #define INIT
++ #define STATIC
++#endif
++
++#include "lzma/LzmaDec.h"
++#include "lzma/LzmaEnc.h"
++
++#define LZMA_BEST_LEVEL (9)
++#define LZMA_BEST_LC (0)
++#define LZMA_BEST_LP (0)
++#define LZMA_BEST_PB (0)
++#define LZMA_BEST_FB (273)
++
++#define LZMA_BEST_DICT(n) (((int)((n) / 2)) * 2)
++
++static void *p_lzma_malloc(void *p, size_t size)
++{
++ if (size == 0)
++ return NULL;
++
++ return LZMA_MALLOC(size);
++}
++
++static void p_lzma_free(void *p, void *address)
++{
++ if (address != NULL)
++ LZMA_FREE(address);
++}
++
++static ISzAlloc lzma_alloc = {p_lzma_malloc, p_lzma_free};
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/LzFind.h
+@@ -0,0 +1,98 @@
++/* LzFind.h -- Match finder for LZ algorithms
++2009-04-22 : Igor Pavlov : Public domain */
++
++#ifndef __LZ_FIND_H
++#define __LZ_FIND_H
++
++#include "Types.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++typedef UInt32 CLzRef;
++
++typedef struct _CMatchFinder
++{
++ Byte *buffer;
++ UInt32 pos;
++ UInt32 posLimit;
++ UInt32 streamPos;
++ UInt32 lenLimit;
++
++ UInt32 cyclicBufferPos;
++ UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */
++
++ UInt32 matchMaxLen;
++ CLzRef *hash;
++ CLzRef *son;
++ UInt32 hashMask;
++ UInt32 cutValue;
++
++ Byte *bufferBase;
++ ISeqInStream *stream;
++ int streamEndWasReached;
++
++ UInt32 blockSize;
++ UInt32 keepSizeBefore;
++ UInt32 keepSizeAfter;
++
++ UInt32 numHashBytes;
++ int directInput;
++ size_t directInputRem;
++ int btMode;
++ int bigHash;
++ UInt32 historySize;
++ UInt32 fixedHashSize;
++ UInt32 hashSizeSum;
++ UInt32 numSons;
++ SRes result;
++ UInt32 crc[256];
++} CMatchFinder;
++
++#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer)
++#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)])
++
++#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos)
++
++void MatchFinder_Construct(CMatchFinder *p);
++
++/* Conditions:
++ historySize <= 3 GB
++ keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB
++*/
++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
++ ISzAlloc *alloc);
++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc);
++
++/*
++Conditions:
++ Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func.
++ Mf_GetPointerToCurrentPos_Func's result must be used only before any other function
++*/
++
++typedef void (*Mf_Init_Func)(void *object);
++typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index);
++typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object);
++typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object);
++typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances);
++typedef void (*Mf_Skip_Func)(void *object, UInt32);
++
++typedef struct _IMatchFinder
++{
++ Mf_Init_Func Init;
++ Mf_GetIndexByte_Func GetIndexByte;
++ Mf_GetNumAvailableBytes_Func GetNumAvailableBytes;
++ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos;
++ Mf_GetMatches_Func GetMatches;
++ Mf_Skip_Func Skip;
++} IMatchFinder;
++
++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/LzHash.h
+@@ -0,0 +1,54 @@
++/* LzHash.h -- HASH functions for LZ algorithms
++2009-02-07 : Igor Pavlov : Public domain */
++
++#ifndef __LZ_HASH_H
++#define __LZ_HASH_H
++
++#define kHash2Size (1 << 10)
++#define kHash3Size (1 << 16)
++#define kHash4Size (1 << 20)
++
++#define kFix3HashSize (kHash2Size)
++#define kFix4HashSize (kHash2Size + kHash3Size)
++#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size)
++
++#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8);
++
++#define HASH3_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; }
++
++#define HASH4_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
++ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; }
++
++#define HASH5_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \
++ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \
++ hash4Value &= (kHash4Size - 1); }
++
++/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */
++#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF;
++
++
++#define MT_HASH2_CALC \
++ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1);
++
++#define MT_HASH3_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); }
++
++#define MT_HASH4_CALC { \
++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
++ hash2Value = temp & (kHash2Size - 1); \
++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); }
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/LzmaDec.h
+@@ -0,0 +1,130 @@
++/* LzmaDec.h -- LZMA Decoder
++2009-02-07 : Igor Pavlov : Public domain */
++
++#ifndef __LZMA_DEC_H
++#define __LZMA_DEC_H
++
++#include "Types.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/* #define _LZMA_PROB32 */
++/* _LZMA_PROB32 can increase the speed on some CPUs,
++ but memory usage for CLzmaDec::probs will be doubled in that case */
++
++#ifdef _LZMA_PROB32
++#define CLzmaProb UInt32
++#else
++#define CLzmaProb UInt16
++#endif
++
++
++/* ---------- LZMA Properties ---------- */
++
++#define LZMA_PROPS_SIZE 5
++
++typedef struct _CLzmaProps
++{
++ unsigned lc, lp, pb;
++ UInt32 dicSize;
++} CLzmaProps;
++
++
++/* ---------- LZMA Decoder state ---------- */
++
++/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
++ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */
++
++#define LZMA_REQUIRED_INPUT_MAX 20
++
++typedef struct
++{
++ CLzmaProps prop;
++ CLzmaProb *probs;
++ Byte *dic;
++ const Byte *buf;
++ UInt32 range, code;
++ SizeT dicPos;
++ SizeT dicBufSize;
++ UInt32 processedPos;
++ UInt32 checkDicSize;
++ unsigned state;
++ UInt32 reps[4];
++ unsigned remainLen;
++ int needFlush;
++ int needInitState;
++ UInt32 numProbs;
++ unsigned tempBufSize;
++ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX];
++} CLzmaDec;
++
++#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; }
++
++/* 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. */
++
++typedef enum
++{
++ LZMA_FINISH_ANY, /* finish at any point */
++ LZMA_FINISH_END /* block must be finished at the end */
++} ELzmaFinishMode;
++
++/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!
++
++ You must use LZMA_FINISH_END, when you know that current output buffer
++ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.
++
++ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK,
++ and output value of destLen will be less than output buffer size limit.
++ You can check status result also.
++
++ You can use multiple checks to test data integrity after full decompression:
++ 1) Check Result and "status" variable.
++ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
++ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
++ You must use correct finish mode in that case. */
++
++typedef enum
++{
++ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */
++ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
++ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */
++ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */
++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */
++} ELzmaStatus;
++
++/* ELzmaStatus is used only as output value for function call */
++
++/* ---------- One Call Interface ---------- */
++
++/* LzmaDecode
++
++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).
++
++Returns:
++ SZ_OK
++ status:
++ LZMA_STATUS_FINISHED_WITH_MARK
++ LZMA_STATUS_NOT_FINISHED
++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
++ SZ_ERROR_DATA - Data error
++ SZ_ERROR_MEM - Memory allocation error
++ SZ_ERROR_UNSUPPORTED - Unsupported properties
++ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
++*/
++
++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
++ ELzmaStatus *status, ISzAlloc *alloc);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/LzmaEnc.h
+@@ -0,0 +1,60 @@
++/* LzmaEnc.h -- LZMA Encoder
++2009-02-07 : Igor Pavlov : Public domain */
++
++#ifndef __LZMA_ENC_H
++#define __LZMA_ENC_H
++
++#include "Types.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define LZMA_PROPS_SIZE 5
++
++typedef struct _CLzmaEncProps
++{
++ int level; /* 0 <= level <= 9 */
++ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version
++ (1 << 12) <= dictSize <= (1 << 30) for 64-bit version
++ default = (1 << 24) */
++ int lc; /* 0 <= lc <= 8, default = 3 */
++ int lp; /* 0 <= lp <= 4, default = 0 */
++ int pb; /* 0 <= pb <= 4, default = 2 */
++ int algo; /* 0 - fast, 1 - normal, default = 1 */
++ int fb; /* 5 <= fb <= 273, default = 32 */
++ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */
++ int numHashBytes; /* 2, 3 or 4, default = 4 */
++ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */
++ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */
++ int numThreads; /* 1 or 2, default = 2 */
++} CLzmaEncProps;
++
++void LzmaEncProps_Init(CLzmaEncProps *p);
++
++/* ---------- CLzmaEncHandle Interface ---------- */
++
++/* LzmaEnc_* functions can return the following exit codes:
++Returns:
++ SZ_OK - OK
++ SZ_ERROR_MEM - Memory allocation error
++ SZ_ERROR_PARAM - Incorrect paramater in props
++ SZ_ERROR_WRITE - Write callback error.
++ SZ_ERROR_PROGRESS - some break from progress callback
++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
++*/
++
++typedef void * CLzmaEncHandle;
++
++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc);
++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_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+--- /dev/null
++++ b/include/linux/lzma/Types.h
+@@ -0,0 +1,226 @@
++/* Types.h -- Basic types
++2009-11-23 : Igor Pavlov : Public domain */
++
++#ifndef __7Z_TYPES_H
++#define __7Z_TYPES_H
++
++#include <stddef.h>
++
++#ifdef _WIN32
++#include <windows.h>
++#endif
++
++#ifndef EXTERN_C_BEGIN
++#ifdef __cplusplus
++#define EXTERN_C_BEGIN extern "C" {
++#define EXTERN_C_END }
++#else
++#define EXTERN_C_BEGIN
++#define EXTERN_C_END
++#endif
++#endif
++
++EXTERN_C_BEGIN
++
++#define SZ_OK 0
++
++#define SZ_ERROR_DATA 1
++#define SZ_ERROR_MEM 2
++#define SZ_ERROR_CRC 3
++#define SZ_ERROR_UNSUPPORTED 4
++#define SZ_ERROR_PARAM 5
++#define SZ_ERROR_INPUT_EOF 6
++#define SZ_ERROR_OUTPUT_EOF 7
++#define SZ_ERROR_READ 8
++#define SZ_ERROR_WRITE 9
++#define SZ_ERROR_PROGRESS 10
++#define SZ_ERROR_FAIL 11
++#define SZ_ERROR_THREAD 12
++
++#define SZ_ERROR_ARCHIVE 16
++#define SZ_ERROR_NO_ARCHIVE 17
++
++typedef int SRes;
++
++#ifdef _WIN32
++typedef DWORD WRes;
++#else
++typedef int WRes;
++#endif
++
++#ifndef RINOK
++#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; }
++#endif
++
++typedef unsigned char Byte;
++typedef short Int16;
++typedef unsigned short UInt16;
++
++#ifdef _LZMA_UINT32_IS_ULONG
++typedef long Int32;
++typedef unsigned long UInt32;
++#else
++typedef int Int32;
++typedef unsigned int UInt32;
++#endif
++
++#ifdef _SZ_NO_INT_64
++
++/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
++ NOTES: Some code will work incorrectly in that case! */
++
++typedef long Int64;
++typedef unsigned long UInt64;
++
++#else
++
++#if defined(_MSC_VER) || defined(__BORLANDC__)
++typedef __int64 Int64;
++typedef unsigned __int64 UInt64;
++#else
++typedef long long int Int64;
++typedef unsigned long long int UInt64;
++#endif
++
++#endif
++
++#ifdef _LZMA_NO_SYSTEM_SIZE_T
++typedef UInt32 SizeT;
++#else
++typedef size_t SizeT;
++#endif
++
++typedef int Bool;
++#define True 1
++#define False 0
++
++
++#ifdef _WIN32
++#define MY_STD_CALL __stdcall
++#else
++#define MY_STD_CALL
++#endif
++
++#ifdef _MSC_VER
++
++#if _MSC_VER >= 1300
++#define MY_NO_INLINE __declspec(noinline)
++#else
++#define MY_NO_INLINE
++#endif
++
++#define MY_CDECL __cdecl
++#define MY_FAST_CALL __fastcall
++
++#else
++
++#define MY_CDECL
++#define MY_FAST_CALL
++
++#endif
++
++
++/* The following interfaces use first parameter as pointer to structure */
++
++typedef struct
++{
++ SRes (*Read)(void *p, void *buf, size_t *size);
++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
++ (output(*size) < input(*size)) is allowed */
++} ISeqInStream;
++
++/* it can return SZ_ERROR_INPUT_EOF */
++SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size);
++SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType);
++SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf);
++
++typedef struct
++{
++ size_t (*Write)(void *p, const void *buf, size_t size);
++ /* Returns: result - the number of actually written bytes.
++ (result < size) means error */
++} ISeqOutStream;
++
++typedef enum
++{
++ SZ_SEEK_SET = 0,
++ SZ_SEEK_CUR = 1,
++ SZ_SEEK_END = 2
++} ESzSeek;
++
++typedef struct
++{
++ SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */
++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
++} ISeekInStream;
++
++typedef struct
++{
++ SRes (*Look)(void *p, void **buf, size_t *size);
++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
++ (output(*size) > input(*size)) is not allowed
++ (output(*size) < input(*size)) is allowed */
++ SRes (*Skip)(void *p, size_t offset);
++ /* offset must be <= output(*size) of Look */
++
++ SRes (*Read)(void *p, void *buf, size_t *size);
++ /* reads directly (without buffer). It's same as ISeqInStream::Read */
++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
++} ILookInStream;
++
++SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size);
++SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset);
++
++/* reads via ILookInStream::Read */
++SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType);
++SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size);
++
++#define LookToRead_BUF_SIZE (1 << 14)
++
++typedef struct
++{
++ ILookInStream s;
++ ISeekInStream *realStream;
++ size_t pos;
++ size_t size;
++ Byte buf[LookToRead_BUF_SIZE];
++} CLookToRead;
++
++void LookToRead_CreateVTable(CLookToRead *p, int lookahead);
++void LookToRead_Init(CLookToRead *p);
++
++typedef struct
++{
++ ISeqInStream s;
++ ILookInStream *realStream;
++} CSecToLook;
++
++void SecToLook_CreateVTable(CSecToLook *p);
++
++typedef struct
++{
++ ISeqInStream s;
++ ILookInStream *realStream;
++} CSecToRead;
++
++void SecToRead_CreateVTable(CSecToRead *p);
++
++typedef struct
++{
++ SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize);
++ /* Returns: result. (result != SZ_OK) means break.
++ Value (UInt64)(Int64)-1 for size means unknown value. */
++} ICompressProgress;
++
++typedef struct
++{
++ void *(*Alloc)(void *p, size_t size);
++ void (*Free)(void *p, void *address); /* address can be 0 */
++} ISzAlloc;
++
++#define IAlloc_Alloc(p, size) (p)->Alloc((p), size)
++#define IAlloc_Free(p, a) (p)->Free((p), a)
++
++EXTERN_C_END
++
++#endif
+--- a/include/uapi/linux/jffs2.h
++++ b/include/uapi/linux/jffs2.h
+@@ -46,6 +46,7 @@
+ #define JFFS2_COMPR_DYNRUBIN 0x05
+ #define JFFS2_COMPR_ZLIB 0x06
+ #define JFFS2_COMPR_LZO 0x07
++#define JFFS2_COMPR_LZMA 0x08
+ /* Compatibility flags. */
+ #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
+ #define JFFS2_NODE_ACCURATE 0x2000
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -340,6 +340,12 @@ config ZSTD_DECOMPRESS
+
+ source "lib/xz/Kconfig"
+
++config LZMA_COMPRESS
++ tristate
++
++config LZMA_DECOMPRESS
++ tristate
++
+ #
+ # These all provide a common interface (hence the apparent duplication with
+ # ZLIB_INFLATE; DECOMPRESS_GZIP is just a wrapper.)
+--- a/lib/Makefile
++++ b/lib/Makefile
+@@ -135,6 +135,16 @@ CFLAGS_kobject.o += -DDEBUG
+ CFLAGS_kobject_uevent.o += -DDEBUG
+ endif
+
++ifdef CONFIG_JFFS2_ZLIB
++ CONFIG_ZLIB_INFLATE:=y
++ CONFIG_ZLIB_DEFLATE:=y
++endif
++
++ifdef CONFIG_JFFS2_LZMA
++ CONFIG_LZMA_DECOMPRESS:=y
++ CONFIG_LZMA_COMPRESS:=y
++endif
++
+ obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o
+ CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any)
+
+@@ -192,6 +202,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/
+ obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/
+ obj-$(CONFIG_XZ_DEC) += xz/
+ obj-$(CONFIG_RAID6_PQ) += raid6/
++obj-$(CONFIG_LZMA_COMPRESS) += lzma/
++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma/
+
+ lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o
+ lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o
+--- /dev/null
++++ b/lib/lzma/LzFind.c
+@@ -0,0 +1,522 @@
++/* LzFind.c -- Match finder for LZ algorithms
++2009-04-22 : Igor Pavlov : Public domain */
++
++#include <string.h>
++
++#include "LzFind.h"
++#include "LzHash.h"
++
++#define kEmptyHashValue 0
++#define kMaxValForNormalize ((UInt32)0xFFFFFFFF)
++#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */
++#define kNormalizeMask (~(kNormalizeStepMin - 1))
++#define kMaxHistorySize ((UInt32)3 << 30)
++
++#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 (!DIRECT_INPUT)
++ {
++ alloc->Free(alloc, p->bufferBase);
++ p->bufferBase = 0;
++ }
++}
++
++/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */
++
++static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc)
++{
++ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv;
++ if (DIRECT_INPUT)
++ {
++ p->blockSize = blockSize;
++ return 1;
++ }
++ if (p->bufferBase == 0 || p->blockSize != blockSize)
++ {
++ LzInWindow_Free(p, alloc);
++ p->blockSize = blockSize;
++ p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize);
++ }
++ return (p->bufferBase != 0);
++}
++
++static Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
++static Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
++
++static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
++
++static void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
++{
++ p->posLimit -= subValue;
++ p->pos -= subValue;
++ p->streamPos -= subValue;
++}
++
++static void MatchFinder_ReadBlock(CMatchFinder *p)
++{
++ if (p->streamEndWasReached || p->result != SZ_OK)
++ return;
++ if (DIRECT_INPUT)
++ {
++ UInt32 curSize = 0xFFFFFFFF - p->streamPos;
++ if (curSize > p->directInputRem)
++ curSize = (UInt32)p->directInputRem;
++ p->directInputRem -= curSize;
++ p->streamPos += curSize;
++ if (p->directInputRem == 0)
++ p->streamEndWasReached = 1;
++ return;
++ }
++ for (;;)
++ {
++ Byte *dest = p->buffer + (p->streamPos - p->pos);
++ size_t size = (p->bufferBase + p->blockSize - dest);
++ if (size == 0)
++ return;
++ p->result = p->stream->Read(p->stream, dest, &size);
++ if (p->result != SZ_OK)
++ return;
++ if (size == 0)
++ {
++ p->streamEndWasReached = 1;
++ return;
++ }
++ p->streamPos += (UInt32)size;
++ if (p->streamPos - p->pos > p->keepSizeAfter)
++ return;
++ }
++}
++
++static void MatchFinder_MoveBlock(CMatchFinder *p)
++{
++ memmove(p->bufferBase,
++ p->buffer - p->keepSizeBefore,
++ (size_t)(p->streamPos - p->pos + p->keepSizeBefore));
++ p->buffer = p->bufferBase + p->keepSizeBefore;
++}
++
++static int MatchFinder_NeedMove(CMatchFinder *p)
++{
++ if (DIRECT_INPUT)
++ return 0;
++ /* if (p->streamEndWasReached) return 0; */
++ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter);
++}
++
++static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p)
++{
++ if (MatchFinder_NeedMove(p))
++ MatchFinder_MoveBlock(p);
++ MatchFinder_ReadBlock(p);
++}
++
++static void MatchFinder_SetDefaultSettings(CMatchFinder *p)
++{
++ p->cutValue = 32;
++ p->btMode = 1;
++ p->numHashBytes = 4;
++ p->bigHash = 0;
++}
++
++#define kCrcPoly 0xEDB88320
++
++void MatchFinder_Construct(CMatchFinder *p)
++{
++ UInt32 i;
++ p->bufferBase = 0;
++ p->directInput = 0;
++ p->hash = 0;
++ MatchFinder_SetDefaultSettings(p);
++
++ for (i = 0; i < 256; i++)
++ {
++ UInt32 r = i;
++ int j;
++ for (j = 0; j < 8; j++)
++ r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
++ p->crc[i] = r;
++ }
++}
++
++static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->hash);
++ p->hash = 0;
++}
++
++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc)
++{
++ MatchFinder_FreeThisClassMemory(p, alloc);
++ LzInWindow_Free(p, alloc);
++}
++
++static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc)
++{
++ size_t sizeInBytes = (size_t)num * sizeof(CLzRef);
++ if (sizeInBytes / sizeof(CLzRef) != num)
++ return 0;
++ return (CLzRef *)alloc->Alloc(alloc, sizeInBytes);
++}
++
++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
++ ISzAlloc *alloc)
++{
++ UInt32 sizeReserv;
++ if (historySize > kMaxHistorySize)
++ {
++ MatchFinder_Free(p, alloc);
++ return 0;
++ }
++ sizeReserv = historySize >> 1;
++ if (historySize > ((UInt32)2 << 30))
++ sizeReserv = historySize >> 2;
++ sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19);
++
++ p->keepSizeBefore = historySize + keepAddBufferBefore + 1;
++ p->keepSizeAfter = matchMaxLen + keepAddBufferAfter;
++ /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */
++ if (LzInWindow_Create(p, sizeReserv, alloc))
++ {
++ UInt32 newCyclicBufferSize = historySize + 1;
++ UInt32 hs;
++ p->matchMaxLen = matchMaxLen;
++ {
++ p->fixedHashSize = 0;
++ if (p->numHashBytes == 2)
++ hs = (1 << 16) - 1;
++ else
++ {
++ hs = historySize - 1;
++ hs |= (hs >> 1);
++ hs |= (hs >> 2);
++ hs |= (hs >> 4);
++ hs |= (hs >> 8);
++ hs >>= 1;
++ hs |= 0xFFFF; /* don't change it! It's required for Deflate */
++ if (hs > (1 << 24))
++ {
++ if (p->numHashBytes == 3)
++ hs = (1 << 24) - 1;
++ else
++ hs >>= 1;
++ }
++ }
++ p->hashMask = hs;
++ hs++;
++ if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size;
++ if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size;
++ if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size;
++ hs += p->fixedHashSize;
++ }
++
++ {
++ UInt32 prevSize = p->hashSizeSum + p->numSons;
++ UInt32 newSize;
++ p->historySize = historySize;
++ p->hashSizeSum = hs;
++ p->cyclicBufferSize = newCyclicBufferSize;
++ p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize);
++ newSize = p->hashSizeSum + p->numSons;
++ if (p->hash != 0 && prevSize == newSize)
++ return 1;
++ MatchFinder_FreeThisClassMemory(p, alloc);
++ p->hash = AllocRefs(newSize, alloc);
++ if (p->hash != 0)
++ {
++ p->son = p->hash + p->hashSizeSum;
++ return 1;
++ }
++ }
++ }
++ MatchFinder_Free(p, alloc);
++ return 0;
++}
++
++static void MatchFinder_SetLimits(CMatchFinder *p)
++{
++ UInt32 limit = kMaxValForNormalize - p->pos;
++ UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos;
++ if (limit2 < limit)
++ limit = limit2;
++ limit2 = p->streamPos - p->pos;
++ if (limit2 <= p->keepSizeAfter)
++ {
++ if (limit2 > 0)
++ limit2 = 1;
++ }
++ else
++ limit2 -= p->keepSizeAfter;
++ if (limit2 < limit)
++ limit = limit2;
++ {
++ UInt32 lenLimit = p->streamPos - p->pos;
++ if (lenLimit > p->matchMaxLen)
++ lenLimit = p->matchMaxLen;
++ p->lenLimit = lenLimit;
++ }
++ p->posLimit = p->pos + limit;
++}
++
++static void MatchFinder_Init(CMatchFinder *p)
++{
++ UInt32 i;
++ for (i = 0; i < p->hashSizeSum; i++)
++ p->hash[i] = kEmptyHashValue;
++ p->cyclicBufferPos = 0;
++ p->buffer = p->bufferBase;
++ p->pos = p->streamPos = p->cyclicBufferSize;
++ p->result = SZ_OK;
++ p->streamEndWasReached = 0;
++ MatchFinder_ReadBlock(p);
++ MatchFinder_SetLimits(p);
++}
++
++static UInt32 MatchFinder_GetSubValue(CMatchFinder *p)
++{
++ return (p->pos - p->historySize - 1) & kNormalizeMask;
++}
++
++static void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
++{
++ UInt32 i;
++ for (i = 0; i < numItems; i++)
++ {
++ UInt32 value = items[i];
++ if (value <= subValue)
++ value = kEmptyHashValue;
++ else
++ value -= subValue;
++ items[i] = value;
++ }
++}
++
++static void MatchFinder_Normalize(CMatchFinder *p)
++{
++ UInt32 subValue = MatchFinder_GetSubValue(p);
++ MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons);
++ MatchFinder_ReduceOffsets(p, subValue);
++}
++
++static void MatchFinder_CheckLimits(CMatchFinder *p)
++{
++ if (p->pos == kMaxValForNormalize)
++ MatchFinder_Normalize(p);
++ if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos)
++ MatchFinder_CheckAndMoveAndRead(p);
++ if (p->cyclicBufferPos == p->cyclicBufferSize)
++ p->cyclicBufferPos = 0;
++ MatchFinder_SetLimits(p);
++}
++
++static UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
++ UInt32 *distances, UInt32 maxLen)
++{
++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
++ UInt32 len0 = 0, len1 = 0;
++ for (;;)
++ {
++ UInt32 delta = pos - curMatch;
++ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
++ {
++ *ptr0 = *ptr1 = kEmptyHashValue;
++ return distances;
++ }
++ {
++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
++ const Byte *pb = cur - delta;
++ UInt32 len = (len0 < len1 ? len0 : len1);
++ if (pb[len] == cur[len])
++ {
++ if (++len != lenLimit && pb[len] == cur[len])
++ while (++len != lenLimit)
++ if (pb[len] != cur[len])
++ break;
++ if (maxLen < len)
++ {
++ *distances++ = maxLen = len;
++ *distances++ = delta - 1;
++ if (len == lenLimit)
++ {
++ *ptr1 = pair[0];
++ *ptr0 = pair[1];
++ return distances;
++ }
++ }
++ }
++ if (pb[len] < cur[len])
++ {
++ *ptr1 = curMatch;
++ ptr1 = pair + 1;
++ curMatch = *ptr1;
++ len1 = len;
++ }
++ else
++ {
++ *ptr0 = curMatch;
++ ptr0 = pair;
++ curMatch = *ptr0;
++ len0 = len;
++ }
++ }
++ }
++}
++
++static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue)
++{
++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
++ UInt32 len0 = 0, len1 = 0;
++ for (;;)
++ {
++ UInt32 delta = pos - curMatch;
++ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
++ {
++ *ptr0 = *ptr1 = kEmptyHashValue;
++ return;
++ }
++ {
++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
++ const Byte *pb = cur - delta;
++ UInt32 len = (len0 < len1 ? len0 : len1);
++ if (pb[len] == cur[len])
++ {
++ while (++len != lenLimit)
++ if (pb[len] != cur[len])
++ break;
++ {
++ if (len == lenLimit)
++ {
++ *ptr1 = pair[0];
++ *ptr0 = pair[1];
++ return;
++ }
++ }
++ }
++ if (pb[len] < cur[len])
++ {
++ *ptr1 = curMatch;
++ ptr1 = pair + 1;
++ curMatch = *ptr1;
++ len1 = len;
++ }
++ else
++ {
++ *ptr0 = curMatch;
++ ptr0 = pair;
++ curMatch = *ptr0;
++ len0 = len;
++ }
++ }
++ }
++}
++
++#define MOVE_POS \
++ ++p->cyclicBufferPos; \
++ p->buffer++; \
++ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p);
++
++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; }} \
++ cur = p->buffer;
++
++#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0)
++#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue)
++
++#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue
++
++#define GET_MATCHES_FOOTER(offset, maxLen) \
++ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \
++ distances + offset, maxLen) - distances); MOVE_POS_RET;
++
++#define SKIP_FOOTER \
++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MatchFinder_MovePos(p);
++
++static UInt32 Bt4_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)
++ {
++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
++ MOVE_POS_RET;
++ }
++ }
++ if (maxLen < 3)
++ maxLen = 3;
++ GET_MATCHES_FOOTER(offset, maxLen)
++}
++
++static void Bt4_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->pos;
++ p->hash[kFix4HashSize + hashValue] = p->pos;
++ SKIP_FOOTER
++ }
++ 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;
++ 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,925 @@
++/* LzmaDec.c -- LZMA Decoder
++2009-09-20 : Igor Pavlov : Public domain */
++
++#include "LzmaDec.h"
++
++#include <string.h>
++
++#define kNumTopBits 24
++#define kTopValue ((UInt32)1 << kNumTopBits)
++
++#define kNumBitModelTotalBits 11
++#define kBitModelTotal (1 << kNumBitModelTotalBits)
++#define kNumMoveBits 5
++
++#define RC_INIT_SIZE 5
++
++#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }
++
++#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
++#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
++#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits));
++#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \
++ { UPDATE_0(p); i = (i + i); A0; } else \
++ { UPDATE_1(p); i = (i + i) + 1; A1; }
++#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;)
++
++#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); }
++#define TREE_DECODE(probs, limit, i) \
++ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; }
++
++/* #define _LZMA_SIZE_OPT */
++
++#ifdef _LZMA_SIZE_OPT
++#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i)
++#else
++#define TREE_6_DECODE(probs, i) \
++ { i = 1; \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ TREE_GET_BIT(probs, i); \
++ i -= 0x40; }
++#endif
++
++#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }
++
++#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
++#define UPDATE_0_CHECK range = bound;
++#define UPDATE_1_CHECK range -= bound; code -= bound;
++#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \
++ { UPDATE_0_CHECK; i = (i + i); A0; } else \
++ { UPDATE_1_CHECK; i = (i + i) + 1; A1; }
++#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;)
++#define TREE_DECODE_CHECK(probs, limit, i) \
++ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; }
++
++
++#define kNumPosBitsMax 4
++#define kNumPosStatesMax (1 << kNumPosBitsMax)
++
++#define kLenNumLowBits 3
++#define kLenNumLowSymbols (1 << kLenNumLowBits)
++#define kLenNumMidBits 3
++#define kLenNumMidSymbols (1 << kLenNumMidBits)
++#define kLenNumHighBits 8
++#define kLenNumHighSymbols (1 << kLenNumHighBits)
++
++#define LenChoice 0
++#define LenChoice2 (LenChoice + 1)
++#define LenLow (LenChoice2 + 1)
++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
++#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
++
++
++#define kNumStates 12
++#define kNumLitStates 7
++
++#define kStartPosModelIndex 4
++#define kEndPosModelIndex 14
++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
++
++#define kNumPosSlotBits 6
++#define kNumLenToPosStates 4
++
++#define kNumAlignBits 4
++#define kAlignTableSize (1 << kNumAlignBits)
++
++#define kMatchMinLen 2
++#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
++
++#define IsMatch 0
++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
++#define IsRepG0 (IsRep + kNumStates)
++#define IsRepG1 (IsRepG0 + kNumStates)
++#define IsRepG2 (IsRepG1 + kNumStates)
++#define IsRep0Long (IsRepG2 + kNumStates)
++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
++#define LenCoder (Align + kAlignTableSize)
++#define RepLenCoder (LenCoder + kNumLenProbs)
++#define Literal (RepLenCoder + kNumLenProbs)
++
++#define LZMA_BASE_SIZE 1846
++#define LZMA_LIT_SIZE 768
++
++#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp)))
++
++#if Literal != LZMA_BASE_SIZE
++StopCompilingDueBUG
++#endif
++
++#define LZMA_DIC_MIN (1 << 12)
++
++/* First LZMA-symbol is always decoded.
++And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization
++Out:
++ Result:
++ SZ_OK - OK
++ SZ_ERROR_DATA - Error
++ p->remainLen:
++ < kMatchSpecLenStart : normal remain
++ = kMatchSpecLenStart : finished
++ = kMatchSpecLenStart + 1 : Flush marker
++ = kMatchSpecLenStart + 2 : State Init Marker
++*/
++
++static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
++{
++ CLzmaProb *probs = p->probs;
++
++ unsigned state = p->state;
++ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3];
++ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1;
++ unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1;
++ unsigned lc = p->prop.lc;
++
++ Byte *dic = p->dic;
++ SizeT dicBufSize = p->dicBufSize;
++ SizeT dicPos = p->dicPos;
++
++ UInt32 processedPos = p->processedPos;
++ UInt32 checkDicSize = p->checkDicSize;
++ unsigned len = 0;
++
++ const Byte *buf = p->buf;
++ UInt32 range = p->range;
++ UInt32 code = p->code;
++
++ do
++ {
++ CLzmaProb *prob;
++ UInt32 bound;
++ unsigned ttt;
++ unsigned posState = processedPos & pbMask;
++
++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
++ IF_BIT_0(prob)
++ {
++ unsigned symbol;
++ UPDATE_0(prob);
++ prob = probs + Literal;
++ if (checkDicSize != 0 || processedPos != 0)
++ prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) +
++ (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc))));
++
++ if (state < kNumLitStates)
++ {
++ state -= (state < 4) ? state : 3;
++ symbol = 1;
++ do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100);
++ }
++ else
++ {
++ unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
++ unsigned offs = 0x100;
++ state -= (state < 10) ? 3 : 6;
++ symbol = 1;
++ do
++ {
++ unsigned bit;
++ CLzmaProb *probLit;
++ matchByte <<= 1;
++ bit = (matchByte & offs);
++ probLit = prob + offs + bit + symbol;
++ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit)
++ }
++ while (symbol < 0x100);
++ }
++ dic[dicPos++] = (Byte)symbol;
++ processedPos++;
++ continue;
++ }
++ else
++ {
++ UPDATE_1(prob);
++ prob = probs + IsRep + state;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ state += kNumStates;
++ prob = probs + LenCoder;
++ }
++ else
++ {
++ UPDATE_1(prob);
++ if (checkDicSize == 0 && processedPos == 0)
++ return SZ_ERROR_DATA;
++ prob = probs + IsRepG0 + state;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
++ dicPos++;
++ processedPos++;
++ state = state < kNumLitStates ? 9 : 11;
++ continue;
++ }
++ UPDATE_1(prob);
++ }
++ else
++ {
++ UInt32 distance;
++ UPDATE_1(prob);
++ prob = probs + IsRepG1 + state;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ distance = rep1;
++ }
++ else
++ {
++ UPDATE_1(prob);
++ prob = probs + IsRepG2 + state;
++ IF_BIT_0(prob)
++ {
++ UPDATE_0(prob);
++ distance = rep2;
++ }
++ else
++ {
++ UPDATE_1(prob);
++ distance = rep3;
++ rep3 = rep2;
++ }
++ rep2 = rep1;
++ }
++ rep1 = rep0;
++ rep0 = distance;
++ }
++ state = state < kNumLitStates ? 8 : 11;
++ prob = probs + RepLenCoder;
++ }
++ {
++ unsigned limit, offset;
++ CLzmaProb *probLen = prob + LenChoice;
++ IF_BIT_0(probLen)
++ {
++ UPDATE_0(probLen);
++ probLen = prob + LenLow + (posState << kLenNumLowBits);
++ offset = 0;
++ limit = (1 << kLenNumLowBits);
++ }
++ else
++ {
++ UPDATE_1(probLen);
++ probLen = prob + LenChoice2;
++ IF_BIT_0(probLen)
++ {
++ UPDATE_0(probLen);
++ probLen = prob + LenMid + (posState << kLenNumMidBits);
++ offset = kLenNumLowSymbols;
++ limit = (1 << kLenNumMidBits);
++ }
++ else
++ {
++ UPDATE_1(probLen);
++ probLen = prob + LenHigh;
++ offset = kLenNumLowSymbols + kLenNumMidSymbols;
++ limit = (1 << kLenNumHighBits);
++ }
++ }
++ TREE_DECODE(probLen, limit, len);
++ len += offset;
++ }
++
++ if (state >= kNumStates)
++ {
++ UInt32 distance;
++ prob = probs + PosSlot +
++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits);
++ TREE_6_DECODE(prob, distance);
++ if (distance >= kStartPosModelIndex)
++ {
++ unsigned posSlot = (unsigned)distance;
++ int numDirectBits = (int)(((distance >> 1) - 1));
++ distance = (2 | (distance & 1));
++ if (posSlot < kEndPosModelIndex)
++ {
++ distance <<= numDirectBits;
++ prob = probs + SpecPos + distance - posSlot - 1;
++ {
++ UInt32 mask = 1;
++ unsigned i = 1;
++ do
++ {
++ GET_BIT2(prob + i, i, ; , distance |= mask);
++ mask <<= 1;
++ }
++ while (--numDirectBits != 0);
++ }
++ }
++ else
++ {
++ numDirectBits -= kNumAlignBits;
++ do
++ {
++ NORMALIZE
++ range >>= 1;
++
++ {
++ UInt32 t;
++ code -= range;
++ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */
++ distance = (distance << 1) + (t + 1);
++ code += range & t;
++ }
++ /*
++ distance <<= 1;
++ if (code >= range)
++ {
++ code -= range;
++ distance |= 1;
++ }
++ */
++ }
++ while (--numDirectBits != 0);
++ prob = probs + Align;
++ distance <<= kNumAlignBits;
++ {
++ unsigned i = 1;
++ GET_BIT2(prob + i, i, ; , distance |= 1);
++ GET_BIT2(prob + i, i, ; , distance |= 2);
++ GET_BIT2(prob + i, i, ; , distance |= 4);
++ GET_BIT2(prob + i, i, ; , distance |= 8);
++ }
++ if (distance == (UInt32)0xFFFFFFFF)
++ {
++ len += kMatchSpecLenStart;
++ state -= kNumStates;
++ break;
++ }
++ }
++ }
++ rep3 = rep2;
++ rep2 = rep1;
++ rep1 = rep0;
++ rep0 = distance + 1;
++ if (checkDicSize == 0)
++ {
++ if (distance >= processedPos)
++ return SZ_ERROR_DATA;
++ }
++ else if (distance >= checkDicSize)
++ return SZ_ERROR_DATA;
++ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3;
++ }
++
++ len += kMatchMinLen;
++
++ if (limit == dicPos)
++ return SZ_ERROR_DATA;
++ {
++ SizeT rem = limit - dicPos;
++ unsigned curLen = ((rem < len) ? (unsigned)rem : len);
++ SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0);
++
++ processedPos += curLen;
++
++ len -= curLen;
++ if (pos + curLen <= dicBufSize)
++ {
++ Byte *dest = dic + dicPos;
++ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos;
++ const Byte *lim = dest + curLen;
++ dicPos += curLen;
++ do
++ *(dest) = (Byte)*(dest + src);
++ while (++dest != lim);
++ }
++ else
++ {
++ do
++ {
++ dic[dicPos++] = dic[pos];
++ if (++pos == dicBufSize)
++ pos = 0;
++ }
++ while (--curLen != 0);
++ }
++ }
++ }
++ }
++ while (dicPos < limit && buf < bufLimit);
++ NORMALIZE;
++ p->buf = buf;
++ p->range = range;
++ p->code = code;
++ p->remainLen = len;
++ p->dicPos = dicPos;
++ p->processedPos = processedPos;
++ p->reps[0] = rep0;
++ p->reps[1] = rep1;
++ p->reps[2] = rep2;
++ p->reps[3] = rep3;
++ p->state = state;
++
++ return SZ_OK;
++}
++
++static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit)
++{
++ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart)
++ {
++ Byte *dic = p->dic;
++ SizeT dicPos = p->dicPos;
++ SizeT dicBufSize = p->dicBufSize;
++ unsigned len = p->remainLen;
++ UInt32 rep0 = p->reps[0];
++ if (limit - dicPos < len)
++ len = (unsigned)(limit - dicPos);
++
++ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len)
++ p->checkDicSize = p->prop.dicSize;
++
++ p->processedPos += len;
++ p->remainLen -= len;
++ while (len-- != 0)
++ {
++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
++ dicPos++;
++ }
++ p->dicPos = dicPos;
++ }
++}
++
++static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
++{
++ do
++ {
++ SizeT limit2 = limit;
++ if (p->checkDicSize == 0)
++ {
++ UInt32 rem = p->prop.dicSize - p->processedPos;
++ if (limit - p->dicPos > rem)
++ limit2 = p->dicPos + rem;
++ }
++ RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit));
++ if (p->processedPos >= p->prop.dicSize)
++ p->checkDicSize = p->prop.dicSize;
++ LzmaDec_WriteRem(p, limit);
++ }
++ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart);
++
++ if (p->remainLen > kMatchSpecLenStart)
++ {
++ p->remainLen = kMatchSpecLenStart;
++ }
++ return 0;
++}
++
++typedef enum
++{
++ DUMMY_ERROR, /* unexpected end of input stream */
++ DUMMY_LIT,
++ DUMMY_MATCH,
++ DUMMY_REP
++} ELzmaDummy;
++
++static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize)
++{
++ UInt32 range = p->range;
++ UInt32 code = p->code;
++ const Byte *bufLimit = buf + inSize;
++ CLzmaProb *probs = p->probs;
++ unsigned state = p->state;
++ ELzmaDummy res;
++
++ {
++ CLzmaProb *prob;
++ UInt32 bound;
++ unsigned ttt;
++ unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1);
++
++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK
++
++ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */
++
++ prob = probs + Literal;
++ if (p->checkDicSize != 0 || p->processedPos != 0)
++ prob += (LZMA_LIT_SIZE *
++ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) +
++ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc))));
++
++ if (state < kNumLitStates)
++ {
++ unsigned symbol = 1;
++ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100);
++ }
++ else
++ {
++ unsigned matchByte = p->dic[p->dicPos - p->reps[0] +
++ ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)];
++ unsigned offs = 0x100;
++ unsigned symbol = 1;
++ do
++ {
++ unsigned bit;
++ CLzmaProb *probLit;
++ matchByte <<= 1;
++ bit = (matchByte & offs);
++ probLit = prob + offs + bit + symbol;
++ GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit)
++ }
++ while (symbol < 0x100);
++ }
++ res = DUMMY_LIT;
++ }
++ else
++ {
++ unsigned len;
++ UPDATE_1_CHECK;
++
++ prob = probs + IsRep + state;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ state = 0;
++ prob = probs + LenCoder;
++ res = DUMMY_MATCH;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ res = DUMMY_REP;
++ prob = probs + IsRepG0 + state;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ NORMALIZE_CHECK;
++ return DUMMY_REP;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ }
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ prob = probs + IsRepG1 + state;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ prob = probs + IsRepG2 + state;
++ IF_BIT_0_CHECK(prob)
++ {
++ UPDATE_0_CHECK;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ }
++ }
++ }
++ state = kNumStates;
++ prob = probs + RepLenCoder;
++ }
++ {
++ unsigned limit, offset;
++ CLzmaProb *probLen = prob + LenChoice;
++ IF_BIT_0_CHECK(probLen)
++ {
++ UPDATE_0_CHECK;
++ probLen = prob + LenLow + (posState << kLenNumLowBits);
++ offset = 0;
++ limit = 1 << kLenNumLowBits;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ probLen = prob + LenChoice2;
++ IF_BIT_0_CHECK(probLen)
++ {
++ UPDATE_0_CHECK;
++ probLen = prob + LenMid + (posState << kLenNumMidBits);
++ offset = kLenNumLowSymbols;
++ limit = 1 << kLenNumMidBits;
++ }
++ else
++ {
++ UPDATE_1_CHECK;
++ probLen = prob + LenHigh;
++ offset = kLenNumLowSymbols + kLenNumMidSymbols;
++ limit = 1 << kLenNumHighBits;
++ }
++ }
++ TREE_DECODE_CHECK(probLen, limit, len);
++ len += offset;
++ }
++
++ if (state < 4)
++ {
++ unsigned posSlot;
++ prob = probs + PosSlot +
++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
++ kNumPosSlotBits);
++ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot);
++ if (posSlot >= kStartPosModelIndex)
++ {
++ int numDirectBits = ((posSlot >> 1) - 1);
++
++ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */
++
++ if (posSlot < kEndPosModelIndex)
++ {
++ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1;
++ }
++ else
++ {
++ numDirectBits -= kNumAlignBits;
++ do
++ {
++ NORMALIZE_CHECK
++ range >>= 1;
++ code -= range & (((code - range) >> 31) - 1);
++ /* if (code >= range) code -= range; */
++ }
++ while (--numDirectBits != 0);
++ prob = probs + Align;
++ numDirectBits = kNumAlignBits;
++ }
++ {
++ unsigned i = 1;
++ do
++ {
++ GET_BIT_CHECK(prob + i, i);
++ }
++ while (--numDirectBits != 0);
++ }
++ }
++ }
++ }
++ }
++ NORMALIZE_CHECK;
++ return res;
++}
++
++
++static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data)
++{
++ p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]);
++ p->range = 0xFFFFFFFF;
++ p->needFlush = 0;
++}
++
++static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
++{
++ p->needFlush = 1;
++ p->remainLen = 0;
++ p->tempBufSize = 0;
++
++ if (initDic)
++ {
++ p->processedPos = 0;
++ p->checkDicSize = 0;
++ p->needInitState = 1;
++ }
++ if (initState)
++ p->needInitState = 1;
++}
++
++static void LzmaDec_Init(CLzmaDec *p)
++{
++ p->dicPos = 0;
++ LzmaDec_InitDicAndState(p, True, True);
++}
++
++static void LzmaDec_InitStateReal(CLzmaDec *p)
++{
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp));
++ UInt32 i;
++ CLzmaProb *probs = p->probs;
++ for (i = 0; i < numProbs; i++)
++ probs[i] = kBitModelTotal >> 1;
++ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1;
++ p->state = 0;
++ p->needInitState = 0;
++}
++
++static SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
++ ELzmaFinishMode finishMode, ELzmaStatus *status)
++{
++ SizeT inSize = *srcLen;
++ (*srcLen) = 0;
++ LzmaDec_WriteRem(p, dicLimit);
++
++ *status = LZMA_STATUS_NOT_SPECIFIED;
++
++ while (p->remainLen != kMatchSpecLenStart)
++ {
++ int checkEndMarkNow;
++
++ if (p->needFlush != 0)
++ {
++ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--)
++ p->tempBuf[p->tempBufSize++] = *src++;
++ if (p->tempBufSize < RC_INIT_SIZE)
++ {
++ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
++ return SZ_OK;
++ }
++ if (p->tempBuf[0] != 0)
++ return SZ_ERROR_DATA;
++
++ LzmaDec_InitRc(p, p->tempBuf);
++ p->tempBufSize = 0;
++ }
++
++ checkEndMarkNow = 0;
++ if (p->dicPos >= dicLimit)
++ {
++ if (p->remainLen == 0 && p->code == 0)
++ {
++ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK;
++ return SZ_OK;
++ }
++ if (finishMode == LZMA_FINISH_ANY)
++ {
++ *status = LZMA_STATUS_NOT_FINISHED;
++ return SZ_OK;
++ }
++ if (p->remainLen != 0)
++ {
++ *status = LZMA_STATUS_NOT_FINISHED;
++ return SZ_ERROR_DATA;
++ }
++ checkEndMarkNow = 1;
++ }
++
++ if (p->needInitState)
++ LzmaDec_InitStateReal(p);
++
++ if (p->tempBufSize == 0)
++ {
++ SizeT processed;
++ const Byte *bufLimit;
++ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
++ {
++ int dummyRes = LzmaDec_TryDummy(p, src, inSize);
++ if (dummyRes == DUMMY_ERROR)
++ {
++ memcpy(p->tempBuf, src, inSize);
++ p->tempBufSize = (unsigned)inSize;
++ (*srcLen) += inSize;
++ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
++ return SZ_OK;
++ }
++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
++ {
++ *status = LZMA_STATUS_NOT_FINISHED;
++ return SZ_ERROR_DATA;
++ }
++ bufLimit = src;
++ }
++ else
++ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX;
++ p->buf = src;
++ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0)
++ return SZ_ERROR_DATA;
++ processed = (SizeT)(p->buf - src);
++ (*srcLen) += processed;
++ src += processed;
++ inSize -= processed;
++ }
++ else
++ {
++ unsigned rem = p->tempBufSize, lookAhead = 0;
++ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize)
++ p->tempBuf[rem++] = src[lookAhead++];
++ p->tempBufSize = rem;
++ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
++ {
++ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem);
++ if (dummyRes == DUMMY_ERROR)
++ {
++ (*srcLen) += lookAhead;
++ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
++ return SZ_OK;
++ }
++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
++ {
++ *status = LZMA_STATUS_NOT_FINISHED;
++ return SZ_ERROR_DATA;
++ }
++ }
++ p->buf = p->tempBuf;
++ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0)
++ return SZ_ERROR_DATA;
++ lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf));
++ (*srcLen) += lookAhead;
++ src += lookAhead;
++ inSize -= lookAhead;
++ p->tempBufSize = 0;
++ }
++ }
++ if (p->code == 0)
++ *status = LZMA_STATUS_FINISHED_WITH_MARK;
++ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA;
++}
++
++static void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->probs);
++ p->probs = 0;
++}
++
++static SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
++{
++ UInt32 dicSize;
++ Byte d;
++
++ if (size < LZMA_PROPS_SIZE)
++ return SZ_ERROR_UNSUPPORTED;
++ else
++ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24);
++
++ if (dicSize < LZMA_DIC_MIN)
++ dicSize = LZMA_DIC_MIN;
++ p->dicSize = dicSize;
++
++ d = data[0];
++ if (d >= (9 * 5 * 5))
++ return SZ_ERROR_UNSUPPORTED;
++
++ p->lc = d % 9;
++ d /= 9;
++ p->pb = d / 5;
++ p->lp = d % 5;
++
++ return SZ_OK;
++}
++
++static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc)
++{
++ UInt32 numProbs = LzmaProps_GetNumProbs(propNew);
++ if (p->probs == 0 || numProbs != p->numProbs)
++ {
++ LzmaDec_FreeProbs(p, alloc);
++ p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb));
++ p->numProbs = numProbs;
++ if (p->probs == 0)
++ return SZ_ERROR_MEM;
++ }
++ return SZ_OK;
++}
++
++static 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 LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
++ ELzmaStatus *status, ISzAlloc *alloc)
++{
++ CLzmaDec p;
++ SRes res;
++ SizeT inSize = *srcLen;
++ SizeT outSize = *destLen;
++ *srcLen = *destLen = 0;
++ if (inSize < RC_INIT_SIZE)
++ return SZ_ERROR_INPUT_EOF;
++
++ LzmaDec_Construct(&p);
++ res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc);
++ if (res != 0)
++ return res;
++ p.dic = dest;
++ p.dicBufSize = outSize;
++
++ LzmaDec_Init(&p);
++
++ *srcLen = inSize;
++ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status);
++
++ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
++ res = SZ_ERROR_INPUT_EOF;
++
++ (*destLen) = p.dicPos;
++ LzmaDec_FreeProbs(&p, alloc);
++ return res;
++}
+--- /dev/null
++++ b/lib/lzma/LzmaEnc.c
+@@ -0,0 +1,2123 @@
++/* LzmaEnc.c -- LZMA Encoder
++2009-11-24 : Igor Pavlov : Public domain */
++
++#include <string.h>
++
++/* #define SHOW_STAT */
++/* #define SHOW_STAT2 */
++
++#if defined(SHOW_STAT) || defined(SHOW_STAT2)
++#include <stdio.h>
++#endif
++
++#include "LzmaEnc.h"
++
++/* disable MT */
++#define _7ZIP_ST
++
++#include "LzFind.h"
++#ifndef _7ZIP_ST
++#include "LzFindMt.h"
++#endif
++
++#ifdef SHOW_STAT
++static int ttt = 0;
++#endif
++
++#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1)
++
++#define kBlockSize (9 << 10)
++#define kUnpackBlockSize (1 << 18)
++#define kMatchArraySize (1 << 21)
++#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX)
++
++#define kNumMaxDirectBits (31)
++
++#define kNumTopBits 24
++#define kTopValue ((UInt32)1 << kNumTopBits)
++
++#define kNumBitModelTotalBits 11
++#define kBitModelTotal (1 << kNumBitModelTotalBits)
++#define kNumMoveBits 5
++#define kProbInitValue (kBitModelTotal >> 1)
++
++#define kNumMoveReducingBits 4
++#define kNumBitPriceShiftBits 4
++#define kBitPrice (1 << kNumBitPriceShiftBits)
++
++void LzmaEncProps_Init(CLzmaEncProps *p)
++{
++ p->level = 5;
++ p->dictSize = p->mc = 0;
++ p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1;
++ p->writeEndMark = 0;
++}
++
++static void LzmaEncProps_Normalize(CLzmaEncProps *p)
++{
++ int level = p->level;
++ if (level < 0) level = 5;
++ p->level = level;
++ if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26)));
++ if (p->lc < 0) p->lc = 3;
++ if (p->lp < 0) p->lp = 0;
++ if (p->pb < 0) p->pb = 2;
++ if (p->algo < 0) p->algo = (level < 5 ? 0 : 1);
++ if (p->fb < 0) p->fb = (level < 7 ? 32 : 64);
++ if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1);
++ if (p->numHashBytes < 0) p->numHashBytes = 4;
++ if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1);
++ if (p->numThreads < 0)
++ p->numThreads =
++ #ifndef _7ZIP_ST
++ ((p->btMode && p->algo) ? 2 : 1);
++ #else
++ 1;
++ #endif
++}
++
++static UInt32 __maybe_unused LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
++{
++ CLzmaEncProps props = *props2;
++ LzmaEncProps_Normalize(&props);
++ return props.dictSize;
++}
++
++/* #define LZMA_LOG_BSR */
++/* Define it for Intel's CPU */
++
++
++#ifdef LZMA_LOG_BSR
++
++#define kDicLogSizeMaxCompress 30
++
++#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); }
++
++static UInt32 GetPosSlot1(UInt32 pos)
++{
++ UInt32 res;
++ BSR2_RET(pos, res);
++ return res;
++}
++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); }
++#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); }
++
++#else
++
++#define kNumLogBits (9 + (int)sizeof(size_t) / 2)
++#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7)
++
++static void LzmaEnc_FastPosInit(Byte *g_FastPos)
++{
++ int c = 2, slotFast;
++ g_FastPos[0] = 0;
++ g_FastPos[1] = 1;
++
++ for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++)
++ {
++ UInt32 k = (1 << ((slotFast >> 1) - 1));
++ UInt32 j;
++ for (j = 0; j < k; j++, c++)
++ g_FastPos[c] = (Byte)slotFast;
++ }
++}
++
++#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \
++ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \
++ res = p->g_FastPos[pos >> i] + (i * 2); }
++/*
++#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \
++ p->g_FastPos[pos >> 6] + 12 : \
++ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; }
++*/
++
++#define GetPosSlot1(pos) p->g_FastPos[pos]
++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); }
++#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); }
++
++#endif
++
++
++#define LZMA_NUM_REPS 4
++
++typedef unsigned CState;
++
++typedef struct
++{
++ UInt32 price;
++
++ CState state;
++ int prev1IsChar;
++ int prev2;
++
++ UInt32 posPrev2;
++ UInt32 backPrev2;
++
++ UInt32 posPrev;
++ UInt32 backPrev;
++ UInt32 backs[LZMA_NUM_REPS];
++} COptimal;
++
++#define kNumOpts (1 << 12)
++
++#define kNumLenToPosStates 4
++#define kNumPosSlotBits 6
++#define kDicLogSizeMin 0
++#define kDicLogSizeMax 32
++#define kDistTableSizeMax (kDicLogSizeMax * 2)
++
++
++#define kNumAlignBits 4
++#define kAlignTableSize (1 << kNumAlignBits)
++#define kAlignMask (kAlignTableSize - 1)
++
++#define kStartPosModelIndex 4
++#define kEndPosModelIndex 14
++#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex)
++
++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
++
++#ifdef _LZMA_PROB32
++#define CLzmaProb UInt32
++#else
++#define CLzmaProb UInt16
++#endif
++
++#define LZMA_PB_MAX 4
++#define LZMA_LC_MAX 8
++#define LZMA_LP_MAX 4
++
++#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX)
++
++
++#define kLenNumLowBits 3
++#define kLenNumLowSymbols (1 << kLenNumLowBits)
++#define kLenNumMidBits 3
++#define kLenNumMidSymbols (1 << kLenNumMidBits)
++#define kLenNumHighBits 8
++#define kLenNumHighSymbols (1 << kLenNumHighBits)
++
++#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
++
++#define LZMA_MATCH_LEN_MIN 2
++#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1)
++
++#define kNumStates 12
++
++typedef struct
++{
++ CLzmaProb choice;
++ CLzmaProb choice2;
++ CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits];
++ CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits];
++ CLzmaProb high[kLenNumHighSymbols];
++} CLenEnc;
++
++typedef struct
++{
++ CLenEnc p;
++ UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal];
++ UInt32 tableSize;
++ UInt32 counters[LZMA_NUM_PB_STATES_MAX];
++} CLenPriceEnc;
++
++typedef struct
++{
++ UInt32 range;
++ Byte cache;
++ UInt64 low;
++ UInt64 cacheSize;
++ Byte *buf;
++ Byte *bufLim;
++ Byte *bufBase;
++ ISeqOutStream *outStream;
++ UInt64 processed;
++ SRes res;
++} CRangeEnc;
++
++typedef struct
++{
++ CLzmaProb *litProbs;
++
++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX];
++ CLzmaProb isRep[kNumStates];
++ CLzmaProb isRepG0[kNumStates];
++ CLzmaProb isRepG1[kNumStates];
++ CLzmaProb isRepG2[kNumStates];
++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX];
++
++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits];
++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex];
++ CLzmaProb posAlignEncoder[1 << kNumAlignBits];
++
++ CLenPriceEnc lenEnc;
++ CLenPriceEnc repLenEnc;
++
++ UInt32 reps[LZMA_NUM_REPS];
++ UInt32 state;
++} CSaveState;
++
++typedef struct
++{
++ IMatchFinder matchFinder;
++ void *matchFinderObj;
++
++ #ifndef _7ZIP_ST
++ Bool mtMode;
++ CMatchFinderMt matchFinderMt;
++ #endif
++
++ CMatchFinder matchFinderBase;
++
++ #ifndef _7ZIP_ST
++ Byte pad[128];
++ #endif
++
++ UInt32 optimumEndIndex;
++ UInt32 optimumCurrentIndex;
++
++ UInt32 longestMatchLength;
++ UInt32 numPairs;
++ UInt32 numAvail;
++ COptimal opt[kNumOpts];
++
++ #ifndef LZMA_LOG_BSR
++ Byte g_FastPos[1 << kNumLogBits];
++ #endif
++
++ UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits];
++ UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1];
++ UInt32 numFastBytes;
++ UInt32 additionalOffset;
++ UInt32 reps[LZMA_NUM_REPS];
++ UInt32 state;
++
++ UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax];
++ UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances];
++ UInt32 alignPrices[kAlignTableSize];
++ UInt32 alignPriceCount;
++
++ UInt32 distTableSize;
++
++ unsigned lc, lp, pb;
++ unsigned lpMask, pbMask;
++
++ CLzmaProb *litProbs;
++
++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX];
++ CLzmaProb isRep[kNumStates];
++ CLzmaProb isRepG0[kNumStates];
++ CLzmaProb isRepG1[kNumStates];
++ CLzmaProb isRepG2[kNumStates];
++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX];
++
++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits];
++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex];
++ CLzmaProb posAlignEncoder[1 << kNumAlignBits];
++
++ CLenPriceEnc lenEnc;
++ CLenPriceEnc repLenEnc;
++
++ unsigned lclp;
++
++ Bool fastMode;
++
++ CRangeEnc rc;
++
++ Bool writeEndMark;
++ UInt64 nowPos64;
++ UInt32 matchPriceCount;
++ Bool finished;
++ Bool multiThread;
++
++ SRes result;
++ UInt32 dictSize;
++ UInt32 matchFinderCycles;
++
++ int needInit;
++
++ CSaveState saveState;
++} CLzmaEnc;
++
++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ CLzmaEncProps props = *props2;
++ LzmaEncProps_Normalize(&props);
++
++ if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX ||
++ props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30))
++ return SZ_ERROR_PARAM;
++ p->dictSize = props.dictSize;
++ p->matchFinderCycles = props.mc;
++ {
++ unsigned fb = props.fb;
++ if (fb < 5)
++ fb = 5;
++ if (fb > LZMA_MATCH_LEN_MAX)
++ fb = LZMA_MATCH_LEN_MAX;
++ p->numFastBytes = fb;
++ }
++ p->lc = props.lc;
++ p->lp = props.lp;
++ p->pb = props.pb;
++ p->fastMode = (props.algo == 0);
++ p->matchFinderBase.btMode = props.btMode;
++ {
++ UInt32 numHashBytes = 4;
++ if (props.btMode)
++ {
++ if (props.numHashBytes < 2)
++ numHashBytes = 2;
++ else if (props.numHashBytes < 4)
++ numHashBytes = props.numHashBytes;
++ }
++ p->matchFinderBase.numHashBytes = numHashBytes;
++ }
++
++ p->matchFinderBase.cutValue = props.mc;
++
++ p->writeEndMark = props.writeEndMark;
++
++ #ifndef _7ZIP_ST
++ /*
++ if (newMultiThread != _multiThread)
++ {
++ ReleaseMatchFinder();
++ _multiThread = newMultiThread;
++ }
++ */
++ p->multiThread = (props.numThreads > 1);
++ #endif
++
++ return SZ_OK;
++}
++
++static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5};
++static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10};
++static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11};
++static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11};
++
++#define IsCharState(s) ((s) < 7)
++
++#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1)
++
++#define kInfinityPrice (1 << 30)
++
++static void RangeEnc_Construct(CRangeEnc *p)
++{
++ p->outStream = 0;
++ p->bufBase = 0;
++}
++
++#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize)
++
++#define RC_BUF_SIZE (1 << 16)
++static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc)
++{
++ if (p->bufBase == 0)
++ {
++ p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE);
++ if (p->bufBase == 0)
++ return 0;
++ p->bufLim = p->bufBase + RC_BUF_SIZE;
++ }
++ return 1;
++}
++
++static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->bufBase);
++ p->bufBase = 0;
++}
++
++static void RangeEnc_Init(CRangeEnc *p)
++{
++ /* Stream.Init(); */
++ p->low = 0;
++ p->range = 0xFFFFFFFF;
++ p->cacheSize = 1;
++ p->cache = 0;
++
++ p->buf = p->bufBase;
++
++ p->processed = 0;
++ p->res = SZ_OK;
++}
++
++static void RangeEnc_FlushStream(CRangeEnc *p)
++{
++ size_t num;
++ if (p->res != SZ_OK)
++ return;
++ num = p->buf - p->bufBase;
++ if (num != p->outStream->Write(p->outStream, p->bufBase, num))
++ p->res = SZ_ERROR_WRITE;
++ p->processed += num;
++ p->buf = p->bufBase;
++}
++
++static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p)
++{
++ if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0)
++ {
++ Byte temp = p->cache;
++ do
++ {
++ Byte *buf = p->buf;
++ *buf++ = (Byte)(temp + (Byte)(p->low >> 32));
++ p->buf = buf;
++ if (buf == p->bufLim)
++ RangeEnc_FlushStream(p);
++ temp = 0xFF;
++ }
++ while (--p->cacheSize != 0);
++ p->cache = (Byte)((UInt32)p->low >> 24);
++ }
++ p->cacheSize++;
++ p->low = (UInt32)p->low << 8;
++}
++
++static void RangeEnc_FlushData(CRangeEnc *p)
++{
++ int i;
++ for (i = 0; i < 5; i++)
++ RangeEnc_ShiftLow(p);
++}
++
++static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits)
++{
++ do
++ {
++ p->range >>= 1;
++ p->low += p->range & (0 - ((value >> --numBits) & 1));
++ if (p->range < kTopValue)
++ {
++ p->range <<= 8;
++ RangeEnc_ShiftLow(p);
++ }
++ }
++ while (numBits != 0);
++}
++
++static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol)
++{
++ UInt32 ttt = *prob;
++ UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt;
++ if (symbol == 0)
++ {
++ p->range = newBound;
++ ttt += (kBitModelTotal - ttt) >> kNumMoveBits;
++ }
++ else
++ {
++ p->low += newBound;
++ p->range -= newBound;
++ ttt -= ttt >> kNumMoveBits;
++ }
++ *prob = (CLzmaProb)ttt;
++ if (p->range < kTopValue)
++ {
++ p->range <<= 8;
++ RangeEnc_ShiftLow(p);
++ }
++}
++
++static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol)
++{
++ symbol |= 0x100;
++ do
++ {
++ RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1);
++ symbol <<= 1;
++ }
++ while (symbol < 0x10000);
++}
++
++static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte)
++{
++ UInt32 offs = 0x100;
++ symbol |= 0x100;
++ do
++ {
++ matchByte <<= 1;
++ RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1);
++ symbol <<= 1;
++ offs &= ~(matchByte ^ symbol);
++ }
++ while (symbol < 0x10000);
++}
++
++static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
++{
++ UInt32 i;
++ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits))
++ {
++ const int kCyclesBits = kNumBitPriceShiftBits;
++ UInt32 w = i;
++ UInt32 bitCount = 0;
++ int j;
++ for (j = 0; j < kCyclesBits; j++)
++ {
++ w = w * w;
++ bitCount <<= 1;
++ while (w >= ((UInt32)1 << 16))
++ {
++ w >>= 1;
++ bitCount++;
++ }
++ }
++ ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount);
++ }
++}
++
++
++#define GET_PRICE(prob, symbol) \
++ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits];
++
++#define GET_PRICEa(prob, symbol) \
++ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits];
++
++#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits]
++#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]
++
++#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits]
++#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]
++
++static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices)
++{
++ UInt32 price = 0;
++ symbol |= 0x100;
++ do
++ {
++ price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1);
++ symbol <<= 1;
++ }
++ while (symbol < 0x10000);
++ return price;
++}
++
++static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices)
++{
++ UInt32 price = 0;
++ UInt32 offs = 0x100;
++ symbol |= 0x100;
++ do
++ {
++ matchByte <<= 1;
++ price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1);
++ symbol <<= 1;
++ offs &= ~(matchByte ^ symbol);
++ }
++ while (symbol < 0x10000);
++ return price;
++}
++
++
++static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol)
++{
++ UInt32 m = 1;
++ int i;
++ for (i = numBitLevels; i != 0;)
++ {
++ UInt32 bit;
++ i--;
++ bit = (symbol >> i) & 1;
++ RangeEnc_EncodeBit(rc, probs + m, bit);
++ m = (m << 1) | bit;
++ }
++}
++
++static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol)
++{
++ UInt32 m = 1;
++ int i;
++ for (i = 0; i < numBitLevels; i++)
++ {
++ UInt32 bit = symbol & 1;
++ RangeEnc_EncodeBit(rc, probs + m, bit);
++ m = (m << 1) | bit;
++ symbol >>= 1;
++ }
++}
++
++static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices)
++{
++ UInt32 price = 0;
++ symbol |= (1 << numBitLevels);
++ while (symbol != 1)
++ {
++ price += GET_PRICEa(probs[symbol >> 1], symbol & 1);
++ symbol >>= 1;
++ }
++ return price;
++}
++
++static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices)
++{
++ UInt32 price = 0;
++ UInt32 m = 1;
++ int i;
++ for (i = numBitLevels; i != 0; i--)
++ {
++ UInt32 bit = symbol & 1;
++ symbol >>= 1;
++ price += GET_PRICEa(probs[m], bit);
++ m = (m << 1) | bit;
++ }
++ return price;
++}
++
++
++static void LenEnc_Init(CLenEnc *p)
++{
++ unsigned i;
++ p->choice = p->choice2 = kProbInitValue;
++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++)
++ p->low[i] = kProbInitValue;
++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++)
++ p->mid[i] = kProbInitValue;
++ for (i = 0; i < kLenNumHighSymbols; i++)
++ p->high[i] = kProbInitValue;
++}
++
++static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState)
++{
++ if (symbol < kLenNumLowSymbols)
++ {
++ RangeEnc_EncodeBit(rc, &p->choice, 0);
++ RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol);
++ }
++ else
++ {
++ RangeEnc_EncodeBit(rc, &p->choice, 1);
++ if (symbol < kLenNumLowSymbols + kLenNumMidSymbols)
++ {
++ RangeEnc_EncodeBit(rc, &p->choice2, 0);
++ RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols);
++ }
++ else
++ {
++ RangeEnc_EncodeBit(rc, &p->choice2, 1);
++ RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols);
++ }
++ }
++}
++
++static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices)
++{
++ UInt32 a0 = GET_PRICE_0a(p->choice);
++ UInt32 a1 = GET_PRICE_1a(p->choice);
++ UInt32 b0 = a1 + GET_PRICE_0a(p->choice2);
++ UInt32 b1 = a1 + GET_PRICE_1a(p->choice2);
++ UInt32 i = 0;
++ for (i = 0; i < kLenNumLowSymbols; i++)
++ {
++ if (i >= numSymbols)
++ return;
++ prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices);
++ }
++ for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++)
++ {
++ if (i >= numSymbols)
++ return;
++ prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices);
++ }
++ for (; i < numSymbols; i++)
++ prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices);
++}
++
++static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices)
++{
++ LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices);
++ p->counters[posState] = p->tableSize;
++}
++
++static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices)
++{
++ UInt32 posState;
++ for (posState = 0; posState < numPosStates; posState++)
++ LenPriceEnc_UpdateTable(p, posState, ProbPrices);
++}
++
++static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices)
++{
++ LenEnc_Encode(&p->p, rc, symbol, posState);
++ if (updatePrice)
++ if (--p->counters[posState] == 0)
++ LenPriceEnc_UpdateTable(p, posState, ProbPrices);
++}
++
++
++
++
++static void MovePos(CLzmaEnc *p, UInt32 num)
++{
++ #ifdef SHOW_STAT
++ ttt += num;
++ printf("\n MovePos %d", num);
++ #endif
++ if (num != 0)
++ {
++ p->additionalOffset += num;
++ p->matchFinder.Skip(p->matchFinderObj, num);
++ }
++}
++
++static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes)
++{
++ UInt32 lenRes = 0, numPairs;
++ p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
++ numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches);
++ #ifdef SHOW_STAT
++ printf("\n i = %d numPairs = %d ", ttt, numPairs / 2);
++ ttt++;
++ {
++ UInt32 i;
++ for (i = 0; i < numPairs; i += 2)
++ printf("%2d %6d | ", p->matches[i], p->matches[i + 1]);
++ }
++ #endif
++ if (numPairs > 0)
++ {
++ lenRes = p->matches[numPairs - 2];
++ if (lenRes == p->numFastBytes)
++ {
++ const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++ UInt32 distance = p->matches[numPairs - 1] + 1;
++ UInt32 numAvail = p->numAvail;
++ if (numAvail > LZMA_MATCH_LEN_MAX)
++ numAvail = LZMA_MATCH_LEN_MAX;
++ {
++ const Byte *pby2 = pby - distance;
++ for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++);
++ }
++ }
++ }
++ p->additionalOffset++;
++ *numDistancePairsRes = numPairs;
++ return lenRes;
++}
++
++
++#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False;
++#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False;
++#define IsShortRep(p) ((p)->backPrev == 0)
++
++static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState)
++{
++ return
++ GET_PRICE_0(p->isRepG0[state]) +
++ GET_PRICE_0(p->isRep0Long[state][posState]);
++}
++
++static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState)
++{
++ UInt32 price;
++ if (repIndex == 0)
++ {
++ price = GET_PRICE_0(p->isRepG0[state]);
++ price += GET_PRICE_1(p->isRep0Long[state][posState]);
++ }
++ else
++ {
++ price = GET_PRICE_1(p->isRepG0[state]);
++ if (repIndex == 1)
++ price += GET_PRICE_0(p->isRepG1[state]);
++ else
++ {
++ price += GET_PRICE_1(p->isRepG1[state]);
++ price += GET_PRICE(p->isRepG2[state], repIndex - 2);
++ }
++ }
++ return price;
++}
++
++static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState)
++{
++ return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] +
++ GetPureRepPrice(p, repIndex, state, posState);
++}
++
++static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur)
++{
++ UInt32 posMem = p->opt[cur].posPrev;
++ UInt32 backMem = p->opt[cur].backPrev;
++ p->optimumEndIndex = cur;
++ do
++ {
++ if (p->opt[cur].prev1IsChar)
++ {
++ MakeAsChar(&p->opt[posMem])
++ p->opt[posMem].posPrev = posMem - 1;
++ if (p->opt[cur].prev2)
++ {
++ p->opt[posMem - 1].prev1IsChar = False;
++ p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2;
++ p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2;
++ }
++ }
++ {
++ UInt32 posPrev = posMem;
++ UInt32 backCur = backMem;
++
++ backMem = p->opt[posPrev].backPrev;
++ posMem = p->opt[posPrev].posPrev;
++
++ p->opt[posPrev].backPrev = backCur;
++ p->opt[posPrev].posPrev = cur;
++ cur = posPrev;
++ }
++ }
++ while (cur != 0);
++ *backRes = p->opt[0].backPrev;
++ p->optimumCurrentIndex = p->opt[0].posPrev;
++ return p->optimumCurrentIndex;
++}
++
++#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300)
++
++static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes)
++{
++ UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur;
++ UInt32 matchPrice, repMatchPrice, normalMatchPrice;
++ UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS];
++ UInt32 *matches;
++ const Byte *data;
++ Byte curByte, matchByte;
++ if (p->optimumEndIndex != p->optimumCurrentIndex)
++ {
++ const COptimal *opt = &p->opt[p->optimumCurrentIndex];
++ UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex;
++ *backRes = opt->backPrev;
++ p->optimumCurrentIndex = opt->posPrev;
++ return lenRes;
++ }
++ p->optimumCurrentIndex = p->optimumEndIndex = 0;
++
++ if (p->additionalOffset == 0)
++ mainLen = ReadMatchDistances(p, &numPairs);
++ else
++ {
++ mainLen = p->longestMatchLength;
++ numPairs = p->numPairs;
++ }
++
++ numAvail = p->numAvail;
++ if (numAvail < 2)
++ {
++ *backRes = (UInt32)(-1);
++ return 1;
++ }
++ if (numAvail > LZMA_MATCH_LEN_MAX)
++ numAvail = LZMA_MATCH_LEN_MAX;
++
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++ repMaxIndex = 0;
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ {
++ UInt32 lenTest;
++ const Byte *data2;
++ reps[i] = p->reps[i];
++ data2 = data - (reps[i] + 1);
++ if (data[0] != data2[0] || data[1] != data2[1])
++ {
++ repLens[i] = 0;
++ continue;
++ }
++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++);
++ repLens[i] = lenTest;
++ if (lenTest > repLens[repMaxIndex])
++ repMaxIndex = i;
++ }
++ if (repLens[repMaxIndex] >= p->numFastBytes)
++ {
++ UInt32 lenRes;
++ *backRes = repMaxIndex;
++ lenRes = repLens[repMaxIndex];
++ MovePos(p, lenRes - 1);
++ return lenRes;
++ }
++
++ matches = p->matches;
++ if (mainLen >= p->numFastBytes)
++ {
++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS;
++ MovePos(p, mainLen - 1);
++ return mainLen;
++ }
++ curByte = *data;
++ matchByte = *(data - (reps[0] + 1));
++
++ if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2)
++ {
++ *backRes = (UInt32)-1;
++ return 1;
++ }
++
++ p->opt[0].state = (CState)p->state;
++
++ posState = (position & p->pbMask);
++
++ {
++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1));
++ p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) +
++ (!IsCharState(p->state) ?
++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) :
++ LitEnc_GetPrice(probs, curByte, p->ProbPrices));
++ }
++
++ MakeAsChar(&p->opt[1]);
++
++ matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]);
++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]);
++
++ if (matchByte == curByte)
++ {
++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState);
++ if (shortRepPrice < p->opt[1].price)
++ {
++ p->opt[1].price = shortRepPrice;
++ MakeAsShortRep(&p->opt[1]);
++ }
++ }
++ lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]);
++
++ if (lenEnd < 2)
++ {
++ *backRes = p->opt[1].backPrev;
++ return 1;
++ }
++
++ p->opt[1].posPrev = 0;
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ p->opt[0].backs[i] = reps[i];
++
++ len = lenEnd;
++ do
++ p->opt[len--].price = kInfinityPrice;
++ while (len >= 2);
++
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ {
++ UInt32 repLen = repLens[i];
++ UInt32 price;
++ if (repLen < 2)
++ continue;
++ price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState);
++ do
++ {
++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2];
++ COptimal *opt = &p->opt[repLen];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = 0;
++ opt->backPrev = i;
++ opt->prev1IsChar = False;
++ }
++ }
++ while (--repLen >= 2);
++ }
++
++ normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]);
++
++ len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2);
++ if (len <= mainLen)
++ {
++ UInt32 offs = 0;
++ while (len > matches[offs])
++ offs += 2;
++ for (; ; len++)
++ {
++ COptimal *opt;
++ UInt32 distance = matches[offs + 1];
++
++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN];
++ UInt32 lenToPosState = GetLenToPosState(len);
++ if (distance < kNumFullDistances)
++ curAndLenPrice += p->distancesPrices[lenToPosState][distance];
++ else
++ {
++ UInt32 slot;
++ GetPosSlot2(distance, slot);
++ curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot];
++ }
++ opt = &p->opt[len];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = 0;
++ opt->backPrev = distance + LZMA_NUM_REPS;
++ opt->prev1IsChar = False;
++ }
++ if (len == matches[offs])
++ {
++ offs += 2;
++ if (offs == numPairs)
++ break;
++ }
++ }
++ }
++
++ cur = 0;
++
++ #ifdef SHOW_STAT2
++ if (position >= 0)
++ {
++ unsigned i;
++ printf("\n pos = %4X", position);
++ for (i = cur; i <= lenEnd; i++)
++ printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price);
++ }
++ #endif
++
++ for (;;)
++ {
++ UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen;
++ UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice;
++ Bool nextIsChar;
++ Byte curByte, matchByte;
++ const Byte *data;
++ COptimal *curOpt;
++ COptimal *nextOpt;
++
++ cur++;
++ if (cur == lenEnd)
++ return Backward(p, backRes, cur);
++
++ newLen = ReadMatchDistances(p, &numPairs);
++ if (newLen >= p->numFastBytes)
++ {
++ p->numPairs = numPairs;
++ p->longestMatchLength = newLen;
++ return Backward(p, backRes, cur);
++ }
++ position++;
++ curOpt = &p->opt[cur];
++ posPrev = curOpt->posPrev;
++ if (curOpt->prev1IsChar)
++ {
++ posPrev--;
++ if (curOpt->prev2)
++ {
++ state = p->opt[curOpt->posPrev2].state;
++ if (curOpt->backPrev2 < LZMA_NUM_REPS)
++ state = kRepNextStates[state];
++ else
++ state = kMatchNextStates[state];
++ }
++ else
++ state = p->opt[posPrev].state;
++ state = kLiteralNextStates[state];
++ }
++ else
++ state = p->opt[posPrev].state;
++ if (posPrev == cur - 1)
++ {
++ if (IsShortRep(curOpt))
++ state = kShortRepNextStates[state];
++ else
++ state = kLiteralNextStates[state];
++ }
++ else
++ {
++ UInt32 pos;
++ const COptimal *prevOpt;
++ if (curOpt->prev1IsChar && curOpt->prev2)
++ {
++ posPrev = curOpt->posPrev2;
++ pos = curOpt->backPrev2;
++ state = kRepNextStates[state];
++ }
++ else
++ {
++ pos = curOpt->backPrev;
++ if (pos < LZMA_NUM_REPS)
++ state = kRepNextStates[state];
++ else
++ state = kMatchNextStates[state];
++ }
++ prevOpt = &p->opt[posPrev];
++ if (pos < LZMA_NUM_REPS)
++ {
++ UInt32 i;
++ reps[0] = prevOpt->backs[pos];
++ for (i = 1; i <= pos; i++)
++ reps[i] = prevOpt->backs[i - 1];
++ for (; i < LZMA_NUM_REPS; i++)
++ reps[i] = prevOpt->backs[i];
++ }
++ else
++ {
++ UInt32 i;
++ reps[0] = (pos - LZMA_NUM_REPS);
++ for (i = 1; i < LZMA_NUM_REPS; i++)
++ reps[i] = prevOpt->backs[i - 1];
++ }
++ }
++ curOpt->state = (CState)state;
++
++ curOpt->backs[0] = reps[0];
++ curOpt->backs[1] = reps[1];
++ curOpt->backs[2] = reps[2];
++ curOpt->backs[3] = reps[3];
++
++ curPrice = curOpt->price;
++ nextIsChar = False;
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++ curByte = *data;
++ matchByte = *(data - (reps[0] + 1));
++
++ posState = (position & p->pbMask);
++
++ curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]);
++ {
++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1));
++ curAnd1Price +=
++ (!IsCharState(state) ?
++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) :
++ LitEnc_GetPrice(probs, curByte, p->ProbPrices));
++ }
++
++ nextOpt = &p->opt[cur + 1];
++
++ if (curAnd1Price < nextOpt->price)
++ {
++ nextOpt->price = curAnd1Price;
++ nextOpt->posPrev = cur;
++ MakeAsChar(nextOpt);
++ nextIsChar = True;
++ }
++
++ matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]);
++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]);
++
++ if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0))
++ {
++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState);
++ if (shortRepPrice <= nextOpt->price)
++ {
++ nextOpt->price = shortRepPrice;
++ nextOpt->posPrev = cur;
++ MakeAsShortRep(nextOpt);
++ nextIsChar = True;
++ }
++ }
++ numAvailFull = p->numAvail;
++ {
++ UInt32 temp = kNumOpts - 1 - cur;
++ if (temp < numAvailFull)
++ numAvailFull = temp;
++ }
++
++ if (numAvailFull < 2)
++ continue;
++ numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes);
++
++ if (!nextIsChar && matchByte != curByte) /* speed optimization */
++ {
++ /* try Literal + rep0 */
++ UInt32 temp;
++ UInt32 lenTest2;
++ const Byte *data2 = data - (reps[0] + 1);
++ UInt32 limit = p->numFastBytes + 1;
++ if (limit > numAvailFull)
++ limit = numAvailFull;
++
++ for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++);
++ lenTest2 = temp - 1;
++ if (lenTest2 >= 2)
++ {
++ UInt32 state2 = kLiteralNextStates[state];
++ UInt32 posStateNext = (position + 1) & p->pbMask;
++ UInt32 nextRepMatchPrice = curAnd1Price +
++ GET_PRICE_1(p->isMatch[state2][posStateNext]) +
++ GET_PRICE_1(p->isRep[state2]);
++ /* for (; lenTest2 >= 2; lenTest2--) */
++ {
++ UInt32 curAndLenPrice;
++ COptimal *opt;
++ UInt32 offset = cur + 1 + lenTest2;
++ while (lenEnd < offset)
++ p->opt[++lenEnd].price = kInfinityPrice;
++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
++ opt = &p->opt[offset];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur + 1;
++ opt->backPrev = 0;
++ opt->prev1IsChar = True;
++ opt->prev2 = False;
++ }
++ }
++ }
++ }
++
++ startLen = 2; /* speed optimization */
++ {
++ UInt32 repIndex;
++ for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++)
++ {
++ UInt32 lenTest;
++ UInt32 lenTestTemp;
++ UInt32 price;
++ const Byte *data2 = data - (reps[repIndex] + 1);
++ if (data[0] != data2[0] || data[1] != data2[1])
++ continue;
++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++);
++ while (lenEnd < cur + lenTest)
++ p->opt[++lenEnd].price = kInfinityPrice;
++ lenTestTemp = lenTest;
++ price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState);
++ do
++ {
++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2];
++ COptimal *opt = &p->opt[cur + lenTest];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur;
++ opt->backPrev = repIndex;
++ opt->prev1IsChar = False;
++ }
++ }
++ while (--lenTest >= 2);
++ lenTest = lenTestTemp;
++
++ if (repIndex == 0)
++ startLen = lenTest + 1;
++
++ /* if (_maxMode) */
++ {
++ UInt32 lenTest2 = lenTest + 1;
++ UInt32 limit = lenTest2 + p->numFastBytes;
++ UInt32 nextRepMatchPrice;
++ if (limit > numAvailFull)
++ limit = numAvailFull;
++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++);
++ lenTest2 -= lenTest + 1;
++ if (lenTest2 >= 2)
++ {
++ UInt32 state2 = kRepNextStates[state];
++ UInt32 posStateNext = (position + lenTest) & p->pbMask;
++ UInt32 curAndLenCharPrice =
++ price + p->repLenEnc.prices[posState][lenTest - 2] +
++ GET_PRICE_0(p->isMatch[state2][posStateNext]) +
++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]),
++ data[lenTest], data2[lenTest], p->ProbPrices);
++ state2 = kLiteralNextStates[state2];
++ posStateNext = (position + lenTest + 1) & p->pbMask;
++ nextRepMatchPrice = curAndLenCharPrice +
++ GET_PRICE_1(p->isMatch[state2][posStateNext]) +
++ GET_PRICE_1(p->isRep[state2]);
++
++ /* for (; lenTest2 >= 2; lenTest2--) */
++ {
++ UInt32 curAndLenPrice;
++ COptimal *opt;
++ UInt32 offset = cur + lenTest + 1 + lenTest2;
++ while (lenEnd < offset)
++ p->opt[++lenEnd].price = kInfinityPrice;
++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
++ opt = &p->opt[offset];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur + lenTest + 1;
++ opt->backPrev = 0;
++ opt->prev1IsChar = True;
++ opt->prev2 = True;
++ opt->posPrev2 = cur;
++ opt->backPrev2 = repIndex;
++ }
++ }
++ }
++ }
++ }
++ }
++ /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */
++ if (newLen > numAvail)
++ {
++ newLen = numAvail;
++ for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2);
++ matches[numPairs] = newLen;
++ numPairs += 2;
++ }
++ if (newLen >= startLen)
++ {
++ UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]);
++ UInt32 offs, curBack, posSlot;
++ UInt32 lenTest;
++ while (lenEnd < cur + newLen)
++ p->opt[++lenEnd].price = kInfinityPrice;
++
++ offs = 0;
++ while (startLen > matches[offs])
++ offs += 2;
++ curBack = matches[offs + 1];
++ GetPosSlot2(curBack, posSlot);
++ for (lenTest = /*2*/ startLen; ; lenTest++)
++ {
++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN];
++ UInt32 lenToPosState = GetLenToPosState(lenTest);
++ COptimal *opt;
++ if (curBack < kNumFullDistances)
++ curAndLenPrice += p->distancesPrices[lenToPosState][curBack];
++ else
++ curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask];
++
++ opt = &p->opt[cur + lenTest];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur;
++ opt->backPrev = curBack + LZMA_NUM_REPS;
++ opt->prev1IsChar = False;
++ }
++
++ if (/*_maxMode && */lenTest == matches[offs])
++ {
++ /* Try Match + Literal + Rep0 */
++ const Byte *data2 = data - (curBack + 1);
++ UInt32 lenTest2 = lenTest + 1;
++ UInt32 limit = lenTest2 + p->numFastBytes;
++ UInt32 nextRepMatchPrice;
++ if (limit > numAvailFull)
++ limit = numAvailFull;
++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++);
++ lenTest2 -= lenTest + 1;
++ if (lenTest2 >= 2)
++ {
++ UInt32 state2 = kMatchNextStates[state];
++ UInt32 posStateNext = (position + lenTest) & p->pbMask;
++ UInt32 curAndLenCharPrice = curAndLenPrice +
++ GET_PRICE_0(p->isMatch[state2][posStateNext]) +
++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]),
++ data[lenTest], data2[lenTest], p->ProbPrices);
++ state2 = kLiteralNextStates[state2];
++ posStateNext = (posStateNext + 1) & p->pbMask;
++ nextRepMatchPrice = curAndLenCharPrice +
++ GET_PRICE_1(p->isMatch[state2][posStateNext]) +
++ GET_PRICE_1(p->isRep[state2]);
++
++ /* for (; lenTest2 >= 2; lenTest2--) */
++ {
++ UInt32 offset = cur + lenTest + 1 + lenTest2;
++ UInt32 curAndLenPrice;
++ COptimal *opt;
++ while (lenEnd < offset)
++ p->opt[++lenEnd].price = kInfinityPrice;
++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
++ opt = &p->opt[offset];
++ if (curAndLenPrice < opt->price)
++ {
++ opt->price = curAndLenPrice;
++ opt->posPrev = cur + lenTest + 1;
++ opt->backPrev = 0;
++ opt->prev1IsChar = True;
++ opt->prev2 = True;
++ opt->posPrev2 = cur;
++ opt->backPrev2 = curBack + LZMA_NUM_REPS;
++ }
++ }
++ }
++ offs += 2;
++ if (offs == numPairs)
++ break;
++ curBack = matches[offs + 1];
++ if (curBack >= kNumFullDistances)
++ GetPosSlot2(curBack, posSlot);
++ }
++ }
++ }
++ }
++}
++
++#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist))
++
++static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes)
++{
++ UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i;
++ const Byte *data;
++ const UInt32 *matches;
++
++ if (p->additionalOffset == 0)
++ mainLen = ReadMatchDistances(p, &numPairs);
++ else
++ {
++ mainLen = p->longestMatchLength;
++ numPairs = p->numPairs;
++ }
++
++ numAvail = p->numAvail;
++ *backRes = (UInt32)-1;
++ if (numAvail < 2)
++ return 1;
++ if (numAvail > LZMA_MATCH_LEN_MAX)
++ numAvail = LZMA_MATCH_LEN_MAX;
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++
++ repLen = repIndex = 0;
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ {
++ UInt32 len;
++ const Byte *data2 = data - (p->reps[i] + 1);
++ if (data[0] != data2[0] || data[1] != data2[1])
++ continue;
++ for (len = 2; len < numAvail && data[len] == data2[len]; len++);
++ if (len >= p->numFastBytes)
++ {
++ *backRes = i;
++ MovePos(p, len - 1);
++ return len;
++ }
++ if (len > repLen)
++ {
++ repIndex = i;
++ repLen = len;
++ }
++ }
++
++ matches = p->matches;
++ if (mainLen >= p->numFastBytes)
++ {
++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS;
++ MovePos(p, mainLen - 1);
++ return mainLen;
++ }
++
++ mainDist = 0; /* for GCC */
++ if (mainLen >= 2)
++ {
++ mainDist = matches[numPairs - 1];
++ while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1)
++ {
++ if (!ChangePair(matches[numPairs - 3], mainDist))
++ break;
++ numPairs -= 2;
++ mainLen = matches[numPairs - 2];
++ mainDist = matches[numPairs - 1];
++ }
++ if (mainLen == 2 && mainDist >= 0x80)
++ mainLen = 1;
++ }
++
++ if (repLen >= 2 && (
++ (repLen + 1 >= mainLen) ||
++ (repLen + 2 >= mainLen && mainDist >= (1 << 9)) ||
++ (repLen + 3 >= mainLen && mainDist >= (1 << 15))))
++ {
++ *backRes = repIndex;
++ MovePos(p, repLen - 1);
++ return repLen;
++ }
++
++ if (mainLen < 2 || numAvail <= 2)
++ return 1;
++
++ p->longestMatchLength = ReadMatchDistances(p, &p->numPairs);
++ if (p->longestMatchLength >= 2)
++ {
++ UInt32 newDistance = matches[p->numPairs - 1];
++ if ((p->longestMatchLength >= mainLen && newDistance < mainDist) ||
++ (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) ||
++ (p->longestMatchLength > mainLen + 1) ||
++ (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist)))
++ return 1;
++ }
++
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
++ for (i = 0; i < LZMA_NUM_REPS; i++)
++ {
++ UInt32 len, limit;
++ const Byte *data2 = data - (p->reps[i] + 1);
++ if (data[0] != data2[0] || data[1] != data2[1])
++ continue;
++ limit = mainLen - 1;
++ for (len = 2; len < limit && data[len] == data2[len]; len++);
++ if (len >= limit)
++ return 1;
++ }
++ *backRes = mainDist + LZMA_NUM_REPS;
++ MovePos(p, mainLen - 2);
++ return mainLen;
++}
++
++static void WriteEndMarker(CLzmaEnc *p, UInt32 posState)
++{
++ UInt32 len;
++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1);
++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0);
++ p->state = kMatchNextStates[p->state];
++ len = LZMA_MATCH_LEN_MIN;
++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1);
++ RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits);
++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask);
++}
++
++static SRes CheckErrors(CLzmaEnc *p)
++{
++ if (p->result != SZ_OK)
++ return p->result;
++ if (p->rc.res != SZ_OK)
++ p->result = SZ_ERROR_WRITE;
++ if (p->matchFinderBase.result != SZ_OK)
++ p->result = SZ_ERROR_READ;
++ if (p->result != SZ_OK)
++ p->finished = True;
++ return p->result;
++}
++
++static SRes Flush(CLzmaEnc *p, UInt32 nowPos)
++{
++ /* ReleaseMFStream(); */
++ p->finished = True;
++ if (p->writeEndMark)
++ WriteEndMarker(p, nowPos & p->pbMask);
++ RangeEnc_FlushData(&p->rc);
++ RangeEnc_FlushStream(&p->rc);
++ return CheckErrors(p);
++}
++
++static void FillAlignPrices(CLzmaEnc *p)
++{
++ UInt32 i;
++ for (i = 0; i < kAlignTableSize; i++)
++ p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices);
++ p->alignPriceCount = 0;
++}
++
++static void FillDistancesPrices(CLzmaEnc *p)
++{
++ UInt32 tempPrices[kNumFullDistances];
++ UInt32 i, lenToPosState;
++ for (i = kStartPosModelIndex; i < kNumFullDistances; i++)
++ {
++ UInt32 posSlot = GetPosSlot1(i);
++ UInt32 footerBits = ((posSlot >> 1) - 1);
++ UInt32 base = ((2 | (posSlot & 1)) << footerBits);
++ tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices);
++ }
++
++ for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++)
++ {
++ UInt32 posSlot;
++ const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState];
++ UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState];
++ for (posSlot = 0; posSlot < p->distTableSize; posSlot++)
++ posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices);
++ for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++)
++ posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits);
++
++ {
++ UInt32 *distancesPrices = p->distancesPrices[lenToPosState];
++ UInt32 i;
++ for (i = 0; i < kStartPosModelIndex; i++)
++ distancesPrices[i] = posSlotPrices[i];
++ for (; i < kNumFullDistances; i++)
++ distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i];
++ }
++ }
++ p->matchPriceCount = 0;
++}
++
++static void LzmaEnc_Construct(CLzmaEnc *p)
++{
++ RangeEnc_Construct(&p->rc);
++ MatchFinder_Construct(&p->matchFinderBase);
++ #ifndef _7ZIP_ST
++ MatchFinderMt_Construct(&p->matchFinderMt);
++ p->matchFinderMt.MatchFinder = &p->matchFinderBase;
++ #endif
++
++ {
++ CLzmaEncProps props;
++ LzmaEncProps_Init(&props);
++ LzmaEnc_SetProps(p, &props);
++ }
++
++ #ifndef LZMA_LOG_BSR
++ LzmaEnc_FastPosInit(p->g_FastPos);
++ #endif
++
++ LzmaEnc_InitPriceTables(p->ProbPrices);
++ p->litProbs = 0;
++ p->saveState.litProbs = 0;
++}
++
++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc)
++{
++ void *p;
++ p = alloc->Alloc(alloc, sizeof(CLzmaEnc));
++ if (p != 0)
++ LzmaEnc_Construct((CLzmaEnc *)p);
++ return p;
++}
++
++static void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->litProbs);
++ alloc->Free(alloc, p->saveState.litProbs);
++ p->litProbs = 0;
++ p->saveState.litProbs = 0;
++}
++
++static void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ #ifndef _7ZIP_ST
++ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig);
++ #endif
++ MatchFinder_Free(&p->matchFinderBase, allocBig);
++ LzmaEnc_FreeLits(p, alloc);
++ RangeEnc_Free(&p->rc, alloc);
++}
++
++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig);
++ alloc->Free(alloc, p);
++}
++
++static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize)
++{
++ UInt32 nowPos32, startPos32;
++ if (p->needInit)
++ {
++ p->matchFinder.Init(p->matchFinderObj);
++ p->needInit = 0;
++ }
++
++ if (p->finished)
++ return p->result;
++ RINOK(CheckErrors(p));
++
++ nowPos32 = (UInt32)p->nowPos64;
++ startPos32 = nowPos32;
++
++ if (p->nowPos64 == 0)
++ {
++ UInt32 numPairs;
++ Byte curByte;
++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0)
++ return Flush(p, nowPos32);
++ ReadMatchDistances(p, &numPairs);
++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0);
++ p->state = kLiteralNextStates[p->state];
++ curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset);
++ LitEnc_Encode(&p->rc, p->litProbs, curByte);
++ p->additionalOffset--;
++ nowPos32++;
++ }
++
++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0)
++ for (;;)
++ {
++ UInt32 pos, len, posState;
++
++ if (p->fastMode)
++ len = GetOptimumFast(p, &pos);
++ else
++ len = GetOptimum(p, nowPos32, &pos);
++
++ #ifdef SHOW_STAT2
++ printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos);
++ #endif
++
++ posState = nowPos32 & p->pbMask;
++ if (len == 1 && pos == (UInt32)-1)
++ {
++ Byte curByte;
++ CLzmaProb *probs;
++ const Byte *data;
++
++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0);
++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
++ curByte = *data;
++ probs = LIT_PROBS(nowPos32, *(data - 1));
++ if (IsCharState(p->state))
++ LitEnc_Encode(&p->rc, probs, curByte);
++ else
++ LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1));
++ p->state = kLiteralNextStates[p->state];
++ }
++ else
++ {
++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1);
++ if (pos < LZMA_NUM_REPS)
++ {
++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1);
++ if (pos == 0)
++ {
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0);
++ RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1));
++ }
++ else
++ {
++ UInt32 distance = p->reps[pos];
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1);
++ if (pos == 1)
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0);
++ else
++ {
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1);
++ RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2);
++ if (pos == 3)
++ p->reps[3] = p->reps[2];
++ p->reps[2] = p->reps[1];
++ }
++ p->reps[1] = p->reps[0];
++ p->reps[0] = distance;
++ }
++ if (len == 1)
++ p->state = kShortRepNextStates[p->state];
++ else
++ {
++ LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
++ p->state = kRepNextStates[p->state];
++ }
++ }
++ else
++ {
++ UInt32 posSlot;
++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0);
++ p->state = kMatchNextStates[p->state];
++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
++ pos -= LZMA_NUM_REPS;
++ GetPosSlot(pos, posSlot);
++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot);
++
++ if (posSlot >= kStartPosModelIndex)
++ {
++ UInt32 footerBits = ((posSlot >> 1) - 1);
++ UInt32 base = ((2 | (posSlot & 1)) << footerBits);
++ UInt32 posReduced = pos - base;
++
++ if (posSlot < kEndPosModelIndex)
++ RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced);
++ else
++ {
++ RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits);
++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask);
++ p->alignPriceCount++;
++ }
++ }
++ p->reps[3] = p->reps[2];
++ p->reps[2] = p->reps[1];
++ p->reps[1] = p->reps[0];
++ p->reps[0] = pos;
++ p->matchPriceCount++;
++ }
++ }
++ p->additionalOffset -= len;
++ nowPos32 += len;
++ if (p->additionalOffset == 0)
++ {
++ UInt32 processed;
++ if (!p->fastMode)
++ {
++ if (p->matchPriceCount >= (1 << 7))
++ FillDistancesPrices(p);
++ if (p->alignPriceCount >= kAlignTableSize)
++ FillAlignPrices(p);
++ }
++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0)
++ break;
++ processed = nowPos32 - startPos32;
++ if (useLimits)
++ {
++ if (processed + kNumOpts + 300 >= maxUnpackSize ||
++ RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize)
++ break;
++ }
++ else if (processed >= (1 << 15))
++ {
++ p->nowPos64 += nowPos32 - startPos32;
++ return CheckErrors(p);
++ }
++ }
++ }
++ p->nowPos64 += nowPos32 - startPos32;
++ return Flush(p, nowPos32);
++}
++
++#define kBigHashDicLimit ((UInt32)1 << 24)
++
++static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ UInt32 beforeSize = kNumOpts;
++ Bool btMode;
++ if (!RangeEnc_Alloc(&p->rc, alloc))
++ return SZ_ERROR_MEM;
++ btMode = (p->matchFinderBase.btMode != 0);
++ #ifndef _7ZIP_ST
++ p->mtMode = (p->multiThread && !p->fastMode && btMode);
++ #endif
++
++ {
++ unsigned lclp = p->lc + p->lp;
++ if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp)
++ {
++ LzmaEnc_FreeLits(p, alloc);
++ p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb));
++ p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb));
++ if (p->litProbs == 0 || p->saveState.litProbs == 0)
++ {
++ LzmaEnc_FreeLits(p, alloc);
++ return SZ_ERROR_MEM;
++ }
++ p->lclp = lclp;
++ }
++ }
++
++ p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit);
++
++ if (beforeSize + p->dictSize < keepWindowSize)
++ beforeSize = keepWindowSize - p->dictSize;
++
++ #ifndef _7ZIP_ST
++ if (p->mtMode)
++ {
++ RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig));
++ p->matchFinderObj = &p->matchFinderMt;
++ MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder);
++ }
++ else
++ #endif
++ {
++ if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig))
++ return SZ_ERROR_MEM;
++ p->matchFinderObj = &p->matchFinderBase;
++ MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder);
++ }
++ return SZ_OK;
++}
++
++static void LzmaEnc_Init(CLzmaEnc *p)
++{
++ UInt32 i;
++ p->state = 0;
++ for (i = 0 ; i < LZMA_NUM_REPS; i++)
++ p->reps[i] = 0;
++
++ RangeEnc_Init(&p->rc);
++
++
++ for (i = 0; i < kNumStates; i++)
++ {
++ UInt32 j;
++ for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++)
++ {
++ p->isMatch[i][j] = kProbInitValue;
++ p->isRep0Long[i][j] = kProbInitValue;
++ }
++ p->isRep[i] = kProbInitValue;
++ p->isRepG0[i] = kProbInitValue;
++ p->isRepG1[i] = kProbInitValue;
++ p->isRepG2[i] = kProbInitValue;
++ }
++
++ {
++ UInt32 num = 0x300 << (p->lp + p->lc);
++ for (i = 0; i < num; i++)
++ p->litProbs[i] = kProbInitValue;
++ }
++
++ {
++ for (i = 0; i < kNumLenToPosStates; i++)
++ {
++ CLzmaProb *probs = p->posSlotEncoder[i];
++ UInt32 j;
++ for (j = 0; j < (1 << kNumPosSlotBits); j++)
++ probs[j] = kProbInitValue;
++ }
++ }
++ {
++ for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++)
++ p->posEncoders[i] = kProbInitValue;
++ }
++
++ LenEnc_Init(&p->lenEnc.p);
++ LenEnc_Init(&p->repLenEnc.p);
++
++ for (i = 0; i < (1 << kNumAlignBits); i++)
++ p->posAlignEncoder[i] = kProbInitValue;
++
++ p->optimumEndIndex = 0;
++ p->optimumCurrentIndex = 0;
++ p->additionalOffset = 0;
++
++ p->pbMask = (1 << p->pb) - 1;
++ p->lpMask = (1 << p->lp) - 1;
++}
++
++static void LzmaEnc_InitPrices(CLzmaEnc *p)
++{
++ if (!p->fastMode)
++ {
++ FillDistancesPrices(p);
++ FillAlignPrices(p);
++ }
++
++ p->lenEnc.tableSize =
++ p->repLenEnc.tableSize =
++ p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN;
++ LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices);
++ LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices);
++}
++
++static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ UInt32 i;
++ for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++)
++ if (p->dictSize <= ((UInt32)1 << i))
++ break;
++ p->distTableSize = i * 2;
++
++ p->finished = False;
++ p->result = SZ_OK;
++ RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig));
++ LzmaEnc_Init(p);
++ LzmaEnc_InitPrices(p);
++ p->nowPos64 = 0;
++ return SZ_OK;
++}
++
++static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen)
++{
++ p->matchFinderBase.directInput = 1;
++ p->matchFinderBase.bufferBase = (Byte *)src;
++ p->matchFinderBase.directInputRem = srcLen;
++}
++
++static SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
++ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ LzmaEnc_SetInputBuf(p, src, srcLen);
++ p->needInit = 1;
++
++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
++}
++
++static void LzmaEnc_Finish(CLzmaEncHandle pp)
++{
++ #ifndef _7ZIP_ST
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ if (p->mtMode)
++ MatchFinderMt_ReleaseStream(&p->matchFinderMt);
++ #else
++ pp = pp;
++ #endif
++}
++
++typedef struct
++{
++ ISeqOutStream funcTable;
++ Byte *data;
++ SizeT rem;
++ Bool overflow;
++} CSeqOutStreamBuf;
++
++static size_t MyWrite(void *pp, const void *data, size_t size)
++{
++ CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp;
++ if (p->rem < size)
++ {
++ size = p->rem;
++ p->overflow = True;
++ }
++ memcpy(p->data, data, size);
++ p->rem -= size;
++ p->data += size;
++ return size;
++}
++
++static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress)
++{
++ SRes res = SZ_OK;
++
++ #ifndef _7ZIP_ST
++ Byte allocaDummy[0x300];
++ int i = 0;
++ for (i = 0; i < 16; i++)
++ allocaDummy[i] = (Byte)i;
++ #endif
++
++ for (;;)
++ {
++ res = LzmaEnc_CodeOneBlock(p, False, 0, 0);
++ if (res != SZ_OK || p->finished != 0)
++ break;
++ if (progress != 0)
++ {
++ res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc));
++ if (res != SZ_OK)
++ {
++ res = SZ_ERROR_PROGRESS;
++ break;
++ }
++ }
++ }
++ LzmaEnc_Finish(p);
++ return res;
++}
++
++SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ int i;
++ UInt32 dictSize = p->dictSize;
++ if (*size < LZMA_PROPS_SIZE)
++ return SZ_ERROR_PARAM;
++ *size = LZMA_PROPS_SIZE;
++ props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc);
++
++ for (i = 11; i <= 30; i++)
++ {
++ if (dictSize <= ((UInt32)2 << i))
++ {
++ dictSize = (2 << i);
++ break;
++ }
++ if (dictSize <= ((UInt32)3 << i))
++ {
++ dictSize = (3 << i);
++ break;
++ }
++ }
++
++ for (i = 0; i < 4; i++)
++ props[1 + i] = (Byte)(dictSize >> (8 * i));
++ return SZ_OK;
++}
++
++SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ SRes res;
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++
++ CSeqOutStreamBuf outStream;
++
++ LzmaEnc_SetInputBuf(p, src, srcLen);
++
++ outStream.funcTable.Write = MyWrite;
++ outStream.data = dest;
++ outStream.rem = *destLen;
++ outStream.overflow = False;
++
++ p->writeEndMark = writeEndMark;
++
++ p->rc.outStream = &outStream.funcTable;
++ res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig);
++ if (res == SZ_OK)
++ res = LzmaEnc_Encode2(p, progress);
++
++ *destLen -= outStream.rem;
++ if (outStream.overflow)
++ return SZ_ERROR_OUTPUT_EOF;
++ return res;
++}
+--- /dev/null
++++ b/lib/lzma/Makefile
+@@ -0,0 +1,7 @@
++lzma_compress-objs := LzFind.o LzmaEnc.o
++lzma_decompress-objs := LzmaDec.o
++
++obj-$(CONFIG_LZMA_COMPRESS) += lzma_compress.o
++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma_decompress.o
++
++EXTRA_CFLAGS += -Iinclude/linux -Iinclude/linux/lzma -include types.h
diff --git a/target/linux/generic/pending-6.1/532-jffs2_eofdetect.patch b/target/linux/generic/pending-6.1/532-jffs2_eofdetect.patch
new file mode 100644
index 0000000000..744fbd0e21
--- /dev/null
+++ b/target/linux/generic/pending-6.1/532-jffs2_eofdetect.patch
@@ -0,0 +1,65 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: fs: jffs2: EOF marker
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ fs/jffs2/build.c | 10 ++++++++++
+ fs/jffs2/scan.c | 21 +++++++++++++++++++--
+ 2 files changed, 29 insertions(+), 2 deletions(-)
+
+--- a/fs/jffs2/build.c
++++ b/fs/jffs2/build.c
+@@ -117,6 +117,16 @@ static int jffs2_build_filesystem(struct
+ dbg_fsbuild("scanned flash completely\n");
+ jffs2_dbg_dump_block_lists_nolock(c);
+
++ if (c->flags & (1 << 7)) {
++ printk("%s(): unlocking the mtd device... ", __func__);
++ mtd_unlock(c->mtd, 0, c->mtd->size);
++ printk("done.\n");
++
++ printk("%s(): erasing all blocks after the end marker... ", __func__);
++ jffs2_erase_pending_blocks(c, -1);
++ printk("done.\n");
++ }
++
+ dbg_fsbuild("pass 1 starting\n");
+ c->flags |= JFFS2_SB_FLAG_BUILDING;
+ /* Now scan the directory tree, increasing nlink according to every dirent found. */
+--- a/fs/jffs2/scan.c
++++ b/fs/jffs2/scan.c
+@@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_in
+ /* reset summary info for next eraseblock scan */
+ jffs2_sum_reset_collected(s);
+
+- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
+- buf_size, s);
++ if (c->flags & (1 << 7)) {
++ if (mtd_block_isbad(c->mtd, jeb->offset))
++ ret = BLK_STATE_BADBLOCK;
++ else
++ ret = BLK_STATE_ALLFF;
++ } else
++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
++ buf_size, s);
+
+ if (ret < 0)
+ goto out;
+@@ -567,6 +573,17 @@ full_scan:
+ return err;
+ }
+
++ if ((buf[0] == 0xde) &&
++ (buf[1] == 0xad) &&
++ (buf[2] == 0xc0) &&
++ (buf[3] == 0xde)) {
++ /* end of filesystem. erase everything after this point */
++ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset);
++ c->flags |= (1 << 7);
++
++ return BLK_STATE_ALLFF;
++ }
++
+ /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
+ ofs = 0;
+ max_ofs = EMPTY_SCAN_SIZE(c->sector_size);
diff --git a/target/linux/generic/pending-6.1/600-netfilter_conntrack_flush.patch b/target/linux/generic/pending-6.1/600-netfilter_conntrack_flush.patch
new file mode 100644
index 0000000000..a88e3d7d9a
--- /dev/null
+++ b/target/linux/generic/pending-6.1/600-netfilter_conntrack_flush.patch
@@ -0,0 +1,88 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: netfilter: add support for flushing conntrack via /proc
+
+lede-commit 8193bbe59a74d34d6a26d4a8cb857b1952905314
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/netfilter/nf_conntrack_standalone.c | 59 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 58 insertions(+), 1 deletion(-)
+
+--- a/net/netfilter/nf_conntrack_standalone.c
++++ b/net/netfilter/nf_conntrack_standalone.c
+@@ -9,6 +9,7 @@
+ #include <linux/percpu.h>
+ #include <linux/netdevice.h>
+ #include <linux/security.h>
++#include <linux/inet.h>
+ #include <net/net_namespace.h>
+ #ifdef CONFIG_SYSCTL
+ #include <linux/sysctl.h>
+@@ -462,6 +463,56 @@ static int ct_cpu_seq_show(struct seq_fi
+ return 0;
+ }
+
++struct kill_request {
++ u16 family;
++ union nf_inet_addr addr;
++};
++
++static int kill_matching(struct nf_conn *i, void *data)
++{
++ struct kill_request *kr = data;
++ struct nf_conntrack_tuple *t1 = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
++ struct nf_conntrack_tuple *t2 = &i->tuplehash[IP_CT_DIR_REPLY].tuple;
++
++ if (!kr->family)
++ return 1;
++
++ if (t1->src.l3num != kr->family)
++ return 0;
++
++ return (nf_inet_addr_cmp(&kr->addr, &t1->src.u3) ||
++ nf_inet_addr_cmp(&kr->addr, &t1->dst.u3) ||
++ nf_inet_addr_cmp(&kr->addr, &t2->src.u3) ||
++ nf_inet_addr_cmp(&kr->addr, &t2->dst.u3));
++}
++
++static int ct_file_write(struct file *file, char *buf, size_t count)
++{
++ struct seq_file *seq = file->private_data;
++ struct net *net = seq_file_net(seq);
++ struct kill_request kr = { };
++
++ if (count == 0)
++ return 0;
++
++ if (count >= INET6_ADDRSTRLEN)
++ count = INET6_ADDRSTRLEN - 1;
++
++ if (strnchr(buf, count, ':')) {
++ kr.family = AF_INET6;
++ if (!in6_pton(buf, count, (void *)&kr.addr, '\n', NULL))
++ return -EINVAL;
++ } else if (strnchr(buf, count, '.')) {
++ kr.family = AF_INET;
++ if (!in4_pton(buf, count, (void *)&kr.addr, '\n', NULL))
++ return -EINVAL;
++ }
++
++ nf_ct_iterate_cleanup_net(net, kill_matching, &kr, 0, 0);
++
++ return 0;
++}
++
+ static const struct seq_operations ct_cpu_seq_ops = {
+ .start = ct_cpu_seq_start,
+ .next = ct_cpu_seq_next,
+@@ -475,8 +526,9 @@ static int nf_conntrack_standalone_init_
+ kuid_t root_uid;
+ kgid_t root_gid;
+
+- pde = proc_create_net("nf_conntrack", 0440, net->proc_net, &ct_seq_ops,
+- sizeof(struct ct_iter_state));
++ pde = proc_create_net_data_write("nf_conntrack", 0440, net->proc_net,
++ &ct_seq_ops, &ct_file_write,
++ sizeof(struct ct_iter_state), NULL);
+ if (!pde)
+ goto out_nf_conntrack;
+
diff --git a/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch b/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch
new file mode 100644
index 0000000000..c1e050e935
--- /dev/null
+++ b/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch
@@ -0,0 +1,110 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: add a new version of my netfilter speedup patches for linux 2.6.39 and 3.0
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/uapi/linux/netfilter_ipv4/ip_tables.h | 1 +
+ net/ipv4/netfilter/ip_tables.c | 37 +++++++++++++++++++++++++++
+ 2 files changed, 38 insertions(+)
+
+--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h
++++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h
+@@ -89,6 +89,7 @@ struct ipt_ip {
+ #define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */
+ #define IPT_F_GOTO 0x02 /* Set if jump is a goto */
+ #define IPT_F_MASK 0x03 /* All possible flag bits mask. */
++#define IPT_F_NO_DEF_MATCH 0x80 /* Internal: no default match rules present */
+
+ /* Values for "inv" field in struct ipt_ip. */
+ #define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -50,6 +50,9 @@ ip_packet_match(const struct iphdr *ip,
+ {
+ unsigned long ret;
+
++ if (ipinfo->flags & IPT_F_NO_DEF_MATCH)
++ return true;
++
+ if (NF_INVF(ipinfo, IPT_INV_SRCIP,
+ (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) ||
+ NF_INVF(ipinfo, IPT_INV_DSTIP,
+@@ -80,6 +83,29 @@ ip_packet_match(const struct iphdr *ip,
+ return true;
+ }
+
++static void
++ip_checkdefault(struct ipt_ip *ip)
++{
++ static const char iface_mask[IFNAMSIZ] = {};
++
++ if (ip->invflags || ip->flags & IPT_F_FRAG)
++ return;
++
++ if (memcmp(ip->iniface_mask, iface_mask, IFNAMSIZ) != 0)
++ return;
++
++ if (memcmp(ip->outiface_mask, iface_mask, IFNAMSIZ) != 0)
++ return;
++
++ if (ip->smsk.s_addr || ip->dmsk.s_addr)
++ return;
++
++ if (ip->proto)
++ return;
++
++ ip->flags |= IPT_F_NO_DEF_MATCH;
++}
++
+ static bool
+ ip_checkentry(const struct ipt_ip *ip)
+ {
+@@ -524,6 +550,8 @@ find_check_entry(struct ipt_entry *e, st
+ struct xt_mtchk_param mtpar;
+ struct xt_entry_match *ematch;
+
++ ip_checkdefault(&e->ip);
++
+ if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
+ return -ENOMEM;
+
+@@ -818,6 +846,7 @@ copy_entries_to_user(unsigned int total_
+ const struct xt_table_info *private = table->private;
+ int ret = 0;
+ const void *loc_cpu_entry;
++ u8 flags;
+
+ counters = alloc_counters(table);
+ if (IS_ERR(counters))
+@@ -845,6 +874,14 @@ copy_entries_to_user(unsigned int total_
+ goto free_counters;
+ }
+
++ flags = e->ip.flags & IPT_F_MASK;
++ if (copy_to_user(userptr + off
++ + offsetof(struct ipt_entry, ip.flags),
++ &flags, sizeof(flags)) != 0) {
++ ret = -EFAULT;
++ goto free_counters;
++ }
++
+ for (i = sizeof(struct ipt_entry);
+ i < e->target_offset;
+ i += m->u.match_size) {
+@@ -1222,12 +1259,15 @@ compat_copy_entry_to_user(struct ipt_ent
+ compat_uint_t origsize;
+ const struct xt_entry_match *ematch;
+ int ret = 0;
++ u8 flags = e->ip.flags & IPT_F_MASK;
+
+ origsize = *size;
+ ce = *dstptr;
+ if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 ||
+ copy_to_user(&ce->counters, &counters[i],
+- sizeof(counters[i])) != 0)
++ sizeof(counters[i])) != 0 ||
++ copy_to_user(&ce->ip.flags, &flags,
++ sizeof(flags)) != 0)
+ return -EFAULT;
+
+ *dstptr += sizeof(struct compat_ipt_entry);
diff --git a/target/linux/generic/pending-6.1/611-netfilter_match_bypass_default_table.patch b/target/linux/generic/pending-6.1/611-netfilter_match_bypass_default_table.patch
new file mode 100644
index 0000000000..baf738a8d2
--- /dev/null
+++ b/target/linux/generic/pending-6.1/611-netfilter_match_bypass_default_table.patch
@@ -0,0 +1,106 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: netfilter: match bypass default table
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/ipv4/netfilter/ip_tables.c | 79 +++++++++++++++++++++++++++++++-----------
+ 1 file changed, 58 insertions(+), 21 deletions(-)
+
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -246,6 +246,33 @@ struct ipt_entry *ipt_next_entry(const s
+ return (void *)entry + entry->next_offset;
+ }
+
++static bool
++ipt_handle_default_rule(struct ipt_entry *e, unsigned int *verdict)
++{
++ struct xt_entry_target *t;
++ struct xt_standard_target *st;
++
++ if (e->target_offset != sizeof(struct ipt_entry))
++ return false;
++
++ if (!(e->ip.flags & IPT_F_NO_DEF_MATCH))
++ return false;
++
++ t = ipt_get_target(e);
++ if (t->u.kernel.target->target)
++ return false;
++
++ st = (struct xt_standard_target *) t;
++ if (st->verdict == XT_RETURN)
++ return false;
++
++ if (st->verdict >= 0)
++ return false;
++
++ *verdict = (unsigned)(-st->verdict) - 1;
++ return true;
++}
++
+ /* Returns one of the generic firewall policies, like NF_ACCEPT. */
+ unsigned int
+ ipt_do_table(struct sk_buff *skb,
+@@ -266,27 +293,28 @@ ipt_do_table(struct sk_buff *skb,
+ unsigned int addend;
+
+ /* Initialization */
++ WARN_ON(!(table->valid_hooks & (1 << hook)));
++ local_bh_disable();
++ private = READ_ONCE(table->private); /* Address dependency. */
++ cpu = smp_processor_id();
++ table_base = private->entries;
++
++ e = get_entry(table_base, private->hook_entry[hook]);
++ if (ipt_handle_default_rule(e, &verdict)) {
++ struct xt_counters *counter;
++
++ counter = xt_get_this_cpu_counter(&e->counters);
++ ADD_COUNTER(*counter, skb->len, 1);
++ local_bh_enable();
++ return verdict;
++ }
++
+ stackidx = 0;
+ ip = ip_hdr(skb);
+ indev = state->in ? state->in->name : nulldevname;
+ outdev = state->out ? state->out->name : nulldevname;
+- /* We handle fragments by dealing with the first fragment as
+- * if it was a normal packet. All other fragments are treated
+- * normally, except that they will NEVER match rules that ask
+- * things we don't know, ie. tcp syn flag or ports). If the
+- * rule is also a fragment-specific rule, non-fragments won't
+- * match it. */
+- acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
+- acpar.thoff = ip_hdrlen(skb);
+- acpar.hotdrop = false;
+- acpar.state = state;
+
+- WARN_ON(!(table->valid_hooks & (1 << hook)));
+- local_bh_disable();
+ addend = xt_write_recseq_begin();
+- private = READ_ONCE(table->private); /* Address dependency. */
+- cpu = smp_processor_id();
+- table_base = private->entries;
+ jumpstack = (struct ipt_entry **)private->jumpstack[cpu];
+
+ /* Switch to alternate jumpstack if we're being invoked via TEE.
+@@ -299,7 +327,16 @@ ipt_do_table(struct sk_buff *skb,
+ if (static_key_false(&xt_tee_enabled))
+ jumpstack += private->stacksize * __this_cpu_read(nf_skb_duplicated);
+
+- e = get_entry(table_base, private->hook_entry[hook]);
++ /* We handle fragments by dealing with the first fragment as
++ * if it was a normal packet. All other fragments are treated
++ * normally, except that they will NEVER match rules that ask
++ * things we don't know, ie. tcp syn flag or ports). If the
++ * rule is also a fragment-specific rule, non-fragments won't
++ * match it. */
++ acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
++ acpar.thoff = ip_hdrlen(skb);
++ acpar.hotdrop = false;
++ acpar.state = state;
+
+ do {
+ const struct xt_entry_target *t;
diff --git a/target/linux/generic/pending-6.1/612-netfilter_match_reduce_memory_access.patch b/target/linux/generic/pending-6.1/612-netfilter_match_reduce_memory_access.patch
new file mode 100644
index 0000000000..79da6778b6
--- /dev/null
+++ b/target/linux/generic/pending-6.1/612-netfilter_match_reduce_memory_access.patch
@@ -0,0 +1,22 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: netfilter: reduce match memory access
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ net/ipv4/netfilter/ip_tables.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -53,9 +53,9 @@ ip_packet_match(const struct iphdr *ip,
+ if (ipinfo->flags & IPT_F_NO_DEF_MATCH)
+ return true;
+
+- if (NF_INVF(ipinfo, IPT_INV_SRCIP,
++ if (NF_INVF(ipinfo, IPT_INV_SRCIP, ipinfo->smsk.s_addr &&
+ (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) ||
+- NF_INVF(ipinfo, IPT_INV_DSTIP,
++ NF_INVF(ipinfo, IPT_INV_DSTIP, ipinfo->dmsk.s_addr &&
+ (ip->daddr & ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr))
+ return false;
+
diff --git a/target/linux/generic/pending-6.1/620-net_sched-codel-do-not-defer-queue-length-update.patch b/target/linux/generic/pending-6.1/620-net_sched-codel-do-not-defer-queue-length-update.patch
new file mode 100644
index 0000000000..4b4825ae3b
--- /dev/null
+++ b/target/linux/generic/pending-6.1/620-net_sched-codel-do-not-defer-queue-length-update.patch
@@ -0,0 +1,86 @@
+From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
+Date: Mon, 21 Aug 2017 11:14:14 +0300
+Subject: [PATCH] net_sched/codel: do not defer queue length update
+
+When codel wants to drop last packet in ->dequeue() it cannot call
+qdisc_tree_reduce_backlog() right away - it will notify parent qdisc
+about zero qlen and HTB/HFSC will deactivate class. The same class will
+be deactivated second time by caller of ->dequeue(). Currently codel and
+fq_codel defer update. This triggers warning in HFSC when it's qlen != 0
+but there is no active classes.
+
+This patch update parent queue length immediately: just temporary increase
+qlen around qdisc_tree_reduce_backlog() to prevent first class deactivation
+if we have skb to return.
+
+This might open another problem in HFSC - now operation peek could fail and
+deactivate parent class.
+
+Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
+---
+
+--- a/net/sched/sch_codel.c
++++ b/net/sched/sch_codel.c
+@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_deque
+ &q->stats, qdisc_pkt_len, codel_get_enqueue_time,
+ drop_func, dequeue_func);
+
+- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
+- * or HTB crashes. Defer it for next round.
++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
++ * parent class, dequeue in parent qdisc will do the same if we
++ * return skb. Temporary increment qlen if we have skb.
+ */
+- if (q->stats.drop_count && sch->q.qlen) {
+- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
++ if (q->stats.drop_count) {
++ if (skb)
++ sch->q.qlen++;
++ qdisc_tree_reduce_backlog(sch, q->stats.drop_count,
++ q->stats.drop_len);
++ if (skb)
++ sch->q.qlen--;
+ q->stats.drop_count = 0;
+ q->stats.drop_len = 0;
+ }
+--- a/net/sched/sch_fq_codel.c
++++ b/net/sched/sch_fq_codel.c
+@@ -304,6 +304,21 @@ begin:
+ &flow->cvars, &q->cstats, qdisc_pkt_len,
+ codel_get_enqueue_time, drop_func, dequeue_func);
+
++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
++ * parent class, dequeue in parent qdisc will do the same if we
++ * return skb. Temporary increment qlen if we have skb.
++ */
++ if (q->cstats.drop_count) {
++ if (skb)
++ sch->q.qlen++;
++ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
++ q->cstats.drop_len);
++ if (skb)
++ sch->q.qlen--;
++ q->cstats.drop_count = 0;
++ q->cstats.drop_len = 0;
++ }
++
+ if (!skb) {
+ /* force a pass through old_flows to prevent starvation */
+ if ((head == &q->new_flows) && !list_empty(&q->old_flows))
+@@ -314,15 +329,6 @@ begin:
+ }
+ qdisc_bstats_update(sch, skb);
+ flow->deficit -= qdisc_pkt_len(skb);
+- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
+- * or HTB crashes. Defer it for next round.
+- */
+- if (q->cstats.drop_count && sch->q.qlen) {
+- qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
+- q->cstats.drop_len);
+- q->cstats.drop_count = 0;
+- q->cstats.drop_len = 0;
+- }
+ return skb;
+ }
+
diff --git a/target/linux/generic/pending-6.1/630-packet_socket_type.patch b/target/linux/generic/pending-6.1/630-packet_socket_type.patch
new file mode 100644
index 0000000000..2d2c7b6cc5
--- /dev/null
+++ b/target/linux/generic/pending-6.1/630-packet_socket_type.patch
@@ -0,0 +1,138 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: net: add an optimization for dealing with raw sockets
+
+lede-commit: 4898039703d7315f0f3431c860123338ec3be0f6
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/uapi/linux/if_packet.h | 3 +++
+ net/packet/af_packet.c | 34 +++++++++++++++++++++++++++-------
+ net/packet/internal.h | 1 +
+ 3 files changed, 31 insertions(+), 7 deletions(-)
+
+--- a/include/uapi/linux/if_packet.h
++++ b/include/uapi/linux/if_packet.h
+@@ -33,6 +33,8 @@ struct sockaddr_ll {
+ #define PACKET_KERNEL 7 /* To kernel space */
+ /* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */
+ #define PACKET_FASTROUTE 6 /* Fastrouted frame */
++#define PACKET_MASK_ANY 0xffffffff /* mask for packet type bits */
++
+
+ /* Packet socket options */
+
+@@ -59,6 +61,7 @@ struct sockaddr_ll {
+ #define PACKET_ROLLOVER_STATS 21
+ #define PACKET_FANOUT_DATA 22
+ #define PACKET_IGNORE_OUTGOING 23
++#define PACKET_RECV_TYPE 24
+
+ #define PACKET_FANOUT_HASH 0
+ #define PACKET_FANOUT_LB 1
+--- a/net/packet/af_packet.c
++++ b/net/packet/af_packet.c
+@@ -1826,6 +1826,7 @@ static int packet_rcv_spkt(struct sk_buf
+ {
+ struct sock *sk;
+ struct sockaddr_pkt *spkt;
++ struct packet_sock *po;
+
+ /*
+ * When we registered the protocol we saved the socket in the data
+@@ -1833,6 +1834,7 @@ static int packet_rcv_spkt(struct sk_buf
+ */
+
+ sk = pt->af_packet_priv;
++ po = pkt_sk(sk);
+
+ /*
+ * Yank back the headers [hope the device set this
+@@ -1845,7 +1847,7 @@ static int packet_rcv_spkt(struct sk_buf
+ * so that this procedure is noop.
+ */
+
+- if (skb->pkt_type == PACKET_LOOPBACK)
++ if (!(po->pkt_type & (1 << skb->pkt_type)))
+ goto out;
+
+ if (!net_eq(dev_net(dev), sock_net(sk)))
+@@ -2093,12 +2095,12 @@ static int packet_rcv(struct sk_buff *sk
+ unsigned int snaplen, res;
+ bool is_drop_n_account = false;
+
+- if (skb->pkt_type == PACKET_LOOPBACK)
+- goto drop;
+-
+ sk = pt->af_packet_priv;
+ po = pkt_sk(sk);
+
++ if (!(po->pkt_type & (1 << skb->pkt_type)))
++ goto drop;
++
+ if (!net_eq(dev_net(dev), sock_net(sk)))
+ goto drop;
+
+@@ -2224,12 +2226,12 @@ static int tpacket_rcv(struct sk_buff *s
+ BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32);
+ BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48);
+
+- if (skb->pkt_type == PACKET_LOOPBACK)
+- goto drop;
+-
+ sk = pt->af_packet_priv;
+ po = pkt_sk(sk);
+
++ if (!(po->pkt_type & (1 << skb->pkt_type)))
++ goto drop;
++
+ if (!net_eq(dev_net(dev), sock_net(sk)))
+ goto drop;
+
+@@ -3342,6 +3344,7 @@ static int packet_create(struct net *net
+ mutex_init(&po->pg_vec_lock);
+ po->rollover = NULL;
+ po->prot_hook.func = packet_rcv;
++ po->pkt_type = PACKET_MASK_ANY & ~(1 << PACKET_LOOPBACK);
+
+ if (sock->type == SOCK_PACKET)
+ po->prot_hook.func = packet_rcv_spkt;
+@@ -3979,6 +3982,16 @@ packet_setsockopt(struct socket *sock, i
+ WRITE_ONCE(po->xmit, val ? packet_direct_xmit : dev_queue_xmit);
+ return 0;
+ }
++ case PACKET_RECV_TYPE:
++ {
++ unsigned int val;
++ if (optlen != sizeof(val))
++ return -EINVAL;
++ if (copy_from_sockptr(&val, optval, sizeof(val)))
++ return -EFAULT;
++ po->pkt_type = val & ~BIT(PACKET_LOOPBACK);
++ return 0;
++ }
+ default:
+ return -ENOPROTOOPT;
+ }
+@@ -4035,6 +4048,13 @@ static int packet_getsockopt(struct sock
+ case PACKET_VNET_HDR:
+ val = po->has_vnet_hdr;
+ break;
++ case PACKET_RECV_TYPE:
++ if (len > sizeof(unsigned int))
++ len = sizeof(unsigned int);
++ val = po->pkt_type;
++
++ data = &val;
++ break;
+ case PACKET_VERSION:
+ val = po->tp_version;
+ break;
+--- a/net/packet/internal.h
++++ b/net/packet/internal.h
+@@ -136,6 +136,7 @@ struct packet_sock {
+ int (*xmit)(struct sk_buff *skb);
+ struct packet_type prot_hook ____cacheline_aligned_in_smp;
+ atomic_t tp_drops ____cacheline_aligned_in_smp;
++ unsigned int pkt_type;
+ };
+
+ static inline struct packet_sock *pkt_sk(struct sock *sk)
diff --git a/target/linux/generic/pending-6.1/655-increase_skb_pad.patch b/target/linux/generic/pending-6.1/655-increase_skb_pad.patch
new file mode 100644
index 0000000000..5d100270a9
--- /dev/null
+++ b/target/linux/generic/pending-6.1/655-increase_skb_pad.patch
@@ -0,0 +1,20 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: kernel: add a few patches for avoiding unnecessary skb reallocations - significantly improves ethernet<->wireless performance
+
+lede-commit: 6f89cffc9add6939d44a6b54cf9a5e77849aa7fd
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/skbuff.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -2821,7 +2821,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
+-#define NET_SKB_PAD max(32, L1_CACHE_BYTES)
++#define NET_SKB_PAD max(64, L1_CACHE_BYTES)
+ #endif
+
+ int ___pskb_trim(struct sk_buff *skb, unsigned int len);
diff --git a/target/linux/generic/pending-6.1/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch b/target/linux/generic/pending-6.1/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch
new file mode 100644
index 0000000000..2f6fbd59e4
--- /dev/null
+++ b/target/linux/generic/pending-6.1/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch
@@ -0,0 +1,511 @@
+From: Steven Barth <steven@midlink.org>
+Subject: Add support for MAP-E FMRs (mesh mode)
+
+MAP-E FMRs (draft-ietf-softwire-map-10) are rules for IPv4-communication
+between MAP CEs (mesh mode) without the need to forward such data to a
+border relay. This is similar to how 6rd works but for IPv4 over IPv6.
+
+Signed-off-by: Steven Barth <cyrus@openwrt.org>
+---
+ include/net/ip6_tunnel.h | 13 ++
+ include/uapi/linux/if_tunnel.h | 13 ++
+ net/ipv6/ip6_tunnel.c | 276 +++++++++++++++++++++++++++++++++++++++--
+ 3 files changed, 291 insertions(+), 11 deletions(-)
+
+--- a/include/net/ip6_tunnel.h
++++ b/include/net/ip6_tunnel.h
+@@ -18,6 +18,18 @@
+ /* determine capability on a per-packet basis */
+ #define IP6_TNL_F_CAP_PER_PACKET 0x40000
+
++/* IPv6 tunnel FMR */
++struct __ip6_tnl_fmr {
++ struct __ip6_tnl_fmr *next; /* next fmr in list */
++ struct in6_addr ip6_prefix;
++ struct in_addr ip4_prefix;
++
++ __u8 ip6_prefix_len;
++ __u8 ip4_prefix_len;
++ __u8 ea_len;
++ __u8 offset;
++};
++
+ struct __ip6_tnl_parm {
+ char name[IFNAMSIZ]; /* name of tunnel device */
+ int link; /* ifindex of underlying L2 interface */
+@@ -29,6 +41,7 @@ struct __ip6_tnl_parm {
+ __u32 flags; /* tunnel flags */
+ struct in6_addr laddr; /* local tunnel end-point address */
+ struct in6_addr raddr; /* remote tunnel end-point address */
++ struct __ip6_tnl_fmr *fmrs; /* FMRs */
+
+ __be16 i_flags;
+ __be16 o_flags;
+--- a/include/uapi/linux/if_tunnel.h
++++ b/include/uapi/linux/if_tunnel.h
+@@ -77,10 +77,23 @@ enum {
+ IFLA_IPTUN_ENCAP_DPORT,
+ IFLA_IPTUN_COLLECT_METADATA,
+ IFLA_IPTUN_FWMARK,
++ IFLA_IPTUN_FMRS,
+ __IFLA_IPTUN_MAX,
+ };
+ #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
+
++enum {
++ IFLA_IPTUN_FMR_UNSPEC,
++ IFLA_IPTUN_FMR_IP6_PREFIX,
++ IFLA_IPTUN_FMR_IP4_PREFIX,
++ IFLA_IPTUN_FMR_IP6_PREFIX_LEN,
++ IFLA_IPTUN_FMR_IP4_PREFIX_LEN,
++ IFLA_IPTUN_FMR_EA_LEN,
++ IFLA_IPTUN_FMR_OFFSET,
++ __IFLA_IPTUN_FMR_MAX,
++};
++#define IFLA_IPTUN_FMR_MAX (__IFLA_IPTUN_FMR_MAX - 1)
++
+ enum tunnel_encap_types {
+ TUNNEL_ENCAP_NONE,
+ TUNNEL_ENCAP_FOU,
+--- a/net/ipv6/ip6_tunnel.c
++++ b/net/ipv6/ip6_tunnel.c
+@@ -11,6 +11,9 @@
+ * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c
+ *
+ * RFC 2473
++ *
++ * Changes:
++ * Steven Barth <cyrus@openwrt.org>: MAP-E FMR support
+ */
+
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+@@ -67,9 +70,9 @@ static bool log_ecn_error = true;
+ module_param(log_ecn_error, bool, 0644);
+ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
+
+-static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2)
++static u32 HASH(const struct in6_addr *addr)
+ {
+- u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2);
++ u32 hash = ipv6_addr_hash(addr);
+
+ return hash_32(hash, IP6_TUNNEL_HASH_SIZE_SHIFT);
+ }
+@@ -114,17 +117,33 @@ static struct ip6_tnl *
+ ip6_tnl_lookup(struct net *net, int link,
+ const struct in6_addr *remote, const struct in6_addr *local)
+ {
+- unsigned int hash = HASH(remote, local);
++ unsigned int hash = HASH(local);
+ struct ip6_tnl *t, *cand = NULL;
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ struct in6_addr any;
+
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (!ipv6_addr_equal(local, &t->parms.laddr) ||
+- !ipv6_addr_equal(remote, &t->parms.raddr) ||
+ !(t->dev->flags & IFF_UP))
+ continue;
+
++ if (!ipv6_addr_equal(remote, &t->parms.raddr)) {
++ struct __ip6_tnl_fmr *fmr;
++ bool found = false;
++
++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) {
++ if (!ipv6_prefix_equal(remote, &fmr->ip6_prefix,
++ fmr->ip6_prefix_len))
++ continue;
++
++ found = true;
++ break;
++ }
++
++ if (!found)
++ continue;
++ }
++
+ if (link == t->parms.link)
+ return t;
+ else
+@@ -132,7 +151,7 @@ ip6_tnl_lookup(struct net *net, int link
+ }
+
+ memset(&any, 0, sizeof(any));
+- hash = HASH(&any, local);
++ hash = HASH(local);
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (!ipv6_addr_equal(local, &t->parms.laddr) ||
+ !ipv6_addr_any(&t->parms.raddr) ||
+@@ -145,7 +164,7 @@ ip6_tnl_lookup(struct net *net, int link
+ cand = t;
+ }
+
+- hash = HASH(remote, &any);
++ hash = HASH(&any);
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
+ !ipv6_addr_any(&t->parms.laddr) ||
+@@ -194,7 +213,7 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n,
+
+ if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) {
+ prio = 1;
+- h = HASH(remote, local);
++ h = HASH(local);
+ }
+ return &ip6n->tnls[prio][h];
+ }
+@@ -378,6 +397,12 @@ ip6_tnl_dev_uninit(struct net_device *de
+ struct net *net = t->net;
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+
++ while (t->parms.fmrs) {
++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next;
++ kfree(t->parms.fmrs);
++ t->parms.fmrs = next;
++ }
++
+ if (dev == ip6n->fb_tnl_dev)
+ RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL);
+ else
+@@ -790,6 +815,107 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
+ }
+ EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl);
+
++/**
++ * ip4ip6_fmr_calc - calculate target / source IPv6-address based on FMR
++ * @dest: destination IPv6 address buffer
++ * @skb: received socket buffer
++ * @fmr: MAP FMR
++ * @xmit: Calculate for xmit or rcv
++ **/
++static void ip4ip6_fmr_calc(struct in6_addr *dest,
++ const struct iphdr *iph, const uint8_t *end,
++ const struct __ip6_tnl_fmr *fmr, bool xmit)
++{
++ int psidlen = fmr->ea_len - (32 - fmr->ip4_prefix_len);
++ u8 *portp = NULL;
++ bool use_dest_addr;
++ const struct iphdr *dsth = iph;
++
++ if ((u8*)dsth >= end)
++ return;
++
++ /* find significant IP header */
++ if (iph->protocol == IPPROTO_ICMP) {
++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4);
++ if (ih && ((u8*)&ih[1]) <= end && (
++ ih->type == ICMP_DEST_UNREACH ||
++ ih->type == ICMP_SOURCE_QUENCH ||
++ ih->type == ICMP_TIME_EXCEEDED ||
++ ih->type == ICMP_PARAMETERPROB ||
++ ih->type == ICMP_REDIRECT))
++ dsth = (const struct iphdr*)&ih[1];
++ }
++
++ /* in xmit-path use dest port by default and source port only if
++ this is an ICMP reply to something else; vice versa in rcv-path */
++ use_dest_addr = (xmit && dsth == iph) || (!xmit && dsth != iph);
++
++ /* get dst port */
++ if (((u8*)&dsth[1]) <= end && (
++ dsth->protocol == IPPROTO_UDP ||
++ dsth->protocol == IPPROTO_TCP ||
++ dsth->protocol == IPPROTO_SCTP ||
++ dsth->protocol == IPPROTO_DCCP)) {
++ /* for UDP, TCP, SCTP and DCCP source and dest port
++ follow IPv4 header directly */
++ portp = ((u8*)dsth) + dsth->ihl * 4;
++
++ if (use_dest_addr)
++ portp += sizeof(u16);
++ } else if (iph->protocol == IPPROTO_ICMP) {
++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4);
++
++ /* use icmp identifier as port */
++ if (((u8*)&ih) <= end && (
++ (use_dest_addr && (
++ ih->type == ICMP_ECHOREPLY ||
++ ih->type == ICMP_TIMESTAMPREPLY ||
++ ih->type == ICMP_INFO_REPLY ||
++ ih->type == ICMP_ADDRESSREPLY)) ||
++ (!use_dest_addr && (
++ ih->type == ICMP_ECHO ||
++ ih->type == ICMP_TIMESTAMP ||
++ ih->type == ICMP_INFO_REQUEST ||
++ ih->type == ICMP_ADDRESS)
++ )))
++ portp = (u8*)&ih->un.echo.id;
++ }
++
++ if ((portp && &portp[2] <= end) || psidlen == 0) {
++ int frombyte = fmr->ip6_prefix_len / 8;
++ int fromrem = fmr->ip6_prefix_len % 8;
++ int bytes = sizeof(struct in6_addr) - frombyte;
++ const u32 *addr = (use_dest_addr) ? &iph->daddr : &iph->saddr;
++ u64 eabits = ((u64)ntohl(*addr)) << (32 + fmr->ip4_prefix_len);
++ u64 t = 0;
++
++ /* extract PSID from port and add it to eabits */
++ u16 psidbits = 0;
++ if (psidlen > 0) {
++ psidbits = ((u16)portp[0]) << 8 | ((u16)portp[1]);
++ psidbits >>= 16 - psidlen - fmr->offset;
++ psidbits = (u16)(psidbits << (16 - psidlen));
++ eabits |= ((u64)psidbits) << (48 - (fmr->ea_len - psidlen));
++ }
++
++ /* rewrite destination address */
++ *dest = fmr->ip6_prefix;
++ memcpy(&dest->s6_addr[10], addr, sizeof(*addr));
++ dest->s6_addr16[7] = htons(psidbits >> (16 - psidlen));
++
++ if (bytes > sizeof(u64))
++ bytes = sizeof(u64);
++
++ /* insert eabits */
++ memcpy(&t, &dest->s6_addr[frombyte], bytes);
++ t = be64_to_cpu(t) & ~(((((u64)1) << fmr->ea_len) - 1)
++ << (64 - fmr->ea_len - fromrem));
++ t = cpu_to_be64(t | (eabits >> fromrem));
++ memcpy(&dest->s6_addr[frombyte], &t, bytes);
++ }
++}
++
++
+ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
+ const struct tnl_ptk_info *tpi,
+ struct metadata_dst *tun_dst,
+@@ -843,6 +969,27 @@ static int __ip6_tnl_rcv(struct ip6_tnl
+ skb_reset_network_header(skb);
+ memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+
++ if (tpi->proto == htons(ETH_P_IP) && tunnel->parms.fmrs &&
++ !ipv6_addr_equal(&ipv6h->saddr, &tunnel->parms.raddr)) {
++ /* Packet didn't come from BR, so lookup FMR */
++ struct __ip6_tnl_fmr *fmr;
++ struct in6_addr expected = tunnel->parms.raddr;
++ for (fmr = tunnel->parms.fmrs; fmr; fmr = fmr->next)
++ if (ipv6_prefix_equal(&ipv6h->saddr,
++ &fmr->ip6_prefix, fmr->ip6_prefix_len))
++ break;
++
++ /* Check that IPv6 matches IPv4 source to prevent spoofing */
++ if (fmr)
++ ip4ip6_fmr_calc(&expected, ip_hdr(skb),
++ skb_tail_pointer(skb), fmr, false);
++
++ if (!ipv6_addr_equal(&ipv6h->saddr, &expected)) {
++ rcu_read_unlock();
++ goto drop;
++ }
++ }
++
+ __skb_tunnel_rx(skb, tunnel->dev, tunnel->net);
+
+ err = dscp_ecn_decapsulate(tunnel, ipv6h, skb);
+@@ -994,6 +1141,7 @@ static void init_tel_txopt(struct ipv6_t
+ opt->ops.opt_nflen = 8;
+ }
+
++
+ /**
+ * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own
+ * @t: the outgoing tunnel device
+@@ -1274,6 +1422,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str
+ u8 protocol)
+ {
+ struct ip6_tnl *t = netdev_priv(dev);
++ struct __ip6_tnl_fmr *fmr;
+ struct ipv6hdr *ipv6h;
+ const struct iphdr *iph;
+ int encap_limit = -1;
+@@ -1373,6 +1522,18 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+ dsfield = INET_ECN_encapsulate(dsfield, orig_dsfield);
+
++ /* try to find matching FMR */
++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) {
++ unsigned mshift = 32 - fmr->ip4_prefix_len;
++ if (ntohl(fmr->ip4_prefix.s_addr) >> mshift ==
++ ntohl(ip_hdr(skb)->daddr) >> mshift)
++ break;
++ }
++
++ /* change dstaddr according to FMR */
++ if (fmr)
++ ip4ip6_fmr_calc(&fl6.daddr, ip_hdr(skb), skb_tail_pointer(skb), fmr, true);
++
+ if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6))
+ return -1;
+
+@@ -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;
++
++ while (t->parms.fmrs) {
++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next;
++ kfree(t->parms.fmrs);
++ t->parms.fmrs = next;
++ }
++ t->parms.fmrs = p->fmrs;
++
+ dst_cache_reset(&t->dst_cache);
+ ip6_tnl_link_config(t);
+ return 0;
+@@ -1564,6 +1733,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_
+ p->flowinfo = u->flowinfo;
+ p->link = u->link;
+ p->proto = u->proto;
++ p->fmrs = NULL;
+ memcpy(p->name, u->name, sizeof(u->name));
+ }
+
+@@ -1950,6 +2120,15 @@ static int ip6_tnl_validate(struct nlatt
+ return 0;
+ }
+
++static const struct nla_policy ip6_tnl_fmr_policy[IFLA_IPTUN_FMR_MAX + 1] = {
++ [IFLA_IPTUN_FMR_IP6_PREFIX] = { .len = sizeof(struct in6_addr) },
++ [IFLA_IPTUN_FMR_IP4_PREFIX] = { .len = sizeof(struct in_addr) },
++ [IFLA_IPTUN_FMR_IP6_PREFIX_LEN] = { .type = NLA_U8 },
++ [IFLA_IPTUN_FMR_IP4_PREFIX_LEN] = { .type = NLA_U8 },
++ [IFLA_IPTUN_FMR_EA_LEN] = { .type = NLA_U8 },
++ [IFLA_IPTUN_FMR_OFFSET] = { .type = NLA_U8 }
++};
++
+ static void ip6_tnl_netlink_parms(struct nlattr *data[],
+ struct __ip6_tnl_parm *parms)
+ {
+@@ -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]);
++
++ if (data[IFLA_IPTUN_FMRS]) {
++ unsigned rem;
++ struct nlattr *fmr;
++ nla_for_each_nested(fmr, data[IFLA_IPTUN_FMRS], rem) {
++ struct nlattr *fmrd[IFLA_IPTUN_FMR_MAX + 1], *c;
++ struct __ip6_tnl_fmr *nfmr;
++
++ nla_parse_nested(fmrd, IFLA_IPTUN_FMR_MAX,
++ fmr, ip6_tnl_fmr_policy, NULL);
++
++ if (!(nfmr = kzalloc(sizeof(*nfmr), GFP_KERNEL)))
++ continue;
++
++ nfmr->offset = 6;
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX]))
++ nla_memcpy(&nfmr->ip6_prefix, fmrd[IFLA_IPTUN_FMR_IP6_PREFIX],
++ sizeof(nfmr->ip6_prefix));
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX]))
++ nla_memcpy(&nfmr->ip4_prefix, fmrd[IFLA_IPTUN_FMR_IP4_PREFIX],
++ sizeof(nfmr->ip4_prefix));
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX_LEN]))
++ nfmr->ip6_prefix_len = nla_get_u8(c);
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX_LEN]))
++ nfmr->ip4_prefix_len = nla_get_u8(c);
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_EA_LEN]))
++ nfmr->ea_len = nla_get_u8(c);
++
++ if ((c = fmrd[IFLA_IPTUN_FMR_OFFSET]))
++ nfmr->offset = nla_get_u8(c);
++
++ nfmr->next = parms->fmrs;
++ parms->fmrs = nfmr;
++ }
++ }
+ }
+
+ static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[],
+@@ -2102,6 +2321,12 @@ static void ip6_tnl_dellink(struct net_d
+
+ static size_t ip6_tnl_get_size(const struct net_device *dev)
+ {
++ const struct ip6_tnl *t = netdev_priv(dev);
++ struct __ip6_tnl_fmr *c;
++ int fmrs = 0;
++ for (c = t->parms.fmrs; c; c = c->next)
++ ++fmrs;
++
+ return
+ /* IFLA_IPTUN_LINK */
+ nla_total_size(4) +
+@@ -2131,6 +2356,24 @@ static size_t ip6_tnl_get_size(const str
+ nla_total_size(0) +
+ /* IFLA_IPTUN_FWMARK */
+ nla_total_size(4) +
++ /* IFLA_IPTUN_FMRS */
++ nla_total_size(0) +
++ (
++ /* nest */
++ nla_total_size(0) +
++ /* IFLA_IPTUN_FMR_IP6_PREFIX */
++ nla_total_size(sizeof(struct in6_addr)) +
++ /* IFLA_IPTUN_FMR_IP4_PREFIX */
++ nla_total_size(sizeof(struct in_addr)) +
++ /* IFLA_IPTUN_FMR_EA_LEN */
++ nla_total_size(1) +
++ /* IFLA_IPTUN_FMR_IP6_PREFIX_LEN */
++ nla_total_size(1) +
++ /* IFLA_IPTUN_FMR_IP4_PREFIX_LEN */
++ nla_total_size(1) +
++ /* IFLA_IPTUN_FMR_OFFSET */
++ nla_total_size(1)
++ ) * fmrs +
+ 0;
+ }
+
+@@ -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;
++ struct __ip6_tnl_fmr *c;
++ int fmrcnt = 0;
++ struct nlattr *fmrs;
+
+ if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
+ nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) ||
+@@ -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) ||
+- nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark))
++ nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark) ||
++ !(fmrs = nla_nest_start(skb, IFLA_IPTUN_FMRS)))
+ goto nla_put_failure;
+
++ for (c = parm->fmrs; c; c = c->next) {
++ struct nlattr *fmr = nla_nest_start(skb, ++fmrcnt);
++ if (!fmr ||
++ nla_put(skb, IFLA_IPTUN_FMR_IP6_PREFIX,
++ sizeof(c->ip6_prefix), &c->ip6_prefix) ||
++ nla_put(skb, IFLA_IPTUN_FMR_IP4_PREFIX,
++ sizeof(c->ip4_prefix), &c->ip4_prefix) ||
++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, c->ip6_prefix_len) ||
++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, c->ip4_prefix_len) ||
++ nla_put_u8(skb, IFLA_IPTUN_FMR_EA_LEN, c->ea_len) ||
++ nla_put_u8(skb, IFLA_IPTUN_FMR_OFFSET, c->offset))
++ goto nla_put_failure;
++
++ nla_nest_end(skb, fmr);
++ }
++ nla_nest_end(skb, fmrs);
++
+ 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) ||
+@@ -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 },
++ [IFLA_IPTUN_FMRS] = { .type = NLA_NESTED },
+ };
+
+ static struct rtnl_link_ops ip6_link_ops __read_mostly = {
diff --git a/target/linux/generic/pending-6.1/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/target/linux/generic/pending-6.1/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch
new file mode 100644
index 0000000000..86bd9664e5
--- /dev/null
+++ b/target/linux/generic/pending-6.1/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch
@@ -0,0 +1,263 @@
+From: Jonas Gorski <jogo@openwrt.org>
+Subject: ipv6: allow rejecting with "source address failed policy"
+
+RFC6204 L-14 requires rejecting traffic from invalid addresses with
+ICMPv6 Destination Unreachable, Code 5 (Source address failed ingress/
+egress policy) on the LAN side, so add an appropriate rule for that.
+
+Signed-off-by: Jonas Gorski <jogo@openwrt.org>
+---
+ include/net/netns/ipv6.h | 1 +
+ include/uapi/linux/fib_rules.h | 4 +++
+ include/uapi/linux/rtnetlink.h | 1 +
+ net/ipv4/fib_semantics.c | 4 +++
+ net/ipv4/fib_trie.c | 1 +
+ net/ipv4/ipmr.c | 1 +
+ net/ipv6/fib6_rules.c | 4 +++
+ net/ipv6/ip6mr.c | 2 ++
+ net/ipv6/route.c | 58 +++++++++++++++++++++++++++++++++++++++++-
+ 9 files changed, 75 insertions(+), 1 deletion(-)
+
+--- a/include/net/netns/ipv6.h
++++ b/include/net/netns/ipv6.h
+@@ -85,6 +85,7 @@ struct netns_ipv6 {
+ unsigned int fib6_routes_require_src;
+ #endif
+ struct rt6_info *ip6_prohibit_entry;
++ struct rt6_info *ip6_policy_failed_entry;
+ struct rt6_info *ip6_blk_hole_entry;
+ struct fib6_table *fib6_local_tbl;
+ struct fib_rules_ops *fib6_rules_ops;
+--- a/include/uapi/linux/fib_rules.h
++++ b/include/uapi/linux/fib_rules.h
+@@ -82,6 +82,10 @@ enum {
+ FR_ACT_BLACKHOLE, /* Drop without notification */
+ FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */
+ FR_ACT_PROHIBIT, /* Drop with EACCES */
++ FR_ACT_RES9,
++ FR_ACT_RES10,
++ FR_ACT_RES11,
++ FR_ACT_POLICY_FAILED, /* Drop with EACCES */
+ __FR_ACT_MAX,
+ };
+
+--- a/include/uapi/linux/rtnetlink.h
++++ b/include/uapi/linux/rtnetlink.h
+@@ -256,6 +256,7 @@ enum {
+ RTN_THROW, /* Not in this table */
+ RTN_NAT, /* Translate this address */
+ RTN_XRESOLVE, /* Use external resolver */
++ RTN_POLICY_FAILED, /* Failed ingress/egress policy */
+ __RTN_MAX
+ };
+
+--- a/net/ipv4/fib_semantics.c
++++ b/net/ipv4/fib_semantics.c
+@@ -143,6 +143,10 @@ const struct fib_prop fib_props[RTN_MAX
+ .error = -EINVAL,
+ .scope = RT_SCOPE_NOWHERE,
+ },
++ [RTN_POLICY_FAILED] = {
++ .error = -EACCES,
++ .scope = RT_SCOPE_UNIVERSE,
++ },
+ };
+
+ static void rt_fibinfo_free(struct rtable __rcu **rtp)
+--- a/net/ipv4/fib_trie.c
++++ b/net/ipv4/fib_trie.c
+@@ -2772,6 +2772,7 @@ static const char *const rtn_type_names[
+ [RTN_THROW] = "THROW",
+ [RTN_NAT] = "NAT",
+ [RTN_XRESOLVE] = "XRESOLVE",
++ [RTN_POLICY_FAILED] = "POLICY_FAILED",
+ };
+
+ static inline const char *rtn_type(char *buf, size_t len, unsigned int t)
+--- a/net/ipv4/ipmr.c
++++ b/net/ipv4/ipmr.c
+@@ -175,6 +175,7 @@ static int ipmr_rule_action(struct fib_r
+ case FR_ACT_UNREACHABLE:
+ return -ENETUNREACH;
+ case FR_ACT_PROHIBIT:
++ case FR_ACT_POLICY_FAILED:
+ return -EACCES;
+ case FR_ACT_BLACKHOLE:
+ default:
+--- a/net/ipv6/fib6_rules.c
++++ b/net/ipv6/fib6_rules.c
+@@ -220,6 +220,10 @@ static int __fib6_rule_action(struct fib
+ err = -EACCES;
+ rt = net->ipv6.ip6_prohibit_entry;
+ goto discard_pkt;
++ case FR_ACT_POLICY_FAILED:
++ err = -EACCES;
++ rt = net->ipv6.ip6_policy_failed_entry;
++ goto discard_pkt;
+ }
+
+ tb_id = fib_rule_get_table(rule, arg);
+--- a/net/ipv6/ip6mr.c
++++ b/net/ipv6/ip6mr.c
+@@ -163,6 +163,8 @@ static int ip6mr_rule_action(struct fib_
+ return -ENETUNREACH;
+ case FR_ACT_PROHIBIT:
+ return -EACCES;
++ case FR_ACT_POLICY_FAILED:
++ return -EACCES;
+ case FR_ACT_BLACKHOLE:
+ default:
+ return -EINVAL;
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -97,6 +97,8 @@ static int ip6_pkt_discard(struct sk_bu
+ static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
+ static int ip6_pkt_prohibit(struct sk_buff *skb);
+ static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
++static int ip6_pkt_policy_failed(struct sk_buff *skb);
++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb);
+ static void ip6_link_failure(struct sk_buff *skb);
+ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
+ struct sk_buff *skb, u32 mtu,
+@@ -312,6 +314,18 @@ static const struct rt6_info ip6_prohibi
+ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
+ };
+
++static const struct rt6_info ip6_policy_failed_entry_template = {
++ .dst = {
++ .__refcnt = ATOMIC_INIT(1),
++ .__use = 1,
++ .obsolete = DST_OBSOLETE_FORCE_CHK,
++ .error = -EACCES,
++ .input = ip6_pkt_policy_failed,
++ .output = ip6_pkt_policy_failed_out,
++ },
++ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
++};
++
+ static const struct rt6_info ip6_blk_hole_entry_template = {
+ .dst = {
+ .__refcnt = ATOMIC_INIT(1),
+@@ -1033,6 +1047,7 @@ static const int fib6_prop[RTN_MAX + 1]
+ [RTN_BLACKHOLE] = -EINVAL,
+ [RTN_UNREACHABLE] = -EHOSTUNREACH,
+ [RTN_PROHIBIT] = -EACCES,
++ [RTN_POLICY_FAILED] = -EACCES,
+ [RTN_THROW] = -EAGAIN,
+ [RTN_NAT] = -EINVAL,
+ [RTN_XRESOLVE] = -EINVAL,
+@@ -1068,6 +1083,10 @@ static void ip6_rt_init_dst_reject(struc
+ rt->dst.output = ip6_pkt_prohibit_out;
+ rt->dst.input = ip6_pkt_prohibit;
+ break;
++ case RTN_POLICY_FAILED:
++ rt->dst.output = ip6_pkt_policy_failed_out;
++ rt->dst.input = ip6_pkt_policy_failed;
++ break;
+ case RTN_THROW:
+ case RTN_UNREACHABLE:
+ default:
+@@ -4560,6 +4579,17 @@ static int ip6_pkt_prohibit_out(struct n
+ return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
+ }
+
++static int ip6_pkt_policy_failed(struct sk_buff *skb)
++{
++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_INNOROUTES);
++}
++
++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb)
++{
++ skb->dev = skb_dst(skb)->dev;
++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_OUTNOROUTES);
++}
++
+ /*
+ * Allocate a dst for local (unicast / anycast) address.
+ */
+@@ -5047,7 +5077,8 @@ static int rtm_to_fib6_config(struct sk_
+ if (rtm->rtm_type == RTN_UNREACHABLE ||
+ rtm->rtm_type == RTN_BLACKHOLE ||
+ rtm->rtm_type == RTN_PROHIBIT ||
+- rtm->rtm_type == RTN_THROW)
++ rtm->rtm_type == RTN_THROW ||
++ rtm->rtm_type == RTN_POLICY_FAILED)
+ cfg->fc_flags |= RTF_REJECT;
+
+ if (rtm->rtm_type == RTN_LOCAL)
+@@ -6301,6 +6332,8 @@ static int ip6_route_dev_notify(struct n
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ net->ipv6.ip6_prohibit_entry->dst.dev = dev;
+ net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
++ net->ipv6.ip6_policy_failed_entry->dst.dev = dev;
++ net->ipv6.ip6_policy_failed_entry->rt6i_idev = in6_dev_get(dev);
+ net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
+ net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
+ #endif
+@@ -6312,6 +6345,7 @@ static int ip6_route_dev_notify(struct n
+ in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
++ in6_dev_put_clear(&net->ipv6.ip6_policy_failed_entry->rt6i_idev);
+ in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
+ #endif
+ }
+@@ -6503,6 +6537,8 @@ static int __net_init ip6_route_net_init
+
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ net->ipv6.fib6_has_custom_rules = false;
++
++
+ net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
+ sizeof(*net->ipv6.ip6_prohibit_entry),
+ GFP_KERNEL);
+@@ -6513,11 +6549,21 @@ static int __net_init ip6_route_net_init
+ ip6_template_metrics, true);
+ INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached);
+
++ net->ipv6.ip6_policy_failed_entry =
++ kmemdup(&ip6_policy_failed_entry_template,
++ sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL);
++ if (!net->ipv6.ip6_policy_failed_entry)
++ goto out_ip6_prohibit_entry;
++ net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops;
++ dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst,
++ ip6_template_metrics, true);
++ INIT_LIST_HEAD(&net->ipv6.ip6_policy_failed_entry->rt6i_uncached);
++
+ net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
+ sizeof(*net->ipv6.ip6_blk_hole_entry),
+ GFP_KERNEL);
+ if (!net->ipv6.ip6_blk_hole_entry)
+- goto out_ip6_prohibit_entry;
++ goto out_ip6_policy_failed_entry;
+ net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
+ dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
+ ip6_template_metrics, true);
+@@ -6544,6 +6590,8 @@ out:
+ return ret;
+
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
++out_ip6_policy_failed_entry:
++ kfree(net->ipv6.ip6_policy_failed_entry);
+ out_ip6_prohibit_entry:
+ kfree(net->ipv6.ip6_prohibit_entry);
+ out_ip6_null_entry:
+@@ -6563,6 +6611,7 @@ static void __net_exit ip6_route_net_exi
+ kfree(net->ipv6.ip6_null_entry);
+ #ifdef CONFIG_IPV6_MULTIPLE_TABLES
+ kfree(net->ipv6.ip6_prohibit_entry);
++ kfree(net->ipv6.ip6_policy_failed_entry);
+ kfree(net->ipv6.ip6_blk_hole_entry);
+ #endif
+ dst_entries_destroy(&net->ipv6.ip6_dst_ops);
+@@ -6646,6 +6695,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);
++ init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev;
++ init_net.ipv6.ip6_policy_failed_entry->rt6i_idev =
++ in6_dev_get(init_net.loopback_dev);
+ #endif
+ }
+
diff --git a/target/linux/generic/pending-6.1/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch b/target/linux/generic/pending-6.1/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch
new file mode 100644
index 0000000000..bea43b2bad
--- /dev/null
+++ b/target/linux/generic/pending-6.1/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch
@@ -0,0 +1,50 @@
+From: Jonas Gorski <jogo@openwrt.org>
+Subject: net: provide defines for _POLICY_FAILED until all code is updated
+
+Upstream introduced ICMPV6_POLICY_FAIL for code 5 of destination
+unreachable, conflicting with our name.
+
+Add appropriate defines to allow our code to build with the new
+name until we have updated our local patches for older kernels
+and userspace packages.
+
+Signed-off-by: Jonas Gorski <jogo@openwrt.org>
+---
+ include/uapi/linux/fib_rules.h | 2 ++
+ include/uapi/linux/icmpv6.h | 2 ++
+ include/uapi/linux/rtnetlink.h | 2 ++
+ 3 files changed, 6 insertions(+)
+
+--- a/include/uapi/linux/fib_rules.h
++++ b/include/uapi/linux/fib_rules.h
+@@ -89,6 +89,8 @@ enum {
+ __FR_ACT_MAX,
+ };
+
++#define FR_ACT_FAILED_POLICY FR_ACT_POLICY_FAILED
++
+ #define FR_ACT_MAX (__FR_ACT_MAX - 1)
+
+ #endif
+--- a/include/uapi/linux/icmpv6.h
++++ b/include/uapi/linux/icmpv6.h
+@@ -126,6 +126,8 @@ struct icmp6hdr {
+ #define ICMPV6_POLICY_FAIL 5
+ #define ICMPV6_REJECT_ROUTE 6
+
++#define ICMPV6_FAILED_POLICY ICMPV6_POLICY_FAIL
++
+ /*
+ * Codes for Time Exceeded
+ */
+--- a/include/uapi/linux/rtnetlink.h
++++ b/include/uapi/linux/rtnetlink.h
+@@ -260,6 +260,8 @@ enum {
+ __RTN_MAX
+ };
+
++#define RTN_FAILED_POLICY RTN_POLICY_FAILED
++
+ #define RTN_MAX (__RTN_MAX - 1)
+
+
diff --git a/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
new file mode 100644
index 0000000000..091e65f31e
--- /dev/null
+++ b/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
@@ -0,0 +1,149 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/netdevice.h | 2 ++
+ include/linux/skbuff.h | 3 ++-
+ net/core/dev.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++
+ net/ethernet/eth.c | 18 +++++++++++++++++-
+ 4 files changed, 69 insertions(+), 2 deletions(-)
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -2098,6 +2098,8 @@ struct net_device {
+ struct netdev_hw_addr_list mc;
+ struct netdev_hw_addr_list dev_addrs;
+
++ unsigned char local_addr_mask[MAX_ADDR_LEN];
++
+ #ifdef CONFIG_SYSFS
+ struct kset *queues_kset;
+ #endif
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -893,6 +893,7 @@ struct sk_buff {
+ #ifdef CONFIG_IPV6_NDISC_NODETYPE
+ __u8 ndisc_nodetype:2;
+ #endif
++ __u8 gro_skip:1;
+
+ __u8 ipvs_property:1;
+ __u8 inner_protocol_type:1;
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -6065,6 +6065,9 @@ static enum gro_result dev_gro_receive(s
+ int same_flow;
+ int grow;
+
++ if (skb->gro_skip)
++ goto normal;
++
+ if (netif_elide_gro(skb->dev))
+ goto normal;
+
+@@ -8079,6 +8082,48 @@ static void __netdev_adjacent_dev_unlink
+ &upper_dev->adj_list.lower);
+ }
+
++static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr,
++ struct net_device *dev)
++{
++ int i;
++
++ for (i = 0; i < dev->addr_len; i++)
++ mask[i] |= addr[i] ^ dev->dev_addr[i];
++}
++
++static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev,
++ struct net_device *lower)
++{
++ struct net_device *cur;
++ struct list_head *iter;
++
++ netdev_for_each_upper_dev_rcu(dev, cur, iter) {
++ __netdev_addr_mask(mask, cur->dev_addr, lower);
++ __netdev_upper_mask(mask, cur, lower);
++ }
++}
++
++static void __netdev_update_addr_mask(struct net_device *dev)
++{
++ unsigned char mask[MAX_ADDR_LEN];
++ struct net_device *cur;
++ struct list_head *iter;
++
++ memset(mask, 0, sizeof(mask));
++ __netdev_upper_mask(mask, dev, dev);
++ memcpy(dev->local_addr_mask, mask, dev->addr_len);
++
++ netdev_for_each_lower_dev(dev, cur, iter)
++ __netdev_update_addr_mask(cur);
++}
++
++static void netdev_update_addr_mask(struct net_device *dev)
++{
++ rcu_read_lock();
++ __netdev_update_addr_mask(dev);
++ rcu_read_unlock();
++}
++
+ static int __netdev_upper_dev_link(struct net_device *dev,
+ struct net_device *upper_dev, bool master,
+ void *upper_priv, void *upper_info,
+@@ -8130,6 +8175,7 @@ static int __netdev_upper_dev_link(struc
+ if (ret)
+ return ret;
+
++ netdev_update_addr_mask(dev);
+ ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER,
+ &changeupper_info.info);
+ ret = notifier_to_errno(ret);
+@@ -8226,6 +8272,7 @@ static void __netdev_upper_dev_unlink(st
+
+ __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev);
+
++ netdev_update_addr_mask(dev);
+ call_netdevice_notifiers_info(NETDEV_CHANGEUPPER,
+ &changeupper_info.info);
+
+@@ -9045,6 +9092,7 @@ int dev_set_mac_address(struct net_devic
+ if (err)
+ return err;
+ dev->addr_assign_type = NET_ADDR_SET;
++ netdev_update_addr_mask(dev);
+ call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+ add_device_randomness(dev->dev_addr, dev->addr_len);
+ return 0;
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -142,6 +142,18 @@ u32 eth_get_headlen(const struct net_dev
+ }
+ EXPORT_SYMBOL(eth_get_headlen);
+
++static inline bool
++eth_check_local_mask(const void *addr1, const void *addr2, const void *mask)
++{
++ const u16 *a1 = addr1;
++ const u16 *a2 = addr2;
++ const u16 *m = mask;
++
++ return (((a1[0] ^ a2[0]) & ~m[0]) |
++ ((a1[1] ^ a2[1]) & ~m[1]) |
++ ((a1[2] ^ a2[2]) & ~m[2]));
++}
++
+ /**
+ * eth_type_trans - determine the packet's protocol ID.
+ * @skb: received socket data
+@@ -173,6 +185,10 @@ __be16 eth_type_trans(struct sk_buff *sk
+ } else {
+ skb->pkt_type = PACKET_OTHERHOST;
+ }
++
++ if (eth_check_local_mask(eth->h_dest, dev->dev_addr,
++ dev->local_addr_mask))
++ skb->gro_skip = 1;
+ }
+
+ /*
diff --git a/target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch b/target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch
new file mode 100644
index 0000000000..fe6faddc7d
--- /dev/null
+++ b/target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch
@@ -0,0 +1,89 @@
+From 844c273286f328acf0dab5fbd5d864366b4904dc Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Tue, 30 Mar 2021 18:21:14 +0200
+Subject: [PATCH] of_net: add mac-address-increment support
+
+Lots of embedded devices use the mac-address of other interface
+extracted from nvmem cells and increments it by one or two. Add two
+bindings to integrate this and directly use the right mac-address for
+the interface. Some example are some routers that use the gmac
+mac-address stored in the art partition and increments it by one for the
+wifi. mac-address-increment-byte bindings is used to tell what byte of
+the mac-address has to be increased (if not defined the last byte is
+increased) and mac-address-increment tells how much the byte decided
+early has to be increased.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+---
+ net/core/of_net.c | 43 +++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 39 insertions(+), 4 deletions(-)
+
+--- a/net/core/of_net.c
++++ b/net/core/of_net.c
+@@ -119,27 +119,62 @@ static int of_get_mac_addr_nvmem(struct
+ * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists
+ * but is all zeros.
+ *
++ * DT can tell the system to increment the mac-address after is extracted by
++ * using:
++ * - mac-address-increment-byte to decide what byte to increase
++ * (if not defined is increased the last byte)
++ * - mac-address-increment to decide how much to increase. The value WILL
++ * overflow to other bytes if the increment is over 255 or the total
++ * increment will exceed 255 of the current byte.
++ * (example 00:01:02:03:04:ff + 1 == 00:01:02:03:05:00)
++ * (example 00:01:02:03:04:fe + 5 == 00:01:02:03:05:03)
++ *
+ * Return: 0 on success and errno in case of error.
+ */
+ int of_get_mac_address(struct device_node *np, u8 *addr)
+ {
++ u32 inc_idx, mac_inc, mac_val;
+ int ret;
+
++ /* Check first if the increment byte is present and valid.
++ * If not set assume to increment the last byte if found.
++ */
++ if (of_property_read_u32(np, "mac-address-increment-byte", &inc_idx))
++ inc_idx = 5;
++ if (inc_idx < 3 || inc_idx > 5)
++ return -EINVAL;
++
+ if (!np)
+ return -ENODEV;
+
+ ret = of_get_mac_addr(np, "mac-address", addr);
+ if (!ret)
+- return 0;
++ goto found;
+
+ ret = of_get_mac_addr(np, "local-mac-address", addr);
+ if (!ret)
+- return 0;
++ goto found;
+
+ ret = of_get_mac_addr(np, "address", addr);
+ if (!ret)
+- return 0;
++ goto found;
++
++ ret = of_get_mac_addr_nvmem(np, addr);
++ if (ret)
++ return ret;
++
++found:
++ if (!of_property_read_u32(np, "mac-address-increment", &mac_inc)) {
++ /* Convert to a contiguous value */
++ mac_val = (addr[3] << 16) + (addr[4] << 8) + addr[5];
++ mac_val += mac_inc << 8 * (5-inc_idx);
++
++ /* Apply the incremented value handling overflow case */
++ addr[3] = (mac_val >> 16) & 0xff;
++ addr[4] = (mac_val >> 8) & 0xff;
++ addr[5] = (mac_val >> 0) & 0xff;
++ }
+
+- return of_get_mac_addr_nvmem(np, addr);
++ return ret;
+ }
+ EXPORT_SYMBOL(of_get_mac_address);
diff --git a/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch b/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch
new file mode 100644
index 0000000000..f7ef06a14a
--- /dev/null
+++ b/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch
@@ -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
+Subject: [PATCH] of/of_net: write back netdev MAC-address to device-tree
+
+The label-mac logic relies on the mac-address property of a netdev
+devices of-node. However, the mac address can also be stored as a
+different property or read from e.g. an mtd device.
+
+Create this node when reading a mac-address from OF if it does not
+already exist and copy the mac-address used for the device to this
+property. This way, the MAC address can be accessed using procfs.
+
+---
+ net/core/of_net.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+--- a/net/core/of_net.c
++++ b/net/core/of_net.c
+@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct
+ return 0;
+ }
+
++static int of_add_mac_address(struct device_node *np, u8* addr)
++{
++ struct property *prop;
++
++ prop = kzalloc(sizeof(*prop), GFP_KERNEL);
++ if (!prop)
++ return -ENOMEM;
++
++ prop->name = "mac-address";
++ prop->length = ETH_ALEN;
++ prop->value = kmemdup(addr, ETH_ALEN, GFP_KERNEL);
++ if (!prop->value || of_update_property(np, prop))
++ goto free;
++
++ return 0;
++free:
++ kfree(prop->value);
++ kfree(prop);
++ return -ENOMEM;
++}
++
+ /**
+ * of_get_mac_address()
+ * @np: Caller's Device Node
+@@ -175,6 +196,7 @@ found:
+ addr[5] = (mac_val >> 0) & 0xff;
+ }
+
++ of_add_mac_address(np, addr);
+ return ret;
+ }
+ EXPORT_SYMBOL(of_get_mac_address);
diff --git a/target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch b/target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch
new file mode 100644
index 0000000000..44d88e31a2
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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-6.1/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch b/target/linux/generic/pending-6.1/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch
new file mode 100644
index 0000000000..488c6a8d92
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch b/target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch
new file mode 100644
index 0000000000..a4e3d26503
--- /dev/null
+++ b/target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch
@@ -0,0 +1,41 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 21 Mar 2022 20:39:59 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: enable threaded NAPI
+
+This can improve performance under load by ensuring that NAPI processing is
+not pinned on CPU 0.
+
+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
+@@ -2992,8 +2992,8 @@ static irqreturn_t mtk_handle_irq_rx(int
+
+ eth->rx_events++;
+ if (likely(napi_schedule_prep(&eth->rx_napi))) {
+- __napi_schedule(&eth->rx_napi);
+ mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
++ __napi_schedule(&eth->rx_napi);
+ }
+
+ return IRQ_HANDLED;
+@@ -3005,8 +3005,8 @@ static irqreturn_t mtk_handle_irq_tx(int
+
+ eth->tx_events++;
+ if (likely(napi_schedule_prep(&eth->tx_napi))) {
+- __napi_schedule(&eth->tx_napi);
+ mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
++ __napi_schedule(&eth->tx_napi);
+ }
+
+ return IRQ_HANDLED;
+@@ -4673,6 +4673,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,
+ NAPI_POLL_WEIGHT);
+ netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx,
diff --git a/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch
new file mode 100644
index 0000000000..5baccf73cb
--- /dev/null
+++ b/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch
@@ -0,0 +1,38 @@
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: generic: add detach callback to struct phy_driver
+
+lede-commit: fe61fc2d7d0b3fb348b502f68f98243b3ddf5867
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/net/phy/phy_device.c | 3 +++
+ include/linux/phy.h | 6 ++++++
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1748,6 +1748,9 @@ void phy_detach(struct phy_device *phyde
+ struct module *ndev_owner = NULL;
+ struct mii_bus *bus;
+
++ if (phydev->drv && phydev->drv->detach)
++ phydev->drv->detach(phydev);
++
+ if (phydev->sysfs_links) {
+ if (dev)
+ sysfs_remove_link(&dev->dev.kobj, "phydev");
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -823,6 +823,12 @@ struct phy_driver {
+ /** @handle_interrupt: Override default interrupt handling */
+ irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
+
++ /*
++ * Called before an ethernet device is detached
++ * from the PHY.
++ */
++ void (*detach)(struct phy_device *phydev);
++
+ /** @remove: Clears up any memory if needed */
+ void (*remove)(struct phy_device *phydev);
+
diff --git a/target/linux/generic/pending-6.1/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch b/target/linux/generic/pending-6.1/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch
new file mode 100644
index 0000000000..d444b2027c
--- /dev/null
+++ b/target/linux/generic/pending-6.1/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch
@@ -0,0 +1,28 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 6 May 2022 21:38:42 +0200
+Subject: [PATCH] net: dsa: tag_mtk: add padding for tx packets
+
+Padding for transmitted packets needs to account for the special tag.
+With not enough padding, garbage bytes are inserted by the switch at the
+end of small packets.
+
+Fixes: 5cd8985a1909 ("net-next: dsa: add Mediatek tag RX/TX handler")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/dsa/tag_mtk.c
++++ b/net/dsa/tag_mtk.c
+@@ -27,6 +27,13 @@ static struct sk_buff *mtk_tag_xmit(stru
+
+ skb_set_queue_mapping(skb, dp->index);
+
++ /* The Ethernet switch we are interfaced with needs packets to be at
++ * least 64 bytes (including FCS) otherwise their padding might be
++ * corrupted. With tags enabled, we need to make sure that packets are
++ * at least 68 bytes (including FCS and tag).
++ */
++ eth_skb_pad(skb);
++
+ /* 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-6.1/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch b/target/linux/generic/pending-6.1/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch
new file mode 100644
index 0000000000..2a2ca7f1fa
--- /dev/null
+++ b/target/linux/generic/pending-6.1/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch
@@ -0,0 +1,174 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 27 Aug 2021 12:22:32 +0200
+Subject: [PATCH] bridge: add knob for filtering rx/tx BPDU packets on a port
+
+Some devices (e.g. wireless APs) can't have devices behind them be part of
+a bridge topology with redundant links, due to address limitations.
+Additionally, broadcast traffic on these devices is somewhat expensive, due to
+the low data rate and wakeups of clients in powersave mode.
+This knob can be used to ensure that BPDU packets are never sent or forwarded
+to/from these devices
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/linux/if_bridge.h
++++ b/include/linux/if_bridge.h
+@@ -58,6 +58,7 @@ struct br_ip_list {
+ #define BR_MRP_LOST_CONT BIT(18)
+ #define BR_MRP_LOST_IN_CONT BIT(19)
+ #define BR_TX_FWD_OFFLOAD BIT(20)
++#define BR_BPDU_FILTER BIT(21)
+
+ #define BR_DEFAULT_AGEING_TIME (300 * HZ)
+
+--- a/net/bridge/br_forward.c
++++ b/net/bridge/br_forward.c
+@@ -199,6 +199,7 @@ out:
+ void br_flood(struct net_bridge *br, struct sk_buff *skb,
+ enum br_pkt_type pkt_type, bool local_rcv, bool local_orig)
+ {
++ const unsigned char *dest = eth_hdr(skb)->h_dest;
+ struct net_bridge_port *prev = NULL;
+ struct net_bridge_port *p;
+
+@@ -214,6 +215,10 @@ void br_flood(struct net_bridge *br, str
+ case BR_PKT_MULTICAST:
+ if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
+ continue;
++ if ((p->flags & BR_BPDU_FILTER) &&
++ unlikely(is_link_local_ether_addr(dest) &&
++ dest[5] == 0))
++ continue;
+ break;
+ case BR_PKT_BROADCAST:
+ if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev)
+--- a/net/bridge/br_input.c
++++ b/net/bridge/br_input.c
+@@ -326,6 +326,8 @@ static rx_handler_result_t br_handle_fra
+ fwd_mask |= p->group_fwd_mask;
+ switch (dest[5]) {
+ case 0x00: /* Bridge Group Address */
++ if (p->flags & BR_BPDU_FILTER)
++ goto drop;
+ /* If STP is turned off,
+ then must forward to keep loop detection */
+ if (p->br->stp_enabled == BR_NO_STP ||
+--- a/net/bridge/br_sysfs_if.c
++++ b/net/bridge/br_sysfs_if.c
+@@ -240,6 +240,7 @@ BRPORT_ATTR_FLAG(multicast_flood, BR_MCA
+ BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD);
+ BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
+ BRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
++BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER);
+
+ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
+@@ -292,6 +293,7 @@ static const struct brport_attribute *br
+ &brport_attr_group_fwd_mask,
+ &brport_attr_neigh_suppress,
+ &brport_attr_isolated,
++ &brport_attr_bpdu_filter,
+ &brport_attr_backup_port,
+ NULL
+ };
+--- a/net/bridge/br_stp_bpdu.c
++++ b/net/bridge/br_stp_bpdu.c
+@@ -80,7 +80,8 @@ void br_send_config_bpdu(struct net_brid
+ {
+ unsigned char buf[35];
+
+- if (p->br->stp_enabled != BR_KERNEL_STP)
++ if (p->br->stp_enabled != BR_KERNEL_STP ||
++ (p->flags & BR_BPDU_FILTER))
+ return;
+
+ buf[0] = 0;
+@@ -127,7 +128,8 @@ void br_send_tcn_bpdu(struct net_bridge_
+ {
+ unsigned char buf[4];
+
+- if (p->br->stp_enabled != BR_KERNEL_STP)
++ if (p->br->stp_enabled != BR_KERNEL_STP ||
++ (p->flags & BR_BPDU_FILTER))
+ return;
+
+ buf[0] = 0;
+@@ -172,6 +174,9 @@ void br_stp_rcv(const struct stp_proto *
+ if (!(br->dev->flags & IFF_UP))
+ goto out;
+
++ if (p->flags & BR_BPDU_FILTER)
++ goto out;
++
+ if (p->state == BR_STATE_DISABLED)
+ goto out;
+
+--- a/include/uapi/linux/if_link.h
++++ b/include/uapi/linux/if_link.h
+@@ -536,6 +536,7 @@ enum {
+ IFLA_BRPORT_MRP_IN_OPEN,
+ IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
+ IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
++ IFLA_BRPORT_BPDU_FILTER,
+ __IFLA_BRPORT_MAX
+ };
+ #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
+--- a/net/bridge/br_netlink.c
++++ b/net/bridge/br_netlink.c
+@@ -184,6 +184,7 @@ static inline size_t br_port_info_size(v
+ + nla_total_size(1) /* IFLA_BRPORT_VLAN_TUNNEL */
+ + nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */
+ + nla_total_size(1) /* IFLA_BRPORT_ISOLATED */
++ + nla_total_size(1) /* IFLA_BRPORT_BPDU_FILTER */
+ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */
+ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */
+ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */
+@@ -269,7 +270,8 @@ static int br_port_fill_attrs(struct sk_
+ BR_MRP_LOST_CONT)) ||
+ nla_put_u8(skb, IFLA_BRPORT_MRP_IN_OPEN,
+ !!(p->flags & BR_MRP_LOST_IN_CONT)) ||
+- nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)))
++ nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)) ||
++ nla_put_u8(skb, IFLA_BRPORT_BPDU_FILTER, !!(p->flags & BR_BPDU_FILTER)))
+ return -EMSGSIZE;
+
+ timerval = br_timer_value(&p->message_age_timer);
+@@ -829,6 +831,7 @@ static const struct nla_policy br_port_p
+ [IFLA_BRPORT_ISOLATED] = { .type = NLA_U8 },
+ [IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 },
+ [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 },
++ [IFLA_BRPORT_BPDU_FILTER] = { .type = NLA_U8 },
+ };
+
+ /* Change the state of the port and notify spanning tree */
+@@ -893,6 +896,7 @@ static int br_setport(struct net_bridge_
+ br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL);
+ br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, BR_NEIGH_SUPPRESS);
+ br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED);
++ br_set_port_flag(p, tb, IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER);
+
+ changed_mask = old_flags ^ p->flags;
+
+--- a/net/core/rtnetlink.c
++++ b/net/core/rtnetlink.c
+@@ -55,7 +55,7 @@
+ #include <net/net_namespace.h>
+
+ #define RTNL_MAX_TYPE 50
+-#define RTNL_SLAVE_MAX_TYPE 40
++#define RTNL_SLAVE_MAX_TYPE 41
+
+ struct rtnl_link {
+ rtnl_doit_func doit;
+@@ -4700,7 +4700,9 @@ int ndo_dflt_bridge_getlink(struct sk_bu
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD) ||
+ brport_nla_put_flag(skb, flags, mask,
+- IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD)) {
++ IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD) ||
++ brport_nla_put_flag(skb, flags, mask,
++ IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER)) {
+ nla_nest_cancel(skb, protinfo);
+ goto nla_put_failure;
+ }
diff --git a/target/linux/generic/pending-6.1/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch b/target/linux/generic/pending-6.1/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch
new file mode 100644
index 0000000000..c93fe42273
--- /dev/null
+++ b/target/linux/generic/pending-6.1/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch
@@ -0,0 +1,106 @@
+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,48 @@ 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_2500BASEX:
++ if (!phydev->is_c45) {
++ option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX;
++ break;
++ }
++ fallthrough;
++ case PHY_INTERFACE_MODE_SGMII:
++ 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 +1032,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 +1044,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-6.1/722-net-phy-realtek-support-switching-between-SGMII-and-.patch b/target/linux/generic/pending-6.1/722-net-phy-realtek-support-switching-between-SGMII-and-.patch
new file mode 100644
index 0000000000..211a8ed297
--- /dev/null
+++ b/target/linux/generic/pending-6.1/722-net-phy-realtek-support-switching-between-SGMII-and-.patch
@@ -0,0 +1,61 @@
+From 312753d0aadba0f58841ae513b80fdbabc887523 Mon Sep 17 00:00:00 2001
+From: Chukun Pan <amadeus@jmu.edu.cn>
+Date: Wed, 8 Feb 2023 16:32:18 +0800
+Subject: [PATCH] net: phy: realtek: support switching between SGMII and
+ 2500BASE-X for RTL822x series
+
+After commit ace6aba ("net: phy: realtek: rtl8221: allow to configure
+SERDES mode"), the rtl8221 phy can work in SGMII and 2500base-x modes
+respectively. So add interface automatic switching for rtl8221 phy to
+match various wire speeds.
+
+Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
+---
+ drivers/net/phy/realtek.c | 26 ++++++++++++++++++++++++--
+ 1 file changed, 24 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -676,6 +676,25 @@ static int rtl822x_config_aneg(struct ph
+ return __genphy_config_aneg(phydev, ret);
+ }
+
++static void rtl822x_update_interface(struct phy_device *phydev)
++{
++ /* Automatically switch SERDES interface between
++ * SGMII and 2500-BaseX according to speed.
++ */
++ switch (phydev->speed) {
++ case SPEED_2500:
++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
++ break;
++ case SPEED_1000:
++ case SPEED_100:
++ case SPEED_10:
++ phydev->interface = PHY_INTERFACE_MODE_SGMII;
++ break;
++ default:
++ break;
++ }
++}
++
+ static int rtl822x_read_status(struct phy_device *phydev)
+ {
+ int ret;
+@@ -694,11 +713,14 @@ static int rtl822x_read_status(struct ph
+ phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL);
+ }
+
+- ret = genphy_read_status(phydev);
++ ret = rtlgen_read_status(phydev);
+ if (ret < 0)
+ return ret;
+
+- return rtlgen_get_speed(phydev);
++ if (phydev->is_c45 && phydev->link)
++ rtl822x_update_interface(phydev);
++
++ return 0;
+ }
+
+ static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
diff --git a/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch b/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch
new file mode 100644
index 0000000000..d93ee0685b
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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
+
+The datasheet [1] explicit describes it as requirement for a reset.
+
+[1] MT7531 Reference Manual for Development Board rev 1.0, page 735
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+---
+ drivers/net/dsa/mt7530.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2347,6 +2347,10 @@ mt7531_setup(struct dsa_switch *ds)
+ return -ENODEV;
+ }
+
++ /* 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-6.1/724-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.1/724-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
new file mode 100644
index 0000000000..6941818351
--- /dev/null
+++ b/target/linux/generic/pending-6.1/724-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
@@ -0,0 +1,65 @@
+From 85cd45580f5e3b26068cccb7d6173f200e754dc0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 2 Apr 2023 23:56:16 +0100
+Subject: [PATCH 1/2] net: phy: realtek: use genphy_soft_reset for 2.5G PHYs
+
+Some vendor bootloaders do weird things with those PHYs which result in
+link modes being reported wrongly. Start from a clean sheet by resetting
+the PHY.
+
+Reported-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1018,6 +1018,7 @@ static struct phy_driver realtek_drvs[]
+ .write_page = rtl821x_write_page,
+ .read_mmd = rtl822x_read_mmd,
+ .write_mmd = rtl822x_write_mmd,
++ .soft_reset = genphy_soft_reset,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc840),
+ .name = "RTL8226B_RTL8221B 2.5Gbps PHY",
+@@ -1030,6 +1031,7 @@ static struct phy_driver realtek_drvs[]
+ .write_page = rtl821x_write_page,
+ .read_mmd = rtl822x_read_mmd,
+ .write_mmd = rtl822x_write_mmd,
++ .soft_reset = genphy_soft_reset,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc838),
+ .name = "RTL8226-CG 2.5Gbps PHY",
+@@ -1040,6 +1042,7 @@ static struct phy_driver realtek_drvs[]
+ .resume = rtlgen_resume,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
++ .soft_reset = genphy_soft_reset,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc848),
+ .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
+@@ -1050,6 +1053,7 @@ static struct phy_driver realtek_drvs[]
+ .resume = rtlgen_resume,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
++ .soft_reset = genphy_soft_reset,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc849),
+ .name = "RTL8221B-VB-CG 2.5Gbps PHY",
+@@ -1061,6 +1065,7 @@ static struct phy_driver realtek_drvs[]
+ .resume = rtlgen_resume,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
++ .soft_reset = genphy_soft_reset,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc84a),
+ .name = "RTL8221B-VM-CG 2.5Gbps PHY",
+@@ -1072,6 +1077,7 @@ static struct phy_driver realtek_drvs[]
+ .resume = rtlgen_resume,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
++ .soft_reset = genphy_soft_reset,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc961),
+ .name = "RTL8366RB Gigabit Ethernet",
diff --git a/target/linux/generic/pending-6.1/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch b/target/linux/generic/pending-6.1/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch
new file mode 100644
index 0000000000..1370c6324b
--- /dev/null
+++ b/target/linux/generic/pending-6.1/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch
@@ -0,0 +1,43 @@
+From 2b1b8c4c215af7988136401c902338d091d408a1 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 3 Apr 2023 01:21:57 +0300
+Subject: [PATCH 2/2] net: phy: realtek: disable SGMII in-band AN for 2.5G PHYs
+
+MAC drivers don't use SGMII in-band autonegotiation unless told to do so
+in device tree using 'managed = "in-band-status"'. When using MDIO to
+access a PHY, in-band-status is unneeded as we have link-status via
+MDIO. Switch off SGMII in-band autonegotiation using magic values.
+
+Reported-by: Chen Minqiang <ptpt52@gmail.com>
+Reported-by: Chukun Pan <amadeus@jmu.edu.cn>
+Reported-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
+Tested-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -875,6 +875,7 @@ static irqreturn_t rtl9000a_handle_inter
+ static int rtl8221b_config_init(struct phy_device *phydev)
+ {
+ u16 option_mode;
++ int val;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_2500BASEX:
+@@ -911,6 +912,13 @@ static int rtl8221b_config_init(struct p
+ break;
+ }
+
++ /* Disable SGMII AN */
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7588, 0x2);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7589, 0x71d0);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7587, 0x3);
++ phy_read_mmd_poll_timeout(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7587,
++ val, !(val & BIT(0)), 500, 100000, false);
++
+ return 0;
+ }
+
diff --git a/target/linux/generic/pending-6.1/726-net-phy-realtek-make-sure-paged-read-is-protected-by.patch b/target/linux/generic/pending-6.1/726-net-phy-realtek-make-sure-paged-read-is-protected-by.patch
new file mode 100644
index 0000000000..31f0622327
--- /dev/null
+++ b/target/linux/generic/pending-6.1/726-net-phy-realtek-make-sure-paged-read-is-protected-by.patch
@@ -0,0 +1,35 @@
+From 4dd2cc9b91ecb25f278a2c55e07e6455e9000e6b Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 22 Apr 2023 01:21:14 +0100
+Subject: [PATCH] net: phy: realtek: make sure paged read is protected by mutex
+
+As we cannot rely on phy_read_paged function before the PHY is
+identified, the paged read in rtlgen_supports_2_5gbps needs to be open
+coded as it is being called by the match_phy_device function, ie. before
+.read_page and .write_page have been populated.
+
+Make sure it is also protected by the MDIO bus mutex and use
+rtl821x_write_page instead of 3 individually locked MDIO bus operations.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -727,9 +727,11 @@ static bool rtlgen_supports_2_5gbps(stru
+ {
+ int val;
+
+- phy_write(phydev, RTL821x_PAGE_SELECT, 0xa61);
+- val = phy_read(phydev, 0x13);
+- phy_write(phydev, RTL821x_PAGE_SELECT, 0);
++ mutex_lock(&phydev->mdio.bus->mdio_lock);
++ rtl821x_write_page(phydev, 0xa61);
++ val = __phy_read(phydev, 0x13);
++ rtl821x_write_page(phydev, 0);
++ mutex_unlock(&phydev->mdio.bus->mdio_lock);
+
+ return val >= 0 && val & RTL_SUPPORTS_2500FULL;
+ }
diff --git a/target/linux/generic/pending-6.1/727-net-phy-realtek-use-inline-functions-for-10GbE-adver.patch b/target/linux/generic/pending-6.1/727-net-phy-realtek-use-inline-functions-for-10GbE-adver.patch
new file mode 100644
index 0000000000..a24b4dd79a
--- /dev/null
+++ b/target/linux/generic/pending-6.1/727-net-phy-realtek-use-inline-functions-for-10GbE-adver.patch
@@ -0,0 +1,60 @@
+From 92c8b9d558160d94b981dd8a2b9c47657627ffdc Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 22 Apr 2023 01:23:08 +0100
+Subject: [PATCH 2/3] net: phy: realtek: use inline functions for 10GbE
+ advertisement
+
+Use existing generic inline functions to encode local advertisement
+of 10GbE link modes as well as to decode link-partner advertisement.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 22 +++++-----------------
+ 1 file changed, 5 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -68,10 +68,6 @@
+ #define RTL_SUPPORTS_5000FULL BIT(14)
+ #define RTL_SUPPORTS_2500FULL BIT(13)
+ #define RTL_SUPPORTS_10000FULL BIT(0)
+-#define RTL_ADV_2500FULL BIT(7)
+-#define RTL_LPADV_10000FULL BIT(11)
+-#define RTL_LPADV_5000FULL BIT(6)
+-#define RTL_LPADV_2500FULL BIT(5)
+
+ #define RTL9000A_GINMR 0x14
+ #define RTL9000A_GINMR_LINK_STATUS BIT(4)
+@@ -661,14 +657,11 @@ static int rtl822x_config_aneg(struct ph
+ int ret = 0;
+
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+- u16 adv2500 = 0;
+-
+- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+- phydev->advertising))
+- adv2500 = RTL_ADV_2500FULL;
+-
+ ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
+- RTL_ADV_2500FULL, adv2500);
++ MDIO_AN_10GBT_CTRL_ADV10G |
++ MDIO_AN_10GBT_CTRL_ADV5G |
++ MDIO_AN_10GBT_CTRL_ADV2_5G,
++ linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising));
+ if (ret < 0)
+ return ret;
+ }
+@@ -705,12 +698,7 @@ static int rtl822x_read_status(struct ph
+ if (lpadv < 0)
+ return lpadv;
+
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+- phydev->lp_advertising, lpadv & RTL_LPADV_10000FULL);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+- phydev->lp_advertising, lpadv & RTL_LPADV_5000FULL);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+- phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL);
++ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv);
+ }
+
+ ret = rtlgen_read_status(phydev);
diff --git a/target/linux/generic/pending-6.1/728-net-phy-realtek-check-validity-of-10GbE-link-partner.patch b/target/linux/generic/pending-6.1/728-net-phy-realtek-check-validity-of-10GbE-link-partner.patch
new file mode 100644
index 0000000000..084ee4b645
--- /dev/null
+++ b/target/linux/generic/pending-6.1/728-net-phy-realtek-check-validity-of-10GbE-link-partner.patch
@@ -0,0 +1,28 @@
+From 929bb4d3cfbc7878326c0771a01a636d49c54b40 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 22 Apr 2023 01:25:39 +0100
+Subject: [PATCH 3/3] net: phy: realtek: check validity of 10GbE link-partner
+ advertisement
+
+Only use link-partner advertisement bits for 10GbE modes if they are
+actually valid. Check LOCALOK and REMOTEOK bits and clear 10GbE modes
+unless both of them are set.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -698,6 +698,10 @@ static int rtl822x_read_status(struct ph
+ if (lpadv < 0)
+ return lpadv;
+
++ if (!(lpadv & MDIO_AN_10GBT_STAT_REMOK) ||
++ !(lpadv & MDIO_AN_10GBT_STAT_LOCOK))
++ lpadv = 0;
++
+ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv);
+ }
+
diff --git a/target/linux/generic/pending-6.1/729-net-phy-realtek-introduce-rtl822x_probe.patch b/target/linux/generic/pending-6.1/729-net-phy-realtek-introduce-rtl822x_probe.patch
new file mode 100644
index 0000000000..87102d3818
--- /dev/null
+++ b/target/linux/generic/pending-6.1/729-net-phy-realtek-introduce-rtl822x_probe.patch
@@ -0,0 +1,100 @@
+From 9155098547fb1172d4fa536f3f6bc9d42f59d08c Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 22 Apr 2023 03:26:01 +0100
+Subject: [PATCH] net: phy: realtek: setup ALDPS on RTL822x
+
+Setup Link Down Power Saving Mode according the DTS property
+just like for RTL821x 1GE PHYs.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -62,6 +62,10 @@
+ #define RTL8221B_SERDES_OPTION_MODE_2500BASEX 2
+ #define RTL8221B_SERDES_OPTION_MODE_HISGMII 3
+
++#define RTL8221B_PHYCR1 0xa430
++#define RTL8221B_PHYCR1_ALDPS_EN BIT(2)
++#define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12)
++
+ #define RTL8366RB_POWER_SAVE 0x15
+ #define RTL8366RB_POWER_SAVE_ON BIT(12)
+
+@@ -740,6 +744,25 @@ static int rtl8226_match_phy_device(stru
+ rtlgen_supports_2_5gbps(phydev);
+ }
+
++static int rtl822x_probe(struct phy_device *phydev)
++{
++ struct device *dev = &phydev->mdio.dev;
++ int val;
++
++ val = phy_read_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_PHYCR1);
++ if (val < 0)
++ return val;
++
++ if (of_property_read_bool(dev->of_node, "realtek,aldps-enable"))
++ val |= RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN;
++ else
++ val &= ~(RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN);
++
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_PHYCR1, val);
++
++ return 0;
++}
++
+ static int rtlgen_resume(struct phy_device *phydev)
+ {
+ int ret = genphy_resume(phydev);
+@@ -1013,6 +1036,7 @@ static struct phy_driver realtek_drvs[]
+ .match_phy_device = rtl8226_match_phy_device,
+ .get_features = rtl822x_get_features,
+ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
+ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+ .resume = rtlgen_resume,
+@@ -1026,6 +1050,7 @@ static struct phy_driver realtek_drvs[]
+ .name = "RTL8226B_RTL8221B 2.5Gbps PHY",
+ .get_features = rtl822x_get_features,
+ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
+ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+ .resume = rtlgen_resume,
+@@ -1039,6 +1064,7 @@ static struct phy_driver realtek_drvs[]
+ .name = "RTL8226-CG 2.5Gbps PHY",
+ .get_features = rtl822x_get_features,
+ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
+ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+ .resume = rtlgen_resume,
+@@ -1050,6 +1076,7 @@ static struct phy_driver realtek_drvs[]
+ .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
+ .get_features = rtl822x_get_features,
+ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
+ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+ .resume = rtlgen_resume,
+@@ -1062,6 +1089,7 @@ static struct phy_driver realtek_drvs[]
+ .get_features = rtl822x_get_features,
+ .config_init = rtl8221b_config_init,
+ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
+ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+ .resume = rtlgen_resume,
+@@ -1074,6 +1102,7 @@ static struct phy_driver realtek_drvs[]
+ .get_features = rtl822x_get_features,
+ .config_aneg = rtl822x_config_aneg,
+ .config_init = rtl8221b_config_init,
++ .probe = rtl822x_probe,
+ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+ .resume = rtlgen_resume,
diff --git a/target/linux/generic/pending-6.1/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.1/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch
new file mode 100644
index 0000000000..6d414bfbad
--- /dev/null
+++ b/target/linux/generic/pending-6.1/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch
@@ -0,0 +1,63 @@
+From 0de82310d2b32e78ff79d42c08b1122a6ede3778 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 30 Apr 2023 00:15:41 +0100
+Subject: [PATCH] net: phy: realtek: detect early version of RTL8221B
+
+Early versions (?) of the RTL8221B PHY cannot be identified in a regular
+Clause-45 bus scan as the PHY doesn't report the implemented MMDs
+correctly but returns 0 instead.
+Implement custom identify function using the PKGID instead of iterating
+over the implemented MMDs.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -744,6 +744,38 @@ static int rtl8226_match_phy_device(stru
+ rtlgen_supports_2_5gbps(phydev);
+ }
+
++static int rtl8221b_vb_cg_match_phy_device(struct phy_device *phydev)
++{
++ int val;
++ u32 id;
++
++ if (phydev->mdio.bus->probe_capabilities >= MDIOBUS_C45) {
++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID1);
++ if (val < 0)
++ return 0;
++
++ id = val << 16;
++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID2);
++ if (val < 0)
++ return 0;
++
++ id |= val;
++ } else {
++ val = phy_read(phydev, MII_PHYSID1);
++ if (val < 0)
++ return 0;
++
++ id = val << 16;
++ val = phy_read(phydev, MII_PHYSID2);
++ if (val < 0)
++ return 0;
++
++ id |= val;
++ }
++
++ return (id == 0x001cc849);
++}
++
+ static int rtl822x_probe(struct phy_device *phydev)
+ {
+ struct device *dev = &phydev->mdio.dev;
+@@ -1084,7 +1116,7 @@ static struct phy_driver realtek_drvs[]
+ .write_page = rtl821x_write_page,
+ .soft_reset = genphy_soft_reset,
+ }, {
+- PHY_ID_MATCH_EXACT(0x001cc849),
++ .match_phy_device = rtl8221b_vb_cg_match_phy_device,
+ .name = "RTL8221B-VB-CG 2.5Gbps PHY",
+ .get_features = rtl822x_get_features,
+ .config_init = rtl8221b_config_init,
diff --git a/target/linux/generic/pending-6.1/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch b/target/linux/generic/pending-6.1/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch
new file mode 100644
index 0000000000..60c32b6705
--- /dev/null
+++ b/target/linux/generic/pending-6.1/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch
@@ -0,0 +1,428 @@
+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
+@@ -4642,8 +4642,8 @@ static int mtk_probe(struct platform_dev
+ for (i = 0; i < num_ppe; i++) {
+ 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->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, i);
++
+ if (!eth->ppe[i]) {
+ err = -ENOMEM;
+ goto err_free_dev;
+@@ -4770,6 +4770,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),
+@@ -4807,6 +4808,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),
+@@ -4827,6 +4829,7 @@ static const struct mtk_soc_data mt7981_
+ .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),
+@@ -4847,6 +4850,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
+@@ -1014,6 +1014,8 @@ struct mtk_reg_map {
+ * the extra setup for those pins used by GMAC.
+ * @hash_offset Flow table hash offset.
+ * @foe_entry_size Foe table entry size.
++ * @has_accounting Bool indicating support for accounting of
++ * offloaded flows.
+ * @txd_size Tx DMA descriptor size.
+ * @rxd_size Rx DMA descriptor size.
+ * @rx_irq_done_mask Rx irq done register mask.
+@@ -1031,6 +1033,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,48 @@ 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;
++
++ 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);
++
++ if (ret)
++ dev_err(ppe->dev, "MIB table busy");
++
++ return ret;
++}
++
++static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
++{
++ u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
++ u32 val, cnt_r0, cnt_r1, cnt_r2;
++ int ret;
++
++ 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);
++
++ ret = mtk_ppe_mib_wait_busy(ppe);
++ if (ret)
++ return ret;
++
++ 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);
++
++ 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;
++
++ return 0;
++}
++
+ static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
+ {
+ ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
+@@ -464,6 +506,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;
+
+@@ -571,6 +620,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);
+@@ -762,11 +814,39 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
+ return mtk_ppe_wait_busy(ppe);
+ }
+
+-struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+- int version, int index)
++struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
++ struct mtk_foe_accounting *diff)
++{
++ struct mtk_foe_accounting *acct;
++ int size = sizeof(struct mtk_foe_accounting);
++ u64 bytes, packets;
++
++ if (!ppe->accounting)
++ return NULL;
++
++ 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;
++}
++
++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
+ {
++ bool accounting = eth->soc->has_accounting;
+ const struct mtk_soc_data *soc = eth->soc;
++ struct mtk_foe_accounting *acct;
+ struct device *dev = eth->dev;
++ struct mtk_mib_entry *mib;
+ struct mtk_ppe *ppe;
+ u32 foe_flow_size;
+ void *foe;
+@@ -783,7 +863,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+ ppe->base = base;
+ ppe->eth = eth;
+ ppe->dev = dev;
+- ppe->version = version;
++ ppe->version = eth->soc->offload_version;
++ ppe->accounting = accounting;
+
+ foe = dmam_alloc_coherent(ppe->dev,
+ MTK_PPE_ENTRIES * soc->foe_entry_size,
+@@ -799,6 +880,23 @@ 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;
++
++ ppe->mib_table = mib;
++
++ acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct),
++ GFP_KERNEL);
++
++ if (!acct)
++ return NULL;
++
++ ppe->acct_table = acct;
++ }
++
+ mtk_ppe_debugfs_init(ppe, index);
+
+ return ppe;
+@@ -913,6 +1011,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);
+ }
++
++ 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)
+
+@@ -285,16 +286,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;
++};
++
+ 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;
++
+ u16 foe_check_time[MTK_PPE_ENTRIES];
+ struct hlist_head *foe_flow;
+
+@@ -303,8 +322,7 @@ struct mtk_ppe {
+ void *acct_table;
+ };
+
+-struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+- int version, int index);
++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index);
+ void mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
+ int mtk_ppe_prepare_reset(struct mtk_ppe *ppe);
+@@ -358,5 +376,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=%llu bytes=%llu\n",
+ h_source, h_dest, ntohs(l2->etype),
+- l2->vlan1, l2->vlan2, entry->ib1, ib2);
++ l2->vlan1, l2->vlan2, entry->ib1, ib2,
++ acct ? acct->packets : 0, acct ? acct->bytes : 0);
+ }
+
+ return 0;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -497,6 +497,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,
+@@ -507,6 +508,13 @@ 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;
++ }
++
+ return 0;
+ }
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+@@ -149,6 +149,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)
++
++#define MTK_PPE_MIB_SER_R0 0x340
++#define MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW GENMASK(31, 0)
++
++#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)
++
++#define MTK_PPE_MIB_SER_R2 0x348
++#define MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH GENMASK(23, 0)
++
+ #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-6.1/732-00-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch b/target/linux/generic/pending-6.1/732-00-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch
new file mode 100644
index 0000000000..35b296a255
--- /dev/null
+++ b/target/linux/generic/pending-6.1/732-00-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch
@@ -0,0 +1,201 @@
+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
+@@ -1850,9 +1850,7 @@ static int mtk_poll_rx(struct napi_struc
+
+ while (done < budget) {
+ unsigned int pktlen, *rxdcsum;
+- bool has_hwaccel_tag = false;
+ struct net_device *netdev;
+- u16 vlan_proto, vlan_tci;
+ dma_addr_t dma_addr;
+ u32 hash, reason;
+ int mac = 0;
+@@ -1987,36 +1985,21 @@ static int mtk_poll_rx(struct napi_struc
+ skb_checksum_none_assert(skb);
+ skb->protocol = eth_type_trans(skb, netdev);
+
+- 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_proto = RX_DMA_VPID(trxd.rxd4);
+- vlan_tci = RX_DMA_VID(trxd.rxd4);
+- has_hwaccel_tag = true;
+- }
+- } else if (trxd.rxd2 & RX_DMA_VTAG) {
+- vlan_proto = RX_DMA_VPID(trxd.rxd3);
+- vlan_tci = RX_DMA_VID(trxd.rxd3);
+- has_hwaccel_tag = true;
+- }
+- }
+-
+ /* When using VLAN untagging in combination with DSA, the
+ * hardware treats the MTK special tag as a VLAN and untags it.
+ */
+- if (has_hwaccel_tag && netdev_uses_dsa(netdev)) {
+- unsigned int port = 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);
+- } else if (has_hwaccel_tag) {
+- __vlan_hwaccel_put_tag(skb, htons(vlan_proto), vlan_tci);
+ }
+
++ if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
++ mtk_ppe_check_skb(eth->ppe[0], skb, hash);
++
+ skb_record_rx_queue(skb, 0);
+ napi_gro_receive(napi, skb);
+
+@@ -2831,29 +2814,11 @@ static netdev_features_t mtk_fix_feature
+
+ static int mtk_set_features(struct net_device *dev, netdev_features_t features)
+ {
+- 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);
+
+- /* Set RX VLAN offloading */
+- if (!(diff & NETIF_F_HW_VLAN_CTAG_RX))
+- return 0;
+-
+- 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;
+ }
+
+@@ -3167,30 +3132,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__,
+@@ -3231,6 +3172,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;
++
++ 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);
++
++ mtk_w32(eth, 0, MTK_CDMP_EG_CTRL);
++ }
++
+ return 0;
+ }
+
+@@ -3715,10 +3685,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);
+@@ -4365,7 +4334,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_TSO | \
+ NETIF_F_TSO6 | \
+ NETIF_F_IPV6_CSUM |\
diff --git a/target/linux/generic/pending-6.1/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch b/target/linux/generic/pending-6.1/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch
new file mode 100644
index 0000000000..fbf0cb5735
--- /dev/null
+++ b/target/linux/generic/pending-6.1/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch
@@ -0,0 +1,94 @@
+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
+
+When lots of frames are sent with a number of very small fragments, an
+internal FIFO can overflow, causing the DMA engine to lock up lock up and
+transmit attempts time out.
+
+Fix this on MT7986 by increasing the reserved FIFO space.
+Fix this on older chips 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>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1423,12 +1423,28 @@ static void mtk_wake_queue(struct mtk_et
+ }
+ }
+
++static bool mtk_skb_has_small_frag(struct sk_buff *skb)
++{
++ int min_size = 16;
++ int i;
++
++ if (skb_headlen(skb) < min_size)
++ return true;
++
++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
++ if (skb_frag_size(&skb_shinfo(skb)->frags[i]) < min_size)
++ return true;
++
++ return false;
++}
++
+ 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;
+
+@@ -1450,6 +1466,18 @@ static netdev_tx_t mtk_start_xmit(struct
+ return NETDEV_TX_BUSY;
+ }
+
++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) &&
++ 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;
++
++ if (segs) {
++ consume_skb(skb);
++ skb = segs;
++ }
++ }
++
+ /* TSO: fill MSS info in tcp checksum field */
+ if (skb_is_gso(skb)) {
+ if (skb_cow_head(skb, 0)) {
+@@ -1465,8 +1493,14 @@ static netdev_tx_t mtk_start_xmit(struct
+ }
+ }
+
+- if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
+- goto drop;
++ skb_list_walk_safe(skb, skb, next) {
++ if ((!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) &&
++ 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);
++ }
++ }
+
+ if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
+ netif_tx_stop_all_queues(dev);
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -258,7 +258,7 @@
+ #define MTK_CHK_DDONE_EN BIT(28)
+ #define MTK_DMAD_WR_WDONE BIT(26)
+ #define MTK_WCOMP_EN BIT(24)
+-#define MTK_RESV_BUF (0x40 << 16)
++#define MTK_RESV_BUF (0x80 << 16)
+ #define MTK_MUTLI_CNT (0x4 << 12)
+ #define MTK_LEAKY_BUCKET_EN BIT(11)
+
diff --git a/target/linux/generic/pending-6.1/732-02-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch b/target/linux/generic/pending-6.1/732-02-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch
new file mode 100644
index 0000000000..3c6359ee41
--- /dev/null
+++ b/target/linux/generic/pending-6.1/732-02-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
+@@ -48,8 +48,7 @@
+ #define MTK_HW_FEATURES (NETIF_F_IP_CSUM | \
+ NETIF_F_RXCSUM | \
+ NETIF_F_HW_VLAN_CTAG_TX | \
+- 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-6.1/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch b/target/linux/generic/pending-6.1/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch
new file mode 100644
index 0000000000..0aa9382b6b
--- /dev/null
+++ b/target/linux/generic/pending-6.1/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch
@@ -0,0 +1,42 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 29 Mar 2023 16:02:54 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix remaining throughput
+ regression
+
+Based on further tests, it seems that the QDMA shaper is not able to
+perform shaping close to the MAC link rate without throughput loss.
+This cannot be compensated by increasing the shaping rate, so it seems
+to be an internal limit.
+
+Fix the remaining throughput regression by detecting that condition and
+limiting shaping to ports with lower link speed.
+
+This patch intentionally ignores link speed gain from TRGMII, because
+even on such links, shaping to 1000 Mbit/s incurs some throughput
+degradation.
+
+Fixes: f63959c7eec3 ("net: ethernet: mtk_eth_soc: implement multi-queue support for per-port queues")
+Reported-by: Frank Wunderlich <frank-w@public-files.de>
+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
+@@ -667,6 +667,7 @@ static void mtk_mac_link_up(struct phyli
+ MAC_MCR_FORCE_RX_FC);
+
+ /* Configure speed */
++ mac->speed = speed;
+ switch (speed) {
+ case SPEED_2500:
+ case SPEED_1000:
+@@ -3145,6 +3146,9 @@ found:
+ if (dp->index >= MTK_QDMA_NUM_QUEUES)
+ return NOTIFY_DONE;
+
++ if (mac->speed > 0 && mac->speed <= s.base.speed)
++ s.base.speed = 0;
++
+ mtk_set_queue_speed(eth, dp->index + 3, s.base.speed);
+
+ return NOTIFY_DONE;
diff --git a/target/linux/generic/pending-6.1/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch b/target/linux/generic/pending-6.1/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch
new file mode 100644
index 0000000000..9ef65ec16f
--- /dev/null
+++ b/target/linux/generic/pending-6.1/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch
@@ -0,0 +1,33 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 27 Dec 2022 15:02:51 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: ppe: fix L2 offloading with DSA
+ untagging offload enabled
+
+Check for skb metadata in order to detect the case where the DSA header is not
+present.
+
+Fixes: 2d7605a72906 ("net: ethernet: mtk_eth_soc: enable hardware DSA untagging")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -8,6 +8,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/if_ether.h>
+ #include <linux/if_vlan.h>
++#include <net/dst_metadata.h>
+ #include <net/dsa.h>
+ #include "mtk_eth_soc.h"
+ #include "mtk_ppe.h"
+@@ -757,7 +758,9 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+ skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK)
+ goto out;
+
+- tag += 4;
++ if (!skb_metadata_dst(skb))
++ tag += 4;
++
+ if (get_unaligned_be16(tag) != ETH_P_8021Q)
+ break;
+
diff --git a/target/linux/generic/pending-6.1/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch b/target/linux/generic/pending-6.1/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch
new file mode 100644
index 0000000000..4f765c5c14
--- /dev/null
+++ b/target/linux/generic/pending-6.1/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch
@@ -0,0 +1,266 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 20 Mar 2023 11:44:30 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add code for offloading flows
+ from wlan devices
+
+WED version 2 (on MT7986 and later) can offload flows originating from wireless
+devices. In order to make that work, ndo_setup_tc needs to be implemented on
+the netdevs. This adds the required code to offload flows coming in from WED,
+while keeping track of the incoming wed index used for selecting the correct
+PPE device.
+
+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
+@@ -1277,6 +1277,9 @@ int mtk_gmac_rgmii_path_setup(struct mtk
+ int mtk_eth_offload_init(struct mtk_eth *eth);
+ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
+ void *type_data);
++int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
++ int ppe_index);
++void mtk_flow_offload_cleanup(struct mtk_eth *eth, struct list_head *list);
+ void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
+
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -235,7 +235,8 @@ out:
+ }
+
+ static int
+-mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
++mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
++ int ppe_index)
+ {
+ struct flow_rule *rule = flow_cls_offload_flow_rule(f);
+ struct flow_action_entry *act;
+@@ -452,6 +453,7 @@ mtk_flow_offload_replace(struct mtk_eth
+ entry->cookie = f->cookie;
+ memcpy(&entry->data, &foe, sizeof(entry->data));
+ entry->wed_index = wed_index;
++ entry->ppe_index = ppe_index;
+
+ err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry);
+ if (err < 0)
+@@ -520,25 +522,15 @@ mtk_flow_offload_stats(struct mtk_eth *e
+
+ static DEFINE_MUTEX(mtk_flow_offload_mutex);
+
+-static int
+-mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
++int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
++ int ppe_index)
+ {
+- struct flow_cls_offload *cls = type_data;
+- struct net_device *dev = cb_priv;
+- struct mtk_mac *mac = netdev_priv(dev);
+- struct mtk_eth *eth = mac->hw;
+ int err;
+
+- if (!tc_can_offload(dev))
+- return -EOPNOTSUPP;
+-
+- if (type != TC_SETUP_CLSFLOWER)
+- return -EOPNOTSUPP;
+-
+ mutex_lock(&mtk_flow_offload_mutex);
+ switch (cls->command) {
+ case FLOW_CLS_REPLACE:
+- err = mtk_flow_offload_replace(eth, cls);
++ err = mtk_flow_offload_replace(eth, cls, ppe_index);
+ break;
+ case FLOW_CLS_DESTROY:
+ err = mtk_flow_offload_destroy(eth, cls);
+@@ -556,6 +548,23 @@ mtk_eth_setup_tc_block_cb(enum tc_setup_
+ }
+
+ static int
++mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
++{
++ struct flow_cls_offload *cls = type_data;
++ struct net_device *dev = cb_priv;
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_eth *eth = mac->hw;
++
++ if (!tc_can_offload(dev))
++ return -EOPNOTSUPP;
++
++ if (type != TC_SETUP_CLSFLOWER)
++ return -EOPNOTSUPP;
++
++ return mtk_flow_offload_cmd(eth, cls, 0);
++}
++
++static int
+ mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -13,6 +13,8 @@
+ #include <linux/mfd/syscon.h>
+ #include <linux/debugfs.h>
+ #include <linux/soc/mediatek/mtk_wed.h>
++#include <net/flow_offload.h>
++#include <net/pkt_cls.h>
+ #include "mtk_eth_soc.h"
+ #include "mtk_wed_regs.h"
+ #include "mtk_wed.h"
+@@ -41,6 +43,11 @@
+ static struct mtk_wed_hw *hw_list[2];
+ static DEFINE_MUTEX(hw_lock);
+
++struct mtk_wed_flow_block_priv {
++ struct mtk_wed_hw *hw;
++ struct net_device *dev;
++};
++
+ static void
+ wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val)
+ {
+@@ -1752,6 +1759,99 @@ out:
+ mutex_unlock(&hw_lock);
+ }
+
++static int
++mtk_wed_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
++{
++ struct mtk_wed_flow_block_priv *priv = cb_priv;
++ struct flow_cls_offload *cls = type_data;
++ struct mtk_wed_hw *hw = priv->hw;
++
++ if (!tc_can_offload(priv->dev))
++ return -EOPNOTSUPP;
++
++ if (type != TC_SETUP_CLSFLOWER)
++ return -EOPNOTSUPP;
++
++ return mtk_flow_offload_cmd(hw->eth, cls, hw->index);
++}
++
++static int
++mtk_wed_setup_tc_block(struct mtk_wed_hw *hw, struct net_device *dev,
++ struct flow_block_offload *f)
++{
++ struct mtk_wed_flow_block_priv *priv;
++ static LIST_HEAD(block_cb_list);
++ struct flow_block_cb *block_cb;
++ struct mtk_eth *eth = hw->eth;
++ flow_setup_cb_t *cb;
++
++ if (!eth->soc->offload_version)
++ return -EOPNOTSUPP;
++
++ if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
++ return -EOPNOTSUPP;
++
++ cb = mtk_wed_setup_tc_block_cb;
++ f->driver_block_list = &block_cb_list;
++
++ 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;
++ }
++
++ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->hw = hw;
++ priv->dev = dev;
++ block_cb = flow_block_cb_alloc(cb, dev, priv, NULL);
++ if (IS_ERR(block_cb)) {
++ kfree(priv);
++ return PTR_ERR(block_cb);
++ }
++
++ flow_block_cb_incref(block_cb);
++ 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);
++ if (!block_cb)
++ return -ENOENT;
++
++ if (!flow_block_cb_decref(block_cb)) {
++ flow_block_cb_remove(block_cb, f);
++ list_del(&block_cb->driver_list);
++ kfree(block_cb->cb_priv);
++ }
++ return 0;
++ default:
++ return -EOPNOTSUPP;
++ }
++}
++
++static int
++mtk_wed_setup_tc(struct mtk_wed_device *wed, struct net_device *dev,
++ enum tc_setup_type type, void *type_data)
++{
++ struct mtk_wed_hw *hw = wed->hw;
++
++ if (hw->version < 2)
++ return -EOPNOTSUPP;
++
++ switch (type) {
++ case TC_SETUP_BLOCK:
++ case TC_SETUP_FT:
++ return mtk_wed_setup_tc_block(hw, dev, type_data);
++ default:
++ return -EOPNOTSUPP;
++ }
++}
++
+ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+ void __iomem *wdma, phys_addr_t wdma_phy,
+ int index)
+@@ -1771,6 +1871,7 @@ void mtk_wed_add_hw(struct device_node *
+ .irq_set_mask = mtk_wed_irq_set_mask,
+ .detach = mtk_wed_detach,
+ .ppe_check = mtk_wed_ppe_check,
++ .setup_tc = mtk_wed_setup_tc,
+ };
+ struct device_node *eth_np = eth->dev->of_node;
+ struct platform_device *pdev;
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -6,6 +6,7 @@
+ #include <linux/regmap.h>
+ #include <linux/pci.h>
+ #include <linux/skbuff.h>
++#include <linux/netdevice.h>
+
+ #define MTK_WED_TX_QUEUES 2
+ #define MTK_WED_RX_QUEUES 2
+@@ -180,6 +181,8 @@ struct mtk_wed_ops {
+
+ u32 (*irq_get)(struct mtk_wed_device *dev, u32 mask);
+ void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
++ int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev,
++ enum tc_setup_type type, void *type_data);
+ };
+
+ extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
+@@ -238,6 +241,8 @@ mtk_wed_get_rx_capa(struct mtk_wed_devic
+ (_dev)->ops->msg_update(_dev, _id, _msg, _len)
+ #define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev)
+ #define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev)
++#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) \
++ (_dev)->ops->setup_tc(_dev, _netdev, _type, _type_data)
+ #else
+ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
+ {
+@@ -256,6 +261,7 @@ static inline bool mtk_wed_device_active
+ #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV
+ #define mtk_wed_device_stop(_dev) do {} while (0)
+ #define mtk_wed_device_dma_reset(_dev) do {} while (0)
++#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) -EOPNOTSUPP
+ #endif
+
+ #endif
diff --git a/target/linux/generic/pending-6.1/736-02-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch b/target/linux/generic/pending-6.1/736-02-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch
new file mode 100644
index 0000000000..6e17e4dc52
--- /dev/null
+++ b/target/linux/generic/pending-6.1/736-02-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch
@@ -0,0 +1,37 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 20 Mar 2023 15:37:55 +0100
+Subject: [PATCH] net: ethernet: mediatek: mtk_ppe: prefer newly added l2
+ flows over existing ones
+
+When a device is roaming between interfaces and a new flow entry is created,
+we should assume that its output device is more up to date than whatever
+entry existed already.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -639,10 +639,20 @@ void mtk_foe_entry_clear(struct mtk_ppe
+ static int
+ mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
++ struct mtk_flow_entry *prev;
++
+ entry->type = MTK_FLOW_TYPE_L2;
+
+- return rhashtable_insert_fast(&ppe->l2_flows, &entry->l2_node,
+- mtk_flow_l2_ht_params);
++ prev = rhashtable_lookup_get_insert_fast(&ppe->l2_flows, &entry->l2_node,
++ mtk_flow_l2_ht_params);
++ if (likely(!prev))
++ return 0;
++
++ if (IS_ERR(prev))
++ return PTR_ERR(prev);
++
++ return rhashtable_replace_fast(&ppe->l2_flows, &prev->l2_node,
++ &entry->l2_node, mtk_flow_l2_ht_params);
+ }
+
+ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
diff --git a/target/linux/generic/pending-6.1/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch b/target/linux/generic/pending-6.1/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch
new file mode 100644
index 0000000000..29d6e0b099
--- /dev/null
+++ b/target/linux/generic/pending-6.1/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch
@@ -0,0 +1,331 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 23 Mar 2023 10:24:11 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: improve keeping track of
+ offloaded flows
+
+Unify tracking of L2 and L3 flows. Use the generic list field in struct
+mtk_foe_entry for tracking L2 subflows. Preparation for improving
+flow accounting support.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -466,42 +466,43 @@ int mtk_foe_entry_set_queue(struct mtk_e
+ return 0;
+ }
+
++static int
++mtk_flow_entry_match_len(struct mtk_eth *eth, struct mtk_foe_entry *entry)
++{
++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
++
++ if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++ return offsetof(struct mtk_foe_entry, ipv6._rsv);
++ else
++ return offsetof(struct mtk_foe_entry, ipv4.ib2);
++}
++
+ static bool
+ mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry,
+- struct mtk_foe_entry *data)
++ struct mtk_foe_entry *data, int len)
+ {
+- int type, len;
+-
+ if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP)
+ return false;
+
+- type = mtk_get_ib1_pkt_type(eth, entry->data.ib1);
+- if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
+- len = offsetof(struct mtk_foe_entry, ipv6._rsv);
+- else
+- len = offsetof(struct mtk_foe_entry, ipv4.ib2);
+-
+ return !memcmp(&entry->data.data, &data->data, len - 4);
+ }
+
+ static void
+-__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
++ bool set_state)
+ {
+- struct hlist_head *head;
+ struct hlist_node *tmp;
+
+ if (entry->type == MTK_FLOW_TYPE_L2) {
+ rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node,
+ mtk_flow_l2_ht_params);
+
+- head = &entry->l2_flows;
+- hlist_for_each_entry_safe(entry, tmp, head, l2_data.list)
+- __mtk_foe_entry_clear(ppe, entry);
++ hlist_for_each_entry_safe(entry, tmp, &entry->l2_flows, l2_list)
++ __mtk_foe_entry_clear(ppe, entry, set_state);
+ return;
+ }
+
+- hlist_del_init(&entry->list);
+- if (entry->hash != 0xffff) {
++ if (entry->hash != 0xffff && set_state) {
+ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash);
+
+ hwe->ib1 &= ~MTK_FOE_IB1_STATE;
+@@ -520,7 +521,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
+ if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW)
+ return;
+
+- hlist_del_init(&entry->l2_data.list);
++ hlist_del_init(&entry->l2_list);
++ hlist_del_init(&entry->list);
+ kfree(entry);
+ }
+
+@@ -536,66 +538,55 @@ static int __mtk_foe_entry_idle_time(str
+ return now - timestamp;
+ }
+
++static bool
++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++{
++ struct mtk_foe_entry foe = {};
++ struct mtk_foe_entry *hwe;
++ u16 hash = entry->hash;
++ int len;
++
++ if (hash == 0xffff)
++ return false;
++
++ hwe = mtk_foe_get_entry(ppe, hash);
++ len = mtk_flow_entry_match_len(ppe->eth, &entry->data);
++ memcpy(&foe, hwe, len);
++
++ if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) ||
++ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND)
++ return false;
++
++ entry->data.ib1 = foe.ib1;
++
++ return true;
++}
++
+ static void
+ mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+ u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
+ struct mtk_flow_entry *cur;
+- struct mtk_foe_entry *hwe;
+ struct hlist_node *tmp;
+ int idle;
+
+ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+- hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) {
++ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) {
+ int cur_idle;
+- u32 ib1;
+-
+- hwe = mtk_foe_get_entry(ppe, cur->hash);
+- ib1 = READ_ONCE(hwe->ib1);
+
+- if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
+- cur->hash = 0xffff;
+- __mtk_foe_entry_clear(ppe, cur);
++ if (!mtk_flow_entry_update(ppe, cur)) {
++ __mtk_foe_entry_clear(ppe, entry, false);
+ continue;
+ }
+
+- cur_idle = __mtk_foe_entry_idle_time(ppe, ib1);
++ cur_idle = __mtk_foe_entry_idle_time(ppe, cur->data.ib1);
+ if (cur_idle >= idle)
+ continue;
+
+ idle = cur_idle;
+ entry->data.ib1 &= ~ib1_ts_mask;
+- entry->data.ib1 |= hwe->ib1 & ib1_ts_mask;
+- }
+-}
+-
+-static void
+-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+-{
+- struct mtk_foe_entry foe = {};
+- struct mtk_foe_entry *hwe;
+-
+- spin_lock_bh(&ppe_lock);
+-
+- if (entry->type == MTK_FLOW_TYPE_L2) {
+- mtk_flow_entry_update_l2(ppe, entry);
+- goto out;
++ entry->data.ib1 |= cur->data.ib1 & ib1_ts_mask;
+ }
+-
+- if (entry->hash == 0xffff)
+- goto out;
+-
+- hwe = mtk_foe_get_entry(ppe, entry->hash);
+- memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size);
+- if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) {
+- entry->hash = 0xffff;
+- goto out;
+- }
+-
+- entry->data.ib1 = foe.ib1;
+-
+-out:
+- spin_unlock_bh(&ppe_lock);
+ }
+
+ static void
+@@ -632,7 +623,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+ void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+ spin_lock_bh(&ppe_lock);
+- __mtk_foe_entry_clear(ppe, entry);
++ __mtk_foe_entry_clear(ppe, entry, true);
++ hlist_del_init(&entry->list);
+ spin_unlock_bh(&ppe_lock);
+ }
+
+@@ -679,8 +671,8 @@ mtk_foe_entry_commit_subflow(struct mtk_
+ {
+ const struct mtk_soc_data *soc = ppe->eth->soc;
+ struct mtk_flow_entry *flow_info;
+- struct mtk_foe_entry foe = {}, *hwe;
+ struct mtk_foe_mac_info *l2;
++ struct mtk_foe_entry *hwe;
+ u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP;
+ int type;
+
+@@ -688,30 +680,30 @@ mtk_foe_entry_commit_subflow(struct mtk_
+ if (!flow_info)
+ return;
+
+- flow_info->l2_data.base_flow = entry;
+ flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
+ flow_info->hash = hash;
+ hlist_add_head(&flow_info->list,
+ &ppe->foe_flow[hash / soc->hash_offset]);
+- hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
++ hlist_add_head(&flow_info->l2_list, &entry->l2_flows);
+
+ hwe = mtk_foe_get_entry(ppe, hash);
+- memcpy(&foe, hwe, soc->foe_entry_size);
+- foe.ib1 &= ib1_mask;
+- foe.ib1 |= entry->data.ib1 & ~ib1_mask;
++ memcpy(&flow_info->data, hwe, soc->foe_entry_size);
++ flow_info->data.ib1 &= ib1_mask;
++ flow_info->data.ib1 |= entry->data.ib1 & ~ib1_mask;
+
+- l2 = mtk_foe_entry_l2(ppe->eth, &foe);
++ l2 = mtk_foe_entry_l2(ppe->eth, &flow_info->data);
+ memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
+
+- type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1);
++ type = mtk_get_ib1_pkt_type(ppe->eth, flow_info->data.ib1);
+ if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
+- memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
++ memcpy(&flow_info->data.ipv4.new, &flow_info->data.ipv4.orig,
++ sizeof(flow_info->data.ipv4.new));
+ else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
+ l2->etype = ETH_P_IPV6;
+
+- *mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2;
++ *mtk_foe_entry_ib2(ppe->eth, &flow_info->data) = entry->data.bridge.ib2;
+
+- __mtk_foe_entry_commit(ppe, &foe, hash);
++ __mtk_foe_entry_commit(ppe, &flow_info->data, hash);
+ }
+
+ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
+@@ -721,9 +713,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash);
+ struct mtk_flow_entry *entry;
+ struct mtk_foe_bridge key = {};
++ struct mtk_foe_entry foe = {};
+ struct hlist_node *n;
+ struct ethhdr *eh;
+ bool found = false;
++ int entry_len;
+ u8 *tag;
+
+ spin_lock_bh(&ppe_lock);
+@@ -731,20 +725,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+ if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
+ goto out;
+
+- hlist_for_each_entry_safe(entry, n, head, list) {
+- if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
+- if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
+- MTK_FOE_STATE_BIND))
+- continue;
+-
+- entry->hash = 0xffff;
+- __mtk_foe_entry_clear(ppe, entry);
+- continue;
+- }
++ entry_len = mtk_flow_entry_match_len(ppe->eth, hwe);
++ memcpy(&foe, hwe, entry_len);
+
+- if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) {
++ hlist_for_each_entry_safe(entry, n, head, list) {
++ if (found ||
++ !mtk_flow_entry_match(ppe->eth, entry, &foe, entry_len)) {
+ if (entry->hash != 0xffff)
+- entry->hash = 0xffff;
++ __mtk_foe_entry_clear(ppe, entry, false);
+ continue;
+ }
+
+@@ -795,9 +783,17 @@ out:
+
+ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+- mtk_flow_entry_update(ppe, entry);
++ int idle;
++
++ spin_lock_bh(&ppe_lock);
++ if (entry->type == MTK_FLOW_TYPE_L2)
++ mtk_flow_entry_update_l2(ppe, entry);
++ else
++ mtk_flow_entry_update(ppe, entry);
++ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
++ spin_unlock_bh(&ppe_lock);
+
+- return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
++ return idle;
+ }
+
+ int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -265,7 +265,12 @@ enum {
+
+ struct mtk_flow_entry {
+ union {
+- struct hlist_node list;
++ /* regular flows + L2 subflows */
++ struct {
++ struct hlist_node list;
++ struct hlist_node l2_list;
++ };
++ /* L2 flows */
+ struct {
+ struct rhash_head l2_node;
+ struct hlist_head l2_flows;
+@@ -275,13 +280,7 @@ struct mtk_flow_entry {
+ s8 wed_index;
+ u8 ppe_index;
+ u16 hash;
+- union {
+- struct mtk_foe_entry data;
+- struct {
+- struct mtk_flow_entry *base_flow;
+- struct hlist_node list;
+- } l2_data;
+- };
++ struct mtk_foe_entry data;
+ struct rhash_head node;
+ unsigned long cookie;
+ };
diff --git a/target/linux/generic/pending-6.1/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch b/target/linux/generic/pending-6.1/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch
new file mode 100644
index 0000000000..fdc3d520c1
--- /dev/null
+++ b/target/linux/generic/pending-6.1/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch
@@ -0,0 +1,333 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 23 Mar 2023 11:05:22 +0100
+Subject: [PATCH] net: ethernet: mediatek: fix ppe flow accounting for L2
+ flows
+
+For L2 flows, the packet/byte counters should report the sum of the
+counters of their subflows, both current and expired.
+In order to make this work, change the way that accounting data is tracked.
+Reset counters when a flow enters bind. Once it expires (or enters unbind),
+store the last counter value in struct mtk_flow_entry.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -80,9 +80,9 @@ static int mtk_ppe_mib_wait_busy(struct
+ int ret;
+ u32 val;
+
+- 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);
++ ret = readl_poll_timeout_atomic(ppe->base + MTK_PPE_MIB_SER_CR, val,
++ !(val & MTK_PPE_MIB_SER_CR_ST),
++ 20, MTK_PPE_WAIT_TIMEOUT_US);
+
+ if (ret)
+ dev_err(ppe->dev, "MIB table busy");
+@@ -90,18 +90,32 @@ static int mtk_ppe_mib_wait_busy(struct
+ return ret;
+ }
+
+-static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
++static inline struct mtk_foe_accounting *
++mtk_ppe_acct_data(struct mtk_ppe *ppe, u16 index)
++{
++ if (!ppe->acct_table)
++ return NULL;
++
++ return ppe->acct_table + index * sizeof(struct mtk_foe_accounting);
++}
++
++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index)
+ {
+ u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
+ u32 val, cnt_r0, cnt_r1, cnt_r2;
++ struct mtk_foe_accounting *acct;
+ int ret;
+
+ 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);
+
++ acct = mtk_ppe_acct_data(ppe, index);
++ if (!acct)
++ return NULL;
++
+ ret = mtk_ppe_mib_wait_busy(ppe);
+ if (ret)
+- return ret;
++ return acct;
+
+ cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
+ cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
+@@ -111,10 +125,11 @@ static int mtk_mib_entry_read(struct mtk
+ 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;
+
+- return 0;
++ acct->bytes += ((u64)byte_cnt_high << 32) | byte_cnt_low;
++ acct->packets += (pkt_cnt_high << 16) | pkt_cnt_low;
++
++ return acct;
+ }
+
+ static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
+@@ -508,13 +523,6 @@ __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;
+
+@@ -539,11 +547,14 @@ static int __mtk_foe_entry_idle_time(str
+ }
+
+ static bool
+-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
++ u64 *packets, u64 *bytes)
+ {
++ struct mtk_foe_accounting *acct;
+ struct mtk_foe_entry foe = {};
+ struct mtk_foe_entry *hwe;
+ u16 hash = entry->hash;
++ bool ret = false;
+ int len;
+
+ if (hash == 0xffff)
+@@ -554,18 +565,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp
+ memcpy(&foe, hwe, len);
+
+ if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) ||
+- FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND)
+- return false;
++ FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) {
++ acct = mtk_ppe_acct_data(ppe, hash);
++ if (acct) {
++ entry->prev_packets += acct->packets;
++ entry->prev_bytes += acct->bytes;
++ }
++
++ goto out;
++ }
+
+ entry->data.ib1 = foe.ib1;
++ acct = mtk_ppe_mib_entry_read(ppe, hash);
++ ret = true;
++
++out:
++ if (acct) {
++ *packets += acct->packets;
++ *bytes += acct->bytes;
++ }
+
+- return true;
++ return ret;
+ }
+
+ static void
+ mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+ u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
++ u64 *packets = &entry->packets;
++ u64 *bytes = &entry->bytes;
+ struct mtk_flow_entry *cur;
+ struct hlist_node *tmp;
+ int idle;
+@@ -574,7 +602,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe
+ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) {
+ int cur_idle;
+
+- if (!mtk_flow_entry_update(ppe, cur)) {
++ if (!mtk_flow_entry_update(ppe, cur, packets, bytes)) {
++ entry->prev_packets += cur->prev_packets;
++ entry->prev_bytes += cur->prev_bytes;
+ __mtk_foe_entry_clear(ppe, entry, false);
+ continue;
+ }
+@@ -589,10 +619,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe
+ }
+ }
+
++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
++ int *idle)
++{
++ entry->packets = entry->prev_packets;
++ entry->bytes = entry->prev_bytes;
++
++ spin_lock_bh(&ppe_lock);
++
++ if (entry->type == MTK_FLOW_TYPE_L2)
++ mtk_flow_entry_update_l2(ppe, entry);
++ else
++ mtk_flow_entry_update(ppe, entry, &entry->packets, &entry->bytes);
++
++ *idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
++
++ spin_unlock_bh(&ppe_lock);
++}
++
+ static void
+ __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
+ u16 hash)
+ {
++ struct mtk_foe_accounting *acct;
+ struct mtk_eth *eth = ppe->eth;
+ u16 timestamp = mtk_eth_timestamp(eth);
+ struct mtk_foe_entry *hwe;
+@@ -617,6 +666,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+
+ dma_wmb();
+
++ acct = mtk_ppe_mib_entry_read(ppe, hash);
++ if (acct) {
++ acct->packets = 0;
++ acct->bytes = 0;
++ }
++
+ mtk_ppe_cache_clear(ppe);
+ }
+
+@@ -781,21 +836,6 @@ out:
+ spin_unlock_bh(&ppe_lock);
+ }
+
+-int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+-{
+- int idle;
+-
+- spin_lock_bh(&ppe_lock);
+- if (entry->type == MTK_FLOW_TYPE_L2)
+- mtk_flow_entry_update_l2(ppe, entry);
+- else
+- mtk_flow_entry_update(ppe, entry);
+- idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+- spin_unlock_bh(&ppe_lock);
+-
+- return idle;
+-}
+-
+ int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
+ {
+ if (!ppe)
+@@ -823,32 +863,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
+ return mtk_ppe_wait_busy(ppe);
+ }
+
+-struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
+- struct mtk_foe_accounting *diff)
+-{
+- struct mtk_foe_accounting *acct;
+- int size = sizeof(struct mtk_foe_accounting);
+- u64 bytes, packets;
+-
+- if (!ppe->accounting)
+- return NULL;
+-
+- 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;
+-}
+-
+ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
+ {
+ bool accounting = eth->soc->has_accounting;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -283,6 +283,8 @@ struct mtk_flow_entry {
+ struct mtk_foe_entry data;
+ struct rhash_head node;
+ unsigned long cookie;
++ u64 prev_packets, prev_bytes;
++ u64 packets, bytes;
+ };
+
+ struct mtk_mib_entry {
+@@ -325,6 +327,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+ void mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
+ int mtk_ppe_prepare_reset(struct mtk_ppe *ppe);
++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index);
+
+ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
+
+@@ -373,9 +376,8 @@ int mtk_foe_entry_set_queue(struct mtk_e
+ 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);
+ 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);
++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
++ int *idle);
+
+ #endif
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -96,7 +96,7 @@ 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);
++ acct = mtk_ppe_mib_entry_read(ppe, i);
+
+ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
+ seq_printf(m, "%05x %s %7s", i,
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -499,24 +499,21 @@ 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;
++ u64 packets, bytes;
++ int idle;
+
+ entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
+ mtk_flow_ht_params);
+ if (!entry)
+ return -ENOENT;
+
+- idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
++ packets = entry->packets;
++ bytes = entry->bytes;
++ mtk_foe_entry_get_stats(eth->ppe[entry->ppe_index], entry, &idle);
++ f->stats.pkts += entry->packets - packets;
++ f->stats.bytes += entry->bytes - bytes;
+ 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;
+- }
+-
+ return 0;
+ }
+
diff --git a/target/linux/generic/pending-6.1/736-05-net-ethernet-mtk_eth_soc-add-missing-ppe-cache-flush.patch b/target/linux/generic/pending-6.1/736-05-net-ethernet-mtk_eth_soc-add-missing-ppe-cache-flush.patch
new file mode 100644
index 0000000000..30839319c7
--- /dev/null
+++ b/target/linux/generic/pending-6.1/736-05-net-ethernet-mtk_eth_soc-add-missing-ppe-cache-flush.patch
@@ -0,0 +1,21 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 23 Mar 2023 11:19:14 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add missing ppe cache flush when
+ deleting a flow
+
+The cache needs to be flushed to ensure that the hardware stops offloading
+the flow immediately.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -523,6 +523,7 @@ __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();
++ mtk_ppe_cache_clear(ppe);
+ }
+ entry->hash = 0xffff;
+
diff --git a/target/linux/generic/pending-6.1/736-06-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch b/target/linux/generic/pending-6.1/736-06-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch
new file mode 100644
index 0000000000..3dfa193e70
--- /dev/null
+++ b/target/linux/generic/pending-6.1/736-06-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch
@@ -0,0 +1,51 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 23 Mar 2023 21:45:43 +0100
+Subject: [PATCH] net: ethernet: mediatek: fix ppe flow accounting for v1
+ hardware
+
+Older chips (like MT7622) use a different bit in ib2 to enable hardware
+counter support.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -646,6 +646,7 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+ struct mtk_eth *eth = ppe->eth;
+ u16 timestamp = mtk_eth_timestamp(eth);
+ struct mtk_foe_entry *hwe;
++ u32 val;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+ entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP_V2;
+@@ -662,8 +663,13 @@ __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;
++ if (ppe->accounting) {
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
++ val = MTK_FOE_IB2_MIB_CNT_V2;
++ else
++ val = MTK_FOE_IB2_MIB_CNT;
++ *mtk_foe_entry_ib2(eth, hwe) |= val;
++ }
+
+ dma_wmb();
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -55,9 +55,10 @@ enum {
+ #define MTK_FOE_IB2_PSE_QOS BIT(4)
+ #define MTK_FOE_IB2_DEST_PORT GENMASK(7, 5)
+ #define MTK_FOE_IB2_MULTICAST BIT(8)
++#define MTK_FOE_IB2_MIB_CNT BIT(10)
+
+ #define MTK_FOE_IB2_WDMA_QID2 GENMASK(13, 12)
+-#define MTK_FOE_IB2_MIB_CNT BIT(15)
++#define MTK_FOE_IB2_MIB_CNT_V2 BIT(15)
+ #define MTK_FOE_IB2_WDMA_DEVIDX BIT(16)
+ #define MTK_FOE_IB2_WDMA_WINFO BIT(17)
+
diff --git a/target/linux/generic/pending-6.1/750-skb-Do-mix-page-pool-and-page-referenced-frags-in-GR.patch b/target/linux/generic/pending-6.1/750-skb-Do-mix-page-pool-and-page-referenced-frags-in-GR.patch
new file mode 100644
index 0000000000..6f28e19a48
--- /dev/null
+++ b/target/linux/generic/pending-6.1/750-skb-Do-mix-page-pool-and-page-referenced-frags-in-GR.patch
@@ -0,0 +1,35 @@
+From: Alexander Duyck <alexanderduyck@fb.com>
+Date: Thu, 26 Jan 2023 11:06:59 -0800
+Subject: [PATCH] skb: Do mix page pool and page referenced frags in GRO
+
+GSO should not merge page pool recycled frames with standard reference
+counted frames. Traditionally this didn't occur, at least not often.
+However as we start looking at adding support for wireless adapters there
+becomes the potential to mix the two due to A-MSDU repartitioning frames in
+the receive path. There are possibly other places where this may have
+occurred however I suspect they must be few and far between as we have not
+seen this issue until now.
+
+Fixes: 53e0961da1c7 ("page_pool: add frag page recycling support in page pool")
+Reported-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Alexander Duyck <alexanderduyck@fb.com>
+---
+
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -4347,6 +4347,15 @@ int skb_gro_receive(struct sk_buff *p, s
+ if (unlikely(p->len + len >= 65536 || NAPI_GRO_CB(skb)->flush))
+ return -E2BIG;
+
++ /* Do not splice page pool based packets w/ non-page pool
++ * packets. This can result in reference count issues as page
++ * pool pages will not decrement the reference count and will
++ * instead be immediately returned to the pool or have frag
++ * count decremented.
++ */
++ if (p->pp_recycle != skb->pp_recycle)
++ return -ETOOMANYREFS;
++
+ lp = NAPI_GRO_CB(p)->last;
+ pinfo = skb_shinfo(lp);
+
diff --git a/target/linux/generic/pending-6.1/760-net-core-add-optional-threading-for-backlog-processi.patch b/target/linux/generic/pending-6.1/760-net-core-add-optional-threading-for-backlog-processi.patch
new file mode 100644
index 0000000000..fc83416b2a
--- /dev/null
+++ b/target/linux/generic/pending-6.1/760-net-core-add-optional-threading-for-backlog-processi.patch
@@ -0,0 +1,232 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 16 Feb 2023 18:39:04 +0100
+Subject: [PATCH] net/core: add optional threading for backlog processing
+
+When dealing with few flows or an imbalance on CPU utilization, static RPS
+CPU assignment can be too inflexible. Add support for enabling threaded NAPI
+for backlog processing in order to allow the scheduler to better balance
+processing. This helps better spread the load across idle CPUs.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -502,6 +502,7 @@ static inline bool napi_complete(struct
+ }
+
+ int dev_set_threaded(struct net_device *dev, bool threaded);
++int backlog_set_threaded(bool threaded);
+
+ /**
+ * napi_disable - prevent NAPI from scheduling
+@@ -3365,6 +3366,7 @@ struct softnet_data {
+ unsigned int processed;
+ unsigned int time_squeeze;
+ unsigned int received_rps;
++ unsigned int process_queue_empty;
+ #ifdef CONFIG_RPS
+ struct softnet_data *rps_ipi_list;
+ #endif
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -4574,7 +4574,7 @@ static int rps_ipi_queued(struct softnet
+ #ifdef CONFIG_RPS
+ struct softnet_data *mysd = this_cpu_ptr(&softnet_data);
+
+- if (sd != mysd) {
++ if (sd != mysd && !test_bit(NAPI_STATE_THREADED, &sd->backlog.state)) {
+ sd->rps_ipi_next = mysd->rps_ipi_list;
+ mysd->rps_ipi_list = sd;
+
+@@ -5755,6 +5755,8 @@ static DEFINE_PER_CPU(struct work_struct
+ /* Network device is going away, flush any packets still pending */
+ static void flush_backlog(struct work_struct *work)
+ {
++ unsigned int process_queue_empty;
++ bool threaded, flush_processq;
+ struct sk_buff *skb, *tmp;
+ struct softnet_data *sd;
+
+@@ -5770,9 +5772,18 @@ static void flush_backlog(struct work_st
+ input_queue_head_incr(sd);
+ }
+ }
++
++ threaded = test_bit(NAPI_STATE_THREADED, &sd->backlog.state);
++ flush_processq = threaded &&
++ !skb_queue_empty_lockless(&sd->process_queue);
++ if (flush_processq)
++ process_queue_empty = sd->process_queue_empty;
+ rps_unlock(sd);
+ local_irq_enable();
+
++ if (threaded)
++ goto out;
++
+ skb_queue_walk_safe(&sd->process_queue, skb, tmp) {
+ if (skb->dev->reg_state == NETREG_UNREGISTERING) {
+ __skb_unlink(skb, &sd->process_queue);
+@@ -5780,7 +5791,18 @@ static void flush_backlog(struct work_st
+ input_queue_head_incr(sd);
+ }
+ }
++
++out:
+ local_bh_enable();
++
++ while (flush_processq) {
++ msleep(1);
++ local_irq_disable();
++ rps_lock(sd);
++ flush_processq = process_queue_empty == sd->process_queue_empty;
++ rps_unlock(sd);
++ local_irq_enable();
++ }
+ }
+
+ static bool flush_required(int cpu)
+@@ -6463,6 +6485,7 @@ static int process_backlog(struct napi_s
+
+ local_irq_disable();
+ rps_lock(sd);
++ sd->process_queue_empty++;
+ if (skb_queue_empty(&sd->input_pkt_queue)) {
+ /*
+ * Inline a custom version of __napi_complete().
+@@ -6472,7 +6495,8 @@ static int process_backlog(struct napi_s
+ * We can use a plain write instead of clear_bit(),
+ * and we dont need an smp_mb() memory barrier.
+ */
+- napi->state = 0;
++ napi->state &= ~(NAPIF_STATE_SCHED |
++ NAPIF_STATE_SCHED_THREADED);
+ again = false;
+ } else {
+ skb_queue_splice_tail_init(&sd->input_pkt_queue,
+@@ -6889,6 +6913,57 @@ int dev_set_threaded(struct net_device *
+ }
+ EXPORT_SYMBOL(dev_set_threaded);
+
++int backlog_set_threaded(bool threaded)
++{
++ static bool backlog_threaded;
++ int err = 0;
++ int i;
++
++ if (backlog_threaded == threaded)
++ return 0;
++
++ for_each_possible_cpu(i) {
++ struct softnet_data *sd = &per_cpu(softnet_data, i);
++ struct napi_struct *n = &sd->backlog;
++
++ if (n->thread)
++ continue;
++ n->thread = kthread_run(napi_threaded_poll, n, "napi/backlog-%d", i);
++ if (IS_ERR(n->thread)) {
++ err = PTR_ERR(n->thread);
++ pr_err("kthread_run failed with err %d\n", err);
++ n->thread = NULL;
++ threaded = false;
++ break;
++ }
++
++ }
++
++ backlog_threaded = threaded;
++
++ /* Make sure kthread is created before THREADED bit
++ * is set.
++ */
++ smp_mb__before_atomic();
++
++ for_each_possible_cpu(i) {
++ struct softnet_data *sd = &per_cpu(softnet_data, i);
++ struct napi_struct *n = &sd->backlog;
++ unsigned long flags;
++
++ local_irq_save(flags);
++ rps_lock(sd);
++ if (threaded)
++ n->state |= NAPIF_STATE_THREADED;
++ else
++ n->state &= ~NAPIF_STATE_THREADED;
++ rps_unlock(sd);
++ local_irq_restore(flags);
++ }
++
++ return err;
++}
++
+ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int), int weight)
+ {
+@@ -11367,6 +11442,9 @@ static int dev_cpu_dead(unsigned int old
+ raise_softirq_irqoff(NET_TX_SOFTIRQ);
+ local_irq_enable();
+
++ if (test_bit(NAPI_STATE_THREADED, &oldsd->backlog.state))
++ return 0;
++
+ #ifdef CONFIG_RPS
+ remsd = oldsd->rps_ipi_list;
+ oldsd->rps_ipi_list = NULL;
+@@ -11706,6 +11784,7 @@ static int __init net_dev_init(void)
+ sd->cpu = i;
+ #endif
+
++ INIT_LIST_HEAD(&sd->backlog.poll_list);
+ init_gro_hash(&sd->backlog);
+ sd->backlog.poll = process_backlog;
+ sd->backlog.weight = weight_p;
+--- a/net/core/sysctl_net_core.c
++++ b/net/core/sysctl_net_core.c
+@@ -28,6 +28,7 @@ static int int_3600 = 3600;
+ static int min_sndbuf = SOCK_MIN_SNDBUF;
+ static int min_rcvbuf = SOCK_MIN_RCVBUF;
+ static int max_skb_frags = MAX_SKB_FRAGS;
++static int backlog_threaded;
+ static long long_one __maybe_unused = 1;
+ static long long_max __maybe_unused = LONG_MAX;
+
+@@ -114,6 +115,23 @@ static int rps_sock_flow_sysctl(struct c
+ }
+ #endif /* CONFIG_RPS */
+
++static int backlog_threaded_sysctl(struct ctl_table *table, int write,
++ void *buffer, size_t *lenp, loff_t *ppos)
++{
++ static DEFINE_MUTEX(backlog_threaded_mutex);
++ int ret;
++
++ mutex_lock(&backlog_threaded_mutex);
++
++ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
++ if (write && !ret)
++ ret = backlog_set_threaded(backlog_threaded);
++
++ mutex_unlock(&backlog_threaded_mutex);
++
++ return ret;
++}
++
+ #ifdef CONFIG_NET_FLOW_LIMIT
+ static DEFINE_MUTEX(flow_limit_update_mutex);
+
+@@ -470,6 +488,15 @@ static struct ctl_table net_core_table[]
+ .proc_handler = rps_sock_flow_sysctl
+ },
+ #endif
++ {
++ .procname = "backlog_threaded",
++ .data = &backlog_threaded,
++ .maxlen = sizeof(unsigned int),
++ .mode = 0644,
++ .proc_handler = backlog_threaded_sysctl,
++ .extra1 = SYSCTL_ZERO,
++ .extra2 = SYSCTL_ONE
++ },
+ #ifdef CONFIG_NET_FLOW_LIMIT
+ {
+ .procname = "flow_limit_cpu_bitmap",
diff --git a/target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch
new file mode 100644
index 0000000000..18933de3a0
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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
+@@ -6333,6 +6333,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-6.1/772-net-dsa-b53-add-support-for-BCM63xx-RGMIIs.patch b/target/linux/generic/pending-6.1/772-net-dsa-b53-add-support-for-BCM63xx-RGMIIs.patch
new file mode 100644
index 0000000000..ca963d1260
--- /dev/null
+++ b/target/linux/generic/pending-6.1/772-net-dsa-b53-add-support-for-BCM63xx-RGMIIs.patch
@@ -0,0 +1,174 @@
+From patchwork Sun Mar 19 22:08:05 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
+ <noltari@gmail.com>
+X-Patchwork-Id: 13180645
+X-Patchwork-Delegate: kuba@kernel.org
+Return-Path: <netdev-owner@vger.kernel.org>
+X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
+ aws-us-west-2-korg-lkml-1.web.codeaurora.org
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by smtp.lore.kernel.org (Postfix) with ESMTP id A7A46C6FD1F
+ for <netdev@archiver.kernel.org>; Sun, 19 Mar 2023 22:08:15 +0000 (UTC)
+Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
+ id S230223AbjCSWIN (ORCPT <rfc822;netdev@archiver.kernel.org>);
+ Sun, 19 Mar 2023 18:08:13 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32878 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S229565AbjCSWIM (ORCPT
+ <rfc822;netdev@vger.kernel.org>); Sun, 19 Mar 2023 18:08:12 -0400
+Received: from mail-wr1-x42e.google.com (mail-wr1-x42e.google.com
+ [IPv6:2a00:1450:4864:20::42e])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 605D3E062;
+ Sun, 19 Mar 2023 15:08:10 -0700 (PDT)
+Received: by mail-wr1-x42e.google.com with SMTP id h17so8695188wrt.8;
+ Sun, 19 Mar 2023 15:08:10 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=gmail.com; s=20210112; t=1679263689;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:from:to:cc:subject:date
+ :message-id:reply-to;
+ bh=GmPK75Q9ZD3h3IYflWYuDwS99V2e532KgDlnNucAoJg=;
+ b=PSdrywW48P4Lq8z9wOSPXFB/ZdO/JfuyiGlw3Gz1Iriy+Smo/cBnJ0Ve9zKkX3AKTO
+ Tr7/g8xhSQX8sU5WAOEPC13uVjKpO4VZsamXHTmMKL4mmfII3K/piAsQcMQkkNpgouab
+ Ci9yr+7ASSmqEUHIbYTM6sl6a47rPwqk3b3DcTIE2CwJsPPNXnpQ/aSVbJAcEdhcZICc
+ X4rAmjrYjcsl8coFIGHHPlrMH9ShekQWxB84vEb6bO1nXOORNPizOHuY1vJ3wa3WgXsx
+ YwlvutMFVIUXfgL2ZwCmQAKWJPiAaFk+CCk3oxSeOYoAzkjcbMyapz9VnooStfvR2aV3
+ k+2g==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20210112; t=1679263689;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
+ :subject:date:message-id:reply-to;
+ bh=GmPK75Q9ZD3h3IYflWYuDwS99V2e532KgDlnNucAoJg=;
+ b=NGjqrGERyaxRwINtevHaY97h9X9W+1UY62YYwotqwv5+cfvB8myjBbD3WH2WzaqMes
+ o9MMER9RE8/arW3jIVlBv4ORDUuEZ7AeGgy5UbFyQZIPHlp+hJ/sxFrGvYUwamg4Qrr9
+ ojargh8ORsEiMeqaf+5AkmEagNhrrV3ax0pUuWDzbJ3vXGoHjfCetHz5xyNL46dvXBfb
+ l/OZqjv9IYob552uUoUmCy/TbEQDqvmjkFrROFK9gtBNxgxUJkwbyiWIOVsf6RR8OarP
+ f7bbvSJYkvTvzx2u/g0Up7NW5ZyihMGBmDs377M3yW6AnSxW6jlfl30QmMU1aEigYXvy
+ v3mA==
+X-Gm-Message-State: AO0yUKUm1PYmYa4xlHuVD23mZcZm83a+xbhcbs0Xryi3yF/+UnjM4Cho
+ GAfqSh5MZ/rlOAm3Vnpn//9hOG5Lc8vLYg==
+X-Google-Smtp-Source:
+ AK7set+5pTahGGgk1hF/mHGkGBhsMf0//oQjZd4QFHx+HaeSgP5f6q7g0bRUcTX8kRtgHH0T7l1/hQ==
+X-Received: by 2002:a5d:474f:0:b0:2d6:2ae8:70d with SMTP id
+ o15-20020a5d474f000000b002d62ae8070dmr2382593wrs.39.1679263688549;
+ Sun, 19 Mar 2023 15:08:08 -0700 (PDT)
+Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
+ [79.146.124.255])
+ by smtp.gmail.com with ESMTPSA id
+ d6-20020a5d6dc6000000b002c53f6c7599sm7354727wrz.29.2023.03.19.15.08.07
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Sun, 19 Mar 2023 15:08:07 -0700 (PDT)
+From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+To: f.fainelli@gmail.com, jonas.gorski@gmail.com, andrew@lunn.ch,
+ olteanv@gmail.com, davem@davemloft.net, edumazet@google.com,
+ kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org,
+ linux-kernel@vger.kernel.org
+Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+Subject: [PATCH v2] net: dsa: b53: add support for BCM63xx RGMIIs
+Date: Sun, 19 Mar 2023 23:08:05 +0100
+Message-Id: <20230319220805.124024-1-noltari@gmail.com>
+X-Mailer: git-send-email 2.30.2
+In-Reply-To: <20230319183330.761251-1-noltari@gmail.com>
+References: <20230319183330.761251-1-noltari@gmail.com>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+BCM63xx RGMII ports require additional configuration in order to work.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+---
+ v2: add changes suggested by Andrew:
+ - Use a switch statement.
+ - Use dev_dbg() instead of dev_info().
+
+ drivers/net/dsa/b53/b53_common.c | 46 ++++++++++++++++++++++++++++++++
+ drivers/net/dsa/b53/b53_priv.h | 1 +
+ 2 files changed, 47 insertions(+)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -1209,6 +1209,46 @@ static void b53_force_port_config(struct
+ b53_write8(dev, B53_CTRL_PAGE, off, reg);
+ }
+
++static void b53_adjust_63xx_rgmii(struct dsa_switch *ds, int port,
++ phy_interface_t interface)
++{
++ struct b53_device *dev = ds->priv;
++ u8 rgmii_ctrl = 0, off;
++
++ if (port == dev->imp_port)
++ off = B53_RGMII_CTRL_IMP;
++ else
++ off = B53_RGMII_CTRL_P(port);
++
++ b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl);
++
++ switch (interface) {
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ rgmii_ctrl |= (RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC);
++ break;
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ rgmii_ctrl &= ~(RGMII_CTRL_DLL_TXC);
++ rgmii_ctrl |= RGMII_CTRL_DLL_RXC;
++ break;
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC);
++ rgmii_ctrl |= RGMII_CTRL_DLL_TXC;
++ break;
++ case PHY_INTERFACE_MODE_RGMII:
++ default:
++ rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC);
++ break;
++ }
++
++ if (port != dev->imp_port)
++ rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII;
++
++ b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl);
++
++ dev_dbg(ds->dev, "Configured port %d for %s\n", port,
++ phy_modes(interface));
++}
++
+ static void b53_adjust_link(struct dsa_switch *ds, int port,
+ struct phy_device *phydev)
+ {
+@@ -1235,6 +1275,9 @@ static void b53_adjust_link(struct dsa_s
+ tx_pause, rx_pause);
+ b53_force_link(dev, port, phydev->link);
+
++ if (is63xx(dev) && port >= B53_63XX_RGMII0)
++ b53_adjust_63xx_rgmii(ds, port, phydev->interface);
++
+ if (is531x5(dev) && phy_interface_is_rgmii(phydev)) {
+ if (port == dev->imp_port)
+ off = B53_RGMII_CTRL_IMP;
+@@ -1419,6 +1462,9 @@ void b53_phylink_mac_link_up(struct dsa_
+ {
+ struct b53_device *dev = ds->priv;
+
++ if (is63xx(dev) && port >= B53_63XX_RGMII0)
++ b53_adjust_63xx_rgmii(ds, port, interface);
++
+ if (mode == MLO_AN_PHY)
+ return;
+
+--- a/drivers/net/dsa/b53/b53_priv.h
++++ b/drivers/net/dsa/b53/b53_priv.h
+@@ -206,6 +206,7 @@ static inline int is58xx(struct b53_devi
+ dev->chip_id == BCM7278_DEVICE_ID;
+ }
+
++#define B53_63XX_RGMII0 4
+ #define B53_CPU_PORT_25 5
+ #define B53_CPU_PORT 8
+
diff --git a/target/linux/generic/pending-6.1/773-net-dsa-b53-mmap-add-more-63xx-SoCs.patch b/target/linux/generic/pending-6.1/773-net-dsa-b53-mmap-add-more-63xx-SoCs.patch
new file mode 100644
index 0000000000..e2da4e7f05
--- /dev/null
+++ b/target/linux/generic/pending-6.1/773-net-dsa-b53-mmap-add-more-63xx-SoCs.patch
@@ -0,0 +1,108 @@
+From patchwork Tue Mar 21 17:33:57 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
+ <noltari@gmail.com>
+X-Patchwork-Id: 13183003
+X-Patchwork-Delegate: kuba@kernel.org
+Return-Path: <netdev-owner@vger.kernel.org>
+X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
+ aws-us-west-2-korg-lkml-1.web.codeaurora.org
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by smtp.lore.kernel.org (Postfix) with ESMTP id 823A1C761AF
+ for <netdev@archiver.kernel.org>; Tue, 21 Mar 2023 17:35:06 +0000 (UTC)
+Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
+ id S230490AbjCURfE (ORCPT <rfc822;netdev@archiver.kernel.org>);
+ Tue, 21 Mar 2023 13:35:04 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47440 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S230357AbjCURex (ORCPT
+ <rfc822;netdev@vger.kernel.org>); Tue, 21 Mar 2023 13:34:53 -0400
+Received: from mail-wr1-x430.google.com (mail-wr1-x430.google.com
+ [IPv6:2a00:1450:4864:20::430])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C9A45559D8;
+ Tue, 21 Mar 2023 10:34:26 -0700 (PDT)
+Received: by mail-wr1-x430.google.com with SMTP id m2so14547588wrh.6;
+ Tue, 21 Mar 2023 10:34:26 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=gmail.com; s=20210112; t=1679420063;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:from:to:cc:subject:date
+ :message-id:reply-to;
+ bh=cUvnluVaZPzfEQB9fMRuYo+4/361t/7po7nyUBBJfxc=;
+ b=F0pa8JmQZ1FeXVtdpCygur8UmLrgKwxCcjaMn312u5zNvsXsEPeCAIDqP2tvNNTwv/
+ UYjaNaoZ77HSvv/gSqeG808AXGyNs1PvLuHZYuUTJRNuLaMixKtkNFi4ypheCdk0WCiE
+ IWz0DIm6ojmdwMqafDUKQ6Qwkv5R0vo8Wh5vpjimEmCelOyMvfuLZNqubsiGqpnCguBp
+ uWlmKh95/VubCGgiGG2xK1IXQayL14ENuWseDds7nVpVK50NycrFgJbL17Bd6qJKYkbo
+ m70IC+9jM0hjwKXpyi6ipCBNcW+1E6JIwILVC04Xi+BTpOGhbUAQ59Yn2hyq7tQM7dzs
+ 4PLg==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20210112; t=1679420063;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
+ :subject:date:message-id:reply-to;
+ bh=cUvnluVaZPzfEQB9fMRuYo+4/361t/7po7nyUBBJfxc=;
+ b=SCX78yTuGjdnE5nuL0p7+kxGnOzsCExsigLdaV+x/JswmwxSpZvxn223i1yM95klj9
+ Rk0RnXqATLF1wZA7L1YmbeZ66zxUwW/osnCjJHPeEF8AGgjK/qawtLl/HJQHN67NaRNQ
+ bDsRn2nWQ2GRTRFpvD+iGRy4uyQCDu9HFxLbn43fBsBmRnXWGPQP5cEb90tL83/Onp4D
+ Lx/XcyZOh9QRfJNhj+G1BAeRCLRA/sdA0W3Ecu5SCFs+LtS6uvLVGWDKEDfnZhYY8Xqf
+ Mx9evWzdW2OorEN2FI6+xTglvnEBcVhHIJ7XEGAhCG6ocgMZeck++774S8RWumWl8xpy
+ /K9Q==
+X-Gm-Message-State: AO0yUKUORAlGfbkNwnYmQnTWcGPqW6sp4g9WfgQmRZGCV+9tCB0OebSP
+ ICq6v4YPmUPNRl/WNnVCbps=
+X-Google-Smtp-Source:
+ AK7set8pFDl8fHRwGPhAguqxIfqnQ4PY+b57IHEsybIaQ/HPNwdJ1cs1+IPBGHe3TL14dTS4aVNpHA==
+X-Received: by 2002:a5d:6991:0:b0:2ce:aab5:f96b with SMTP id
+ g17-20020a5d6991000000b002ceaab5f96bmr2965175wru.67.1679420062764;
+ Tue, 21 Mar 2023 10:34:22 -0700 (PDT)
+Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
+ [79.146.124.255])
+ by smtp.gmail.com with ESMTPSA id
+ b13-20020a056000054d00b002da1261aa44sm184775wrf.48.2023.03.21.10.34.21
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Tue, 21 Mar 2023 10:34:21 -0700 (PDT)
+From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+To: f.fainelli@gmail.com, jonas.gorski@gmail.com, andrew@lunn.ch,
+ olteanv@gmail.com, davem@davemloft.net, edumazet@google.com,
+ kuba@kernel.org, pabeni@redhat.com, robh+dt@kernel.org,
+ krzysztof.kozlowski+dt@linaro.org, netdev@vger.kernel.org,
+ devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
+Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+Subject: [PATCH v2 2/4] net: dsa: b53: mmap: add more 63xx SoCs
+Date: Tue, 21 Mar 2023 18:33:57 +0100
+Message-Id: <20230321173359.251778-3-noltari@gmail.com>
+X-Mailer: git-send-email 2.30.2
+In-Reply-To: <20230321173359.251778-1-noltari@gmail.com>
+References: <20230320155024.164523-1-noltari@gmail.com>
+ <20230321173359.251778-1-noltari@gmail.com>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+BCM6318, BCM6362 and BCM63268 are SoCs with a B53 MMAP switch.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ v2: no changes.
+
+ drivers/net/dsa/b53/b53_mmap.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/dsa/b53/b53_mmap.c
++++ b/drivers/net/dsa/b53/b53_mmap.c
+@@ -347,8 +347,11 @@ static void b53_mmap_shutdown(struct pla
+
+ static const struct of_device_id b53_mmap_of_table[] = {
+ { .compatible = "brcm,bcm3384-switch" },
++ { .compatible = "brcm,bcm6318-switch" },
+ { .compatible = "brcm,bcm6328-switch" },
++ { .compatible = "brcm,bcm6362-switch" },
+ { .compatible = "brcm,bcm6368-switch" },
++ { .compatible = "brcm,bcm63268-switch" },
+ { .compatible = "brcm,bcm63xx-switch" },
+ { /* sentinel */ },
+ };
diff --git a/target/linux/generic/pending-6.1/774-net-dsa-b53-mmap-allow-passing-a-chip-ID.patch b/target/linux/generic/pending-6.1/774-net-dsa-b53-mmap-allow-passing-a-chip-ID.patch
new file mode 100644
index 0000000000..456643f05d
--- /dev/null
+++ b/target/linux/generic/pending-6.1/774-net-dsa-b53-mmap-allow-passing-a-chip-ID.patch
@@ -0,0 +1,195 @@
+From patchwork Tue Mar 21 17:33:58 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
+ <noltari@gmail.com>
+X-Patchwork-Id: 13183004
+X-Patchwork-Delegate: kuba@kernel.org
+Return-Path: <netdev-owner@vger.kernel.org>
+X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
+ aws-us-west-2-korg-lkml-1.web.codeaurora.org
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by smtp.lore.kernel.org (Postfix) with ESMTP id B2B12C74A5B
+ for <netdev@archiver.kernel.org>; Tue, 21 Mar 2023 17:35:12 +0000 (UTC)
+Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
+ id S230297AbjCURfK (ORCPT <rfc822;netdev@archiver.kernel.org>);
+ Tue, 21 Mar 2023 13:35:10 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47438 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S230374AbjCURex (ORCPT
+ <rfc822;netdev@vger.kernel.org>); Tue, 21 Mar 2023 13:34:53 -0400
+Received: from mail-wr1-x432.google.com (mail-wr1-x432.google.com
+ [IPv6:2a00:1450:4864:20::432])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C906B5550A;
+ Tue, 21 Mar 2023 10:34:26 -0700 (PDT)
+Received: by mail-wr1-x432.google.com with SMTP id y14so14546846wrq.4;
+ Tue, 21 Mar 2023 10:34:26 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=gmail.com; s=20210112; t=1679420064;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:from:to:cc:subject:date
+ :message-id:reply-to;
+ bh=vnvnwWc5Tmg09HBQo/m9RbRM6yM8KLx8r1VA+Abfg3k=;
+ b=eFv+mwe94Y2YZMiJP5gydXVrGlbIAR5HCrY0rdcoGoMPzQUHLFckZeYCgEKudI55I7
+ gMLZYCtLwvDXvKeHM2AUigsq2YuJSeF5QwICPrhTnMwUGBg4yyyltrc3+J0lSd6/4kQv
+ h0yM1Oo4v0d8CuqjBU6bXienIk34AFVJfsPq+vWQTjAbUL7ht4WHZ2Ez2MFoTvZpkIJA
+ 5iWMyVoMbugZl6eqNRjvDHFmtBtrZIv8AFs10r2Ca6+Yxm+aq0v33DRkbSVVqgFPNEzy
+ q5QOXOeLBPL6BvyovOpmVSWGoHf1zFV7lrzcqi+uc+FuYxQ9dyN3ND73DrrhWSkLaSg9
+ r8yA==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20210112; t=1679420064;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
+ :subject:date:message-id:reply-to;
+ bh=vnvnwWc5Tmg09HBQo/m9RbRM6yM8KLx8r1VA+Abfg3k=;
+ b=jIRB8pIlrLA/ovhnEoePs/6SX8fn6l7l4fY2CxX2pLrTbP1JI8AAetPavvrNVQTr2M
+ Vm0iLbKyL/VpTq9+bSN1SMjaoi4lAMj0pgafoHrwABMVZpFauYvtCfSYTstZ2pw4Dr1j
+ wYQGj3BUSpFIYHtSIDMkb5449WA3T3TONhaQLRFAUCBD6gAFyEky5fY+DIHrGaj352B6
+ 9ST/tkqHgPpuFlmromr42KQWoTFU+Pj0Uhyp7ru4BsnF7tTshWroZZIHUJmSACudEadr
+ fBPiuurX9jgp9zNqj8Oy0HjiVUnULFCapj8yICGp5s44uDAK/XFqFXpOuJ8ptS6uPazU
+ xUwg==
+X-Gm-Message-State: AO0yUKX2w6QZfaGDHtlZAlY/U8F8VuJa3HwlgXbxgGChgdgvIoFThawv
+ oDyFAhWbVfe4DxwXTwxgJ/I=
+X-Google-Smtp-Source:
+ AK7set+sH60XiJYup7bqrZTzFJVNe1YGcX/UTfjWV9xfGwNyodc34cHvKpqNagw5J+vEpv6CKvNHaA==
+X-Received: by 2002:adf:f344:0:b0:2cd:de25:1c76 with SMTP id
+ e4-20020adff344000000b002cdde251c76mr12989754wrp.17.1679420064464;
+ Tue, 21 Mar 2023 10:34:24 -0700 (PDT)
+Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
+ [79.146.124.255])
+ by smtp.gmail.com with ESMTPSA id
+ b13-20020a056000054d00b002da1261aa44sm184775wrf.48.2023.03.21.10.34.22
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Tue, 21 Mar 2023 10:34:23 -0700 (PDT)
+From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+To: f.fainelli@gmail.com, jonas.gorski@gmail.com, andrew@lunn.ch,
+ olteanv@gmail.com, davem@davemloft.net, edumazet@google.com,
+ kuba@kernel.org, pabeni@redhat.com, robh+dt@kernel.org,
+ krzysztof.kozlowski+dt@linaro.org, netdev@vger.kernel.org,
+ devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
+Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+Subject: [PATCH v2 3/4] net: dsa: b53: mmap: allow passing a chip ID
+Date: Tue, 21 Mar 2023 18:33:58 +0100
+Message-Id: <20230321173359.251778-4-noltari@gmail.com>
+X-Mailer: git-send-email 2.30.2
+In-Reply-To: <20230321173359.251778-1-noltari@gmail.com>
+References: <20230320155024.164523-1-noltari@gmail.com>
+ <20230321173359.251778-1-noltari@gmail.com>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+BCM6318 and BCM63268 SoCs require a special handling for their RGMIIs, so we
+should be able to identify them as a special BCM63xx switch.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+---
+ v2:
+ - Add missing chip to b53_switch_chips[].
+ - Fix device_get_match_data() casting warning.
+ - Add BCM63268_DEVICE_ID to BCM6318 too.
+ - Add BCM6318 in commit description.
+
+ drivers/net/dsa/b53/b53_common.c | 13 +++++++++++++
+ drivers/net/dsa/b53/b53_mmap.c | 32 +++++++++++++++++++++++---------
+ drivers/net/dsa/b53/b53_priv.h | 9 ++++++++-
+ 3 files changed, 44 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -2461,6 +2461,19 @@ static const struct b53_chip_data b53_sw
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
+ },
+ {
++ .chip_id = BCM63268_DEVICE_ID,
++ .dev_name = "BCM63268",
++ .vlans = 4096,
++ .enabled_ports = 0, /* pdata must provide them */
++ .arl_bins = 4,
++ .arl_buckets = 1024,
++ .imp_port = 8,
++ .vta_regs = B53_VTA_REGS_63XX,
++ .duplex_reg = B53_DUPLEX_STAT_63XX,
++ .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
++ .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
++ },
++ {
+ .chip_id = BCM53010_DEVICE_ID,
+ .dev_name = "BCM53010",
+ .vlans = 4096,
+--- a/drivers/net/dsa/b53/b53_mmap.c
++++ b/drivers/net/dsa/b53/b53_mmap.c
+@@ -262,7 +262,7 @@ static int b53_mmap_probe_of(struct plat
+ return -ENOMEM;
+
+ pdata->regs = mem;
+- pdata->chip_id = BCM63XX_DEVICE_ID;
++ pdata->chip_id = (u32)(unsigned long)device_get_match_data(dev);
+ pdata->big_endian = of_property_read_bool(np, "big-endian");
+
+ of_ports = of_get_child_by_name(np, "ports");
+@@ -346,14 +346,28 @@ static void b53_mmap_shutdown(struct pla
+ }
+
+ static const struct of_device_id b53_mmap_of_table[] = {
+- { .compatible = "brcm,bcm3384-switch" },
+- { .compatible = "brcm,bcm6318-switch" },
+- { .compatible = "brcm,bcm6328-switch" },
+- { .compatible = "brcm,bcm6362-switch" },
+- { .compatible = "brcm,bcm6368-switch" },
+- { .compatible = "brcm,bcm63268-switch" },
+- { .compatible = "brcm,bcm63xx-switch" },
+- { /* sentinel */ },
++ {
++ .compatible = "brcm,bcm3384-switch",
++ .data = (void *)BCM63XX_DEVICE_ID,
++ }, {
++ .compatible = "brcm,bcm6318-switch",
++ .data = (void *)BCM63268_DEVICE_ID,
++ }, {
++ .compatible = "brcm,bcm6328-switch",
++ .data = (void *)BCM63XX_DEVICE_ID,
++ }, {
++ .compatible = "brcm,bcm6362-switch",
++ .data = (void *)BCM63XX_DEVICE_ID,
++ }, {
++ .compatible = "brcm,bcm6368-switch",
++ .data = (void *)BCM63XX_DEVICE_ID,
++ }, {
++ .compatible = "brcm,bcm63268-switch",
++ .data = (void *)BCM63268_DEVICE_ID,
++ }, {
++ .compatible = "brcm,bcm63xx-switch",
++ .data = (void *)BCM63XX_DEVICE_ID,
++ }, { /* sentinel */ }
+ };
+ MODULE_DEVICE_TABLE(of, b53_mmap_of_table);
+
+--- a/drivers/net/dsa/b53/b53_priv.h
++++ b/drivers/net/dsa/b53/b53_priv.h
+@@ -75,6 +75,7 @@ enum {
+ BCM53125_DEVICE_ID = 0x53125,
+ BCM53128_DEVICE_ID = 0x53128,
+ BCM63XX_DEVICE_ID = 0x6300,
++ BCM63268_DEVICE_ID = 0x63268,
+ BCM53010_DEVICE_ID = 0x53010,
+ BCM53011_DEVICE_ID = 0x53011,
+ BCM53012_DEVICE_ID = 0x53012,
+@@ -186,7 +187,13 @@ static inline int is531x5(struct b53_dev
+
+ static inline int is63xx(struct b53_device *dev)
+ {
+- return dev->chip_id == BCM63XX_DEVICE_ID;
++ return dev->chip_id == BCM63XX_DEVICE_ID ||
++ dev->chip_id == BCM63268_DEVICE_ID;
++}
++
++static inline int is63268(struct b53_device *dev)
++{
++ return dev->chip_id == BCM63268_DEVICE_ID;
+ }
+
+ static inline int is5301x(struct b53_device *dev)
diff --git a/target/linux/generic/pending-6.1/775-net-dsa-b53-add-BCM63268-RGMII-configuration.patch b/target/linux/generic/pending-6.1/775-net-dsa-b53-add-BCM63268-RGMII-configuration.patch
new file mode 100644
index 0000000000..d90d757fb2
--- /dev/null
+++ b/target/linux/generic/pending-6.1/775-net-dsa-b53-add-BCM63268-RGMII-configuration.patch
@@ -0,0 +1,123 @@
+From patchwork Tue Mar 21 17:33:59 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
+ <noltari@gmail.com>
+X-Patchwork-Id: 13183005
+X-Patchwork-Delegate: kuba@kernel.org
+Return-Path: <netdev-owner@vger.kernel.org>
+X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
+ aws-us-west-2-korg-lkml-1.web.codeaurora.org
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by smtp.lore.kernel.org (Postfix) with ESMTP id 31BE4C761A6
+ for <netdev@archiver.kernel.org>; Tue, 21 Mar 2023 17:35:16 +0000 (UTC)
+Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
+ id S229674AbjCURfN (ORCPT <rfc822;netdev@archiver.kernel.org>);
+ Tue, 21 Mar 2023 13:35:13 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47684 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S230327AbjCURfB (ORCPT
+ <rfc822;netdev@vger.kernel.org>); Tue, 21 Mar 2023 13:35:01 -0400
+Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com
+ [IPv6:2a00:1450:4864:20::436])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E1D855507D;
+ Tue, 21 Mar 2023 10:34:27 -0700 (PDT)
+Received: by mail-wr1-x436.google.com with SMTP id i9so14537769wrp.3;
+ Tue, 21 Mar 2023 10:34:27 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=gmail.com; s=20210112; t=1679420066;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:from:to:cc:subject:date
+ :message-id:reply-to;
+ bh=asmFs22xWYwR1Ql9m/IrNv+MPUNDn8hSjmwDRYvO7mE=;
+ b=Cqj2C6aG5vEOlhh9N3ybvDA0CV38nhQODnfdnr7utNddd323iDagoJty1Wmi3MAzj1
+ 5ORmYT5fQvUnild7C4RhcCNTBn+MoYZ+wDZwZYelu6BKHkW11YFK949ax5B50by+ASR2
+ z+rGI3wR5fVXd4VDgmcsT6zF5x69wKyhbhqIfrhG9BVFTctfaBgDS/l+bX1C56kSqv82
+ bQkKSSAehSLGpFoCU3q62OGoZVi3jDe6HDb5M1Dp2mgHhqsW19otZpJ57DjtZ1CmtPai
+ o7T/ew6WoIYSl6whBmV36jeNaDJ3TItOBrKc4nMJBDWaCg4DNzUSe0ei5Xz7Oik5lb3p
+ y9ew==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20210112; t=1679420066;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
+ :subject:date:message-id:reply-to;
+ bh=asmFs22xWYwR1Ql9m/IrNv+MPUNDn8hSjmwDRYvO7mE=;
+ b=UdI2iQNBYwRf40ivf3ROR132t95BU/p3RUzXdZLCyz6c6JWtECQ5byyGeEwoX10n5u
+ HlepoNTJxMFLYrAHGvNLDPpWPuLXMa645S1mCVZ7NyWp8W96XzSynNZPeXHuJdb464QU
+ A7UTRSW3mlvKe9OR3EcB2CfBZv0yHWR0ldbnxcxGUFw8z78PNqpOVnITtjBdfpGesJ9c
+ VJw+fiM6hCcahor4nk9LLcAryPm8xmhDLxBKaLILO8wyTUiHY8G9hsXnFCtcpetnF5wS
+ pW13beAE+odb7ZZaXZUYpWGYhCe/hLzNjbo8YpgzHwadZthxPrT5YvNIYwyrvoViLM0n
+ KDRQ==
+X-Gm-Message-State: AO0yUKW+9H/kqcAUyWeZhZJhiJjsBcYn1THmZaSDrPrk/pNuGXJXGtJd
+ NgsGZW8iSqLEv81yK+U5Os8=
+X-Google-Smtp-Source:
+ AK7set/lzQZwCSxVaOe5dZ+7TR3xaQty/vg5xvZDpRW8TwTiPQblIbw5kJJTPLp67RySehrPIlCqSg==
+X-Received: by 2002:a5d:65c9:0:b0:2ce:ac31:54ff with SMTP id
+ e9-20020a5d65c9000000b002ceac3154ffmr2776515wrw.2.1679420066191;
+ Tue, 21 Mar 2023 10:34:26 -0700 (PDT)
+Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
+ [79.146.124.255])
+ by smtp.gmail.com with ESMTPSA id
+ b13-20020a056000054d00b002da1261aa44sm184775wrf.48.2023.03.21.10.34.24
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Tue, 21 Mar 2023 10:34:25 -0700 (PDT)
+From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+To: f.fainelli@gmail.com, jonas.gorski@gmail.com, andrew@lunn.ch,
+ olteanv@gmail.com, davem@davemloft.net, edumazet@google.com,
+ kuba@kernel.org, pabeni@redhat.com, robh+dt@kernel.org,
+ krzysztof.kozlowski+dt@linaro.org, netdev@vger.kernel.org,
+ devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
+Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>,
+ Simon Horman <simon.horman@corigine.com>
+Subject: [PATCH v2 4/4] net: dsa: b53: add BCM63268 RGMII configuration
+Date: Tue, 21 Mar 2023 18:33:59 +0100
+Message-Id: <20230321173359.251778-5-noltari@gmail.com>
+X-Mailer: git-send-email 2.30.2
+In-Reply-To: <20230321173359.251778-1-noltari@gmail.com>
+References: <20230320155024.164523-1-noltari@gmail.com>
+ <20230321173359.251778-1-noltari@gmail.com>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+BCM63268 requires special RGMII configuration to work.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Simon Horman <simon.horman@corigine.com>
+---
+ v2: no changes.
+
+ drivers/net/dsa/b53/b53_common.c | 6 +++++-
+ drivers/net/dsa/b53/b53_regs.h | 1 +
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -1240,8 +1240,12 @@ static void b53_adjust_63xx_rgmii(struct
+ break;
+ }
+
+- if (port != dev->imp_port)
++ if (port != dev->imp_port) {
++ if (is63268(dev))
++ rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE;
++
+ rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII;
++ }
+
+ b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl);
+
+--- a/drivers/net/dsa/b53/b53_regs.h
++++ b/drivers/net/dsa/b53/b53_regs.h
+@@ -138,6 +138,7 @@
+
+ #define B53_RGMII_CTRL_IMP 0x60
+ #define RGMII_CTRL_ENABLE_GMII BIT(7)
++#define RGMII_CTRL_MII_OVERRIDE BIT(6)
+ #define RGMII_CTRL_TIMING_SEL BIT(2)
+ #define RGMII_CTRL_DLL_RXC BIT(1)
+ #define RGMII_CTRL_DLL_TXC BIT(0)
diff --git a/target/linux/generic/pending-6.1/777-net-dsa-b53-mdio-add-support-for-BCM53134.patch b/target/linux/generic/pending-6.1/777-net-dsa-b53-mdio-add-support-for-BCM53134.patch
new file mode 100644
index 0000000000..53494eca6e
--- /dev/null
+++ b/target/linux/generic/pending-6.1/777-net-dsa-b53-mdio-add-support-for-BCM53134.patch
@@ -0,0 +1,189 @@
+From patchwork Fri Mar 24 08:41:38 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
+ <noltari@gmail.com>
+X-Patchwork-Id: 13186549
+X-Patchwork-Delegate: kuba@kernel.org
+Return-Path: <netdev-owner@vger.kernel.org>
+X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
+ aws-us-west-2-korg-lkml-1.web.codeaurora.org
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by smtp.lore.kernel.org (Postfix) with ESMTP id EF744C76195
+ for <netdev@archiver.kernel.org>; Fri, 24 Mar 2023 08:42:01 +0000 (UTC)
+Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
+ id S231807AbjCXImA (ORCPT <rfc822;netdev@archiver.kernel.org>);
+ Fri, 24 Mar 2023 04:42:00 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32956 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S231272AbjCXIly (ORCPT
+ <rfc822;netdev@vger.kernel.org>); Fri, 24 Mar 2023 04:41:54 -0400
+Received: from mail-ed1-x535.google.com (mail-ed1-x535.google.com
+ [IPv6:2a00:1450:4864:20::535])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 113A517CF3;
+ Fri, 24 Mar 2023 01:41:46 -0700 (PDT)
+Received: by mail-ed1-x535.google.com with SMTP id ek18so4877175edb.6;
+ Fri, 24 Mar 2023 01:41:45 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=gmail.com; s=20210112; t=1679647304;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:from:to:cc:subject:date
+ :message-id:reply-to;
+ bh=OfUWRaFIQIQw/lRivER+LHryfdLliXzvabGrcmkQVEU=;
+ b=JMrl6Eay1FS0JZqgPHsbcVzuNAbFELc0SLNGyzYtOVQXcI+YwKDM9Ls7I9PsQVEPoZ
+ CthomCTYoz5G9DU7uBuia207rnjOhssZJRu0syrCoU+O/ZiQyGLJDvq61z5oZJxC2S40
+ kzRsUsC6MRjn64DKPWmxhsSTMKLzn2+P233LKNFbHtfi3NWF5Qu/85sUkxMurnfUgja0
+ qQhl25qYY7ZIvmlFHYefaI5UkITQFuiybrqJW9tztCdHf/gS+f33YkkvQ8njmMQa1DW0
+ ppedfOUotX+6kmHZGX1yea2V5ezEGGvRourZtYMoecTiD0E5d1J1bKhktKslVLIDm0ig
+ oc2g==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20210112; t=1679647304;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
+ :subject:date:message-id:reply-to;
+ bh=OfUWRaFIQIQw/lRivER+LHryfdLliXzvabGrcmkQVEU=;
+ b=b3Gmga5ZDbnmQfnw1GCz+eU2JwgsVzfciZuSmfYAiVxpW4c6cur3MHbpzDPhi99wzA
+ ZYAM7ryLv88rXl/tQB5g2Nte5rvMfxUeHXsT/JpsRcSSocFRbRrk0QJyiA/Xj86NiD5N
+ C1sKz50Im190FmrvPcBh6OHQbv/3MQyE+1fQx+9q3jW5rQiAWQaYk4Ng8GlWA7gtG3jB
+ fHO6Fuoenn32pgkveJbQLYL/2t2f53wGf3QLQ3IeKW7jdfIHNThwrwqBMxdHoIDaTBT9
+ UWMeJuiYtylIibo/3zbORbWOgIERlWxZRf3BCOFpnzUn4eBzio4LgjtNxZ77ITRxsmbk
+ 3+Hg==
+X-Gm-Message-State: AAQBX9dfyBfbR7Sdd5wqxMiAv3Yhk47pK1XzD87MZyAF3AxyoFyKcMaF
+ EbwJLyRvTGQEFdVWCGw1eMU=
+X-Google-Smtp-Source:
+ AKy350bpDVLq7k1FxG2Mek/VIobZL4KhufiKx8qfmpxpcWmLI3bLg8wfQKEAKJRNJBleo/7CZuCL5g==
+X-Received: by 2002:aa7:c711:0:b0:4a2:588f:b3c5 with SMTP id
+ i17-20020aa7c711000000b004a2588fb3c5mr2261236edq.21.1679647304260;
+ Fri, 24 Mar 2023 01:41:44 -0700 (PDT)
+Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
+ [79.146.124.255])
+ by smtp.gmail.com with ESMTPSA id
+ z21-20020a50cd15000000b004acbda55f6bsm10323728edi.27.2023.03.24.01.41.43
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Fri, 24 Mar 2023 01:41:43 -0700 (PDT)
+From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+To: paul.geurts@prodrive-technologies.com, f.fainelli@gmail.com,
+ jonas.gorski@gmail.com, andrew@lunn.ch, olteanv@gmail.com,
+ davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
+ pabeni@redhat.com, robh+dt@kernel.org,
+ krzysztof.kozlowski+dt@linaro.org, netdev@vger.kernel.org,
+ devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
+Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+Subject: [PATCH v2 2/2] net: dsa: b53: mdio: add support for BCM53134
+Date: Fri, 24 Mar 2023 09:41:38 +0100
+Message-Id: <20230324084138.664285-3-noltari@gmail.com>
+X-Mailer: git-send-email 2.30.2
+In-Reply-To: <20230324084138.664285-1-noltari@gmail.com>
+References: <20230323121804.2249605-1-noltari@gmail.com>
+ <20230324084138.664285-1-noltari@gmail.com>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <netdev.vger.kernel.org>
+X-Mailing-List: netdev@vger.kernel.org
+X-Patchwork-Delegate: kuba@kernel.org
+
+From: Paul Geurts <paul.geurts@prodrive-technologies.com>
+
+Add support for the BCM53134 Ethernet switch in the existing b53 dsa driver.
+BCM53134 is very similar to the BCM58XX series.
+
+Signed-off-by: Paul Geurts <paul.geurts@prodrive-technologies.com>
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+---
+ v2: add BCM53134 to is531x5() and remove special RGMII config
+
+ drivers/net/dsa/b53/b53_common.c | 15 +++++++++++++++
+ drivers/net/dsa/b53/b53_mdio.c | 5 ++++-
+ drivers/net/dsa/b53/b53_priv.h | 7 +++++--
+ 3 files changed, 24 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -2609,6 +2609,20 @@ static const struct b53_chip_data b53_sw
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ },
++ {
++ .chip_id = BCM53134_DEVICE_ID,
++ .dev_name = "BCM53134",
++ .vlans = 4096,
++ .enabled_ports = 0x12f,
++ .imp_port = 8,
++ .cpu_port = B53_CPU_PORT,
++ .vta_regs = B53_VTA_REGS,
++ .arl_bins = 4,
++ .arl_buckets = 1024,
++ .duplex_reg = B53_DUPLEX_STAT_GE,
++ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
++ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
++ },
+ };
+
+ static int b53_switch_init(struct b53_device *dev)
+@@ -2785,6 +2799,7 @@ int b53_switch_detect(struct b53_device
+ case BCM53012_DEVICE_ID:
+ case BCM53018_DEVICE_ID:
+ case BCM53019_DEVICE_ID:
++ case BCM53134_DEVICE_ID:
+ dev->chip_id = id32;
+ break;
+ default:
+--- a/drivers/net/dsa/b53/b53_mdio.c
++++ b/drivers/net/dsa/b53/b53_mdio.c
+@@ -286,6 +286,7 @@ static const struct b53_io_ops b53_mdio_
+ #define B53_BRCM_OUI_2 0x03625c00
+ #define B53_BRCM_OUI_3 0x00406000
+ #define B53_BRCM_OUI_4 0x01410c00
++#define B53_BRCM_OUI_5 0xae025000
+
+ static int b53_mdio_probe(struct mdio_device *mdiodev)
+ {
+@@ -313,7 +314,8 @@ static int b53_mdio_probe(struct mdio_de
+ if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 &&
+ (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 &&
+ (phy_id & 0xfffffc00) != B53_BRCM_OUI_3 &&
+- (phy_id & 0xfffffc00) != B53_BRCM_OUI_4) {
++ (phy_id & 0xfffffc00) != B53_BRCM_OUI_4 &&
++ (phy_id & 0xfffffc00) != B53_BRCM_OUI_5) {
+ dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id);
+ return -ENODEV;
+ }
+@@ -377,6 +379,7 @@ static const struct of_device_id b53_of_
+ { .compatible = "brcm,bcm53115" },
+ { .compatible = "brcm,bcm53125" },
+ { .compatible = "brcm,bcm53128" },
++ { .compatible = "brcm,bcm53134" },
+ { .compatible = "brcm,bcm5365" },
+ { .compatible = "brcm,bcm5389" },
+ { .compatible = "brcm,bcm5395" },
+--- a/drivers/net/dsa/b53/b53_priv.h
++++ b/drivers/net/dsa/b53/b53_priv.h
+@@ -85,6 +85,7 @@ enum {
+ BCM583XX_DEVICE_ID = 0x58300,
+ BCM7445_DEVICE_ID = 0x7445,
+ BCM7278_DEVICE_ID = 0x7278,
++ BCM53134_DEVICE_ID = 0x5075,
+ };
+
+ #define B53_N_PORTS 9
+@@ -182,7 +183,8 @@ static inline int is531x5(struct b53_dev
+ {
+ return dev->chip_id == BCM53115_DEVICE_ID ||
+ dev->chip_id == BCM53125_DEVICE_ID ||
+- dev->chip_id == BCM53128_DEVICE_ID;
++ dev->chip_id == BCM53128_DEVICE_ID ||
++ dev->chip_id == BCM53134_DEVICE_ID;
+ }
+
+ static inline int is63xx(struct b53_device *dev)
+@@ -210,7 +212,8 @@ static inline int is58xx(struct b53_devi
+ return dev->chip_id == BCM58XX_DEVICE_ID ||
+ dev->chip_id == BCM583XX_DEVICE_ID ||
+ dev->chip_id == BCM7445_DEVICE_ID ||
+- dev->chip_id == BCM7278_DEVICE_ID;
++ dev->chip_id == BCM7278_DEVICE_ID ||
++ dev->chip_id == BCM53134_DEVICE_ID;
+ }
+
+ #define B53_63XX_RGMII0 4
diff --git a/target/linux/generic/pending-6.1/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/target/linux/generic/pending-6.1/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-6.1/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-6.1/790-bus-mhi-core-add-SBL-state-callback.patch b/target/linux/generic/pending-6.1/790-bus-mhi-core-add-SBL-state-callback.patch
new file mode 100644
index 0000000000..2f728fefd7
--- /dev/null
+++ b/target/linux/generic/pending-6.1/790-bus-mhi-core-add-SBL-state-callback.patch
@@ -0,0 +1,48 @@
+From 5f7c5e1c0d7a79be144e5efc1f24728ddd7fc25c Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Sat, 5 Nov 2022 20:02:56 +0100
+Subject: [PATCH 1/2] bus: mhi: core: add SBL state callback
+
+Add support for SBL state callback in MHI core.
+
+It is required for ath11k MHI devices in order to be able to set QRTR
+instance ID in the SBL state so that QRTR instance ID-s dont conflict in
+case of multiple PCI/MHI cards or AHB + PCI/MHI card.
+Setting QRTR instance ID is only possible in SBL state and there is
+currently no way to ensure that we are in that state, so provide a
+callback that the controller can trigger off.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+---
+ drivers/bus/mhi/host/main.c | 1 +
+ include/linux/mhi.h | 2 ++
+ 2 files changed, 3 insertions(+)
+
+--- a/drivers/bus/mhi/host/main.c
++++ b/drivers/bus/mhi/host/main.c
+@@ -891,6 +891,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_
+ switch (event) {
+ case MHI_EE_SBL:
+ st = DEV_ST_TRANSITION_SBL;
++ mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_SBL_MODE);
+ break;
+ case MHI_EE_WFW:
+ case MHI_EE_AMSS:
+--- a/include/linux/mhi.h
++++ b/include/linux/mhi.h
+@@ -34,6 +34,7 @@ struct mhi_buf_info;
+ * @MHI_CB_SYS_ERROR: MHI device entered error state (may recover)
+ * @MHI_CB_FATAL_ERROR: MHI device entered fatal error state
+ * @MHI_CB_BW_REQ: Received a bandwidth switch request from device
++ * @MHI_CB_EE_SBL_MODE: MHI device entered SBL mode
+ */
+ enum mhi_callback {
+ MHI_CB_IDLE,
+@@ -45,6 +46,7 @@ enum mhi_callback {
+ MHI_CB_SYS_ERROR,
+ MHI_CB_FATAL_ERROR,
+ MHI_CB_BW_REQ,
++ MHI_CB_EE_SBL_MODE,
+ };
+
+ /**
diff --git a/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch b/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch
new file mode 100644
index 0000000000..f9b88db995
--- /dev/null
+++ b/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch
@@ -0,0 +1,43 @@
+From 1d81e51d6d79d9098013b2e8cdd677bae998c5d8 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Fri, 28 Apr 2023 02:22:59 +0200
+Subject: [PATCH 1/2] mt7530: register OF node for internal MDIO bus
+
+The MT753x switches provide a switch-internal MDIO bus for the embedded
+PHYs.
+
+Register a OF sub-node on the switch OF-node for this internal MDIO bus.
+This allows to configure the embedded PHYs using device-tree.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/dsa/mt7530.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2081,10 +2081,13 @@ mt7530_setup_mdio(struct mt7530_priv *pr
+ {
+ struct dsa_switch *ds = priv->ds;
+ struct device *dev = priv->dev;
++ struct device_node *np, *mnp;
+ struct mii_bus *bus;
+ static int idx;
+ int ret;
+
++ np = priv->dev->of_node;
++
+ bus = devm_mdiobus_alloc(dev);
+ if (!bus)
+ return -ENOMEM;
+@@ -2101,7 +2104,9 @@ mt7530_setup_mdio(struct mt7530_priv *pr
+ if (priv->irq)
+ mt7530_setup_mdio_irq(priv);
+
+- ret = devm_mdiobus_register(dev, bus);
++ mnp = of_get_child_by_name(np, "mdio");
++ ret = devm_of_mdiobus_register(dev, bus, mnp);
++ of_node_put(mnp);
+ if (ret) {
+ dev_err(dev, "failed to register MDIO bus: %d\n", ret);
+ if (priv->irq)
diff --git a/target/linux/generic/pending-6.1/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/target/linux/generic/pending-6.1/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-6.1/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-6.1/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch b/target/linux/generic/pending-6.1/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch
new file mode 100644
index 0000000000..c1e14b9271
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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"
++
++config GPIO_CASCADE
++ tristate "General GPIO cascade"
++ select MULTIPLEXER
++ help
++ Say yes here to enable support for generic GPIO cascade.
++
++ 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.
++
++ To build the driver as a module choose 'm' and the resulting module
++ will be called 'gpio-cascade'.
++
+ 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.
++ */
++
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/platform_device.h>
++#include <linux/mux/consumer.h>
++
++#include <linux/gpio/consumer.h>
++#include <linux/gpio/driver.h>
++
++struct gpio_cascade {
++ struct gpio_chip gpio_chip;
++ struct device *parent;
++ struct mux_control *mux_control;
++ struct gpio_desc *upstream_line;
++};
++
++static struct gpio_cascade *chip_to_cascade(struct gpio_chip *gc)
++{
++ return container_of(gc, struct gpio_cascade, gpio_chip);
++}
++
++static int gpio_cascade_get_direction(struct gpio_chip *gc, unsigned int offset)
++{
++ return GPIO_LINE_DIRECTION_IN;
++}
++
++static int gpio_cascade_get_value(struct gpio_chip *gc, unsigned int offset)
++{
++ struct gpio_cascade *cas = chip_to_cascade(gc);
++ int ret;
++
++ ret = mux_control_select(cas->mux_control, offset);
++ if (ret)
++ return ret;
++
++ ret = gpiod_get_value(cas->upstream_line);
++ mux_control_deselect(cas->mux_control);
++ return ret;
++}
++
++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;
++
++ cas = devm_kzalloc(dev, sizeof(*cas), GFP_KERNEL);
++ if (!cas)
++ return -ENOMEM;
++
++ 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");
++
++ 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");
++
++ cas->upstream_line = upstream;
++ cas->parent = dev;
++
++ 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;
++
++ platform_set_drvdata(pdev, cas);
++ return devm_gpiochip_add_data(dev, &cas->gpio_chip, NULL);
++}
++
++static const struct of_device_id gpio_cascade_id[] = {
++ { .compatible = "gpio-cascade" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, gpio_cascade_id);
++
++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);
++
++MODULE_AUTHOR("Mauri Sandberg <maukka@ext.kapsi.fi>");
++MODULE_DESCRIPTION("Generic GPIO cascade");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch b/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
new file mode 100644
index 0000000000..9b111050ee
--- /dev/null
+++ b/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
@@ -0,0 +1,47 @@
+From 0e71cac033bb7689c4dfa2e6814191337ef770f5 Mon Sep 17 00:00:00 2001
+From: INAGAKI Hiroshi <musashino.open@gmail.com>
+Date: Thu, 13 Oct 2022 00:51:33 +0900
+Subject: [PATCH] nvmem: u-boot-env: align endianness of crc32 values
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch fixes crc32 error on Big-Endianness system by conversion of
+calculated crc32 value.
+
+Little-Endianness system:
+
+ obtained crc32: Little
+calculated crc32: Little
+
+Big-Endianness system:
+
+ obtained crc32: Little
+calculated crc32: Big
+
+log (APRESIA ApresiaLightGS120GT-SS, RTL8382M, Big-Endianness):
+
+[ 8.570000] u_boot_env 18001200.spi:flash@0:partitions:partition@c0000: Invalid calculated CRC32: 0x88cd6f09 (expected: 0x096fcd88)
+[ 8.580000] u_boot_env: probe of 18001200.spi:flash@0:partitions:partition@c0000 failed with error -22
+
+Fixes: f955dc1445069 ("nvmem: add driver handling U-Boot environment variables")
+
+Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
+Acked-by: Rafał Miłecki <rafal@milecki.pl>
+Tested-by: Christian Lamparter <chunkeey@gmail.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+---
+ drivers/nvmem/u-boot-env.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -182,7 +182,7 @@ static int u_boot_env_parse(struct u_boo
+ crc32_data_len = priv->mtd->size - crc32_data_offset;
+ data_len = priv->mtd->size - data_offset;
+
+- calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
++ calc = le32_to_cpu((__le32)crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L);
+ if (calc != crc32) {
+ dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
+ err = -EINVAL;
diff --git a/target/linux/generic/pending-6.1/810-pci_disable_common_quirks.patch b/target/linux/generic/pending-6.1/810-pci_disable_common_quirks.patch
new file mode 100644
index 0000000000..7edbd94f76
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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.
++
++
+ 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 */
++
+ /*
+ * 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
++
+ /*
+ * 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 */
++
+ /*
+ * PCI devices which are on Intel chips can skip the 10ms delay
+ * before entering D3 mode.
diff --git a/target/linux/generic/pending-6.1/811-pci_disable_usb_common_quirks.patch b/target/linux/generic/pending-6.1/811-pci_disable_usb_common_quirks.patch
new file mode 100644
index 0000000000..b498d9f700
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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
++
+ 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 IS_ENABLED(CONFIG_USB_UHCI_HCD)
++
+ /*
+ * 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;
++}
++
++#endif
+ EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
+
++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
++
+ 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 */
++
++#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)
++{
++ return false;
++}
+ 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
+ {
+ 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 */
+
+ #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);
+
++#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
+
+ #ifdef CONFIG_PM
+ extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
diff --git a/target/linux/generic/pending-6.1/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch b/target/linux/generic/pending-6.1/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-6.1/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;
+
+- 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-6.1/834-ledtrig-libata.patch b/target/linux/generic/pending-6.1/834-ledtrig-libata.patch
new file mode 100644
index 0000000000..9c85862a4b
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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
+
+ If unsure, say Y.
+
++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 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;
++
++ 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;
+ }
++#ifdef CONFIG_ATA_LEDS
++ ata_led_act(ap);
++#endif
+
+ 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);
+
+ return ap;
+@@ -5393,6 +5412,12 @@ static void ata_host_release(struct kref
+
+ 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;
+
++ 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;
++ }
++ }
++#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
+@@ -894,6 +897,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-6.1/840-hwrng-bcm2835-set-quality-to-1000.patch b/target/linux/generic/pending-6.1/840-hwrng-bcm2835-set-quality-to-1000.patch
new file mode 100644
index 0000000000..5ca8933d6f
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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
+
+This allows devices without a high precission timer to reduce boot from >100s
+to <30s.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+---
+ 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;
+
+ if (dev_of_node(dev)) {
+ rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node);
diff --git a/target/linux/generic/pending-6.1/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/target/linux/generic/pending-6.1/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch
new file mode 100644
index 0000000000..e180a385e1
--- /dev/null
+++ b/target/linux/generic/pending-6.1/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).
+
+[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: Marek Behún <kabel@kernel.org>
+---
+ 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 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)
+ {
+ struct advk_pcie *pcie = h->host_data;
+
+ 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);
+
+ 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;
+
+ raw_spin_lock_init(&pcie->irq_lock);
+@@ -1422,28 +1425,14 @@ static int advk_pcie_init_irq_domain(str
+ return -ENODEV;
+ }
+
+- 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;
+ }
diff --git a/target/linux/generic/pending-6.1/850-dt-bindings-clk-add-BCM63268-timer-clock-definitions.patch b/target/linux/generic/pending-6.1/850-dt-bindings-clk-add-BCM63268-timer-clock-definitions.patch
new file mode 100644
index 0000000000..cc6f1e0d9d
--- /dev/null
+++ b/target/linux/generic/pending-6.1/850-dt-bindings-clk-add-BCM63268-timer-clock-definitions.patch
@@ -0,0 +1,114 @@
+From patchwork Wed Mar 22 17:15:12 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
+ <noltari@gmail.com>
+X-Patchwork-Id: 13184389
+Return-Path: <linux-clk-owner@vger.kernel.org>
+X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
+ aws-us-west-2-korg-lkml-1.web.codeaurora.org
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by smtp.lore.kernel.org (Postfix) with ESMTP id 73F2DC6FD1C
+ for <linux-clk@archiver.kernel.org>; Wed, 22 Mar 2023 17:15:59 +0000 (UTC)
+Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
+ id S231363AbjCVRP5 (ORCPT <rfc822;linux-clk@archiver.kernel.org>);
+ Wed, 22 Mar 2023 13:15:57 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58824 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S231408AbjCVRPy (ORCPT
+ <rfc822;linux-clk@vger.kernel.org>); Wed, 22 Mar 2023 13:15:54 -0400
+Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com
+ [IPv6:2a00:1450:4864:20::32d])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 70E4C64B28;
+ Wed, 22 Mar 2023 10:15:24 -0700 (PDT)
+Received: by mail-wm1-x32d.google.com with SMTP id n19so1740892wms.0;
+ Wed, 22 Mar 2023 10:15:24 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=gmail.com; s=20210112; t=1679505322;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:from:to:cc:subject:date
+ :message-id:reply-to;
+ bh=dEknM98Izmc8d/crPsoJ+ejZxfl78958Ei6SPYhYDHs=;
+ b=LTOQ75W3s5nYo+nEfiJAKqytSopONB4jCtU3zRygzPMasugVOrYFMsUR+WrpsAjuRT
+ v4HgWpJxEsIWeRXrUN9W21mFXhGgJLJXSxRnrio0CsZZBNMdkebbNOphgKXIWAdm+2iM
+ PzqAdGm5t38wT2mmm6V/9hCy90+12raHM82tNFdhhiezfg2cukVOKP3j/TeOVCwas0gQ
+ iFc+CuZB6y73zYXvMUMUpTsqI5vev4xJsSMHIQJVmUxJAwqhOBhN9JCRo7Ao+wayjn2d
+ Fxo6AV3A8v68nVfoQ0K0I+eWXG48nMCX45iWh/lVvVTOFcR99kn4va7NY1oVnPsh+WQz
+ WcLA==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20210112; t=1679505322;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
+ :subject:date:message-id:reply-to;
+ bh=dEknM98Izmc8d/crPsoJ+ejZxfl78958Ei6SPYhYDHs=;
+ b=wv2NSR1B5RnsdoEE7mgJSHAfSs1JHZbQ1HPMldyaGWAk1dcucqh/uDzM3Flz+ADRi1
+ 19NoaB2Ur7QaWZejbuplnIOK/nte3PnmqJ9ZNw8HejmuS4eU8mB1V1aJUSKSPGsfUi4a
+ LYe3HSw87l0jrAC7ptdKvdUtzBoIkX0CeFvfguTQQkDhUTyAFIG144hY6uPXY9Mga96b
+ gnNe2dLCzHQLbEJpaDaavT7FEEcLDxaq7jNcR2xqEEZaIwfcew+Q05t4xL/3i8GAj9Ru
+ 6ivQjIbBKfYQF88o7KnOW9o1wjrGsk+Nd4Iy0OLZix3JQasCJGrKV7ib5awI9J39upYV
+ fa4A==
+X-Gm-Message-State: AO0yUKWw75I1M5Vjrd4vXq4GTruQu0H84pycgyi2CT3bczTYRJpWmEWg
+ +bHDhvp1n5IWW85GI9vKWpbclB13a/S0RQ==
+X-Google-Smtp-Source:
+ AK7set9T/2oJsVetUb2L4mPEWu8YqDrnK8EzHK5bJf1ABIa1Et8f7BFJ7AA3j14ITZuf8cH0HqlRtg==
+X-Received: by 2002:a05:600c:2304:b0:3ed:2949:985b with SMTP id
+ 4-20020a05600c230400b003ed2949985bmr206833wmo.23.1679505322457;
+ Wed, 22 Mar 2023 10:15:22 -0700 (PDT)
+Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
+ [79.146.124.255])
+ by smtp.gmail.com with ESMTPSA id
+ v10-20020a05600c470a00b003ee11ac2288sm8414333wmo.21.2023.03.22.10.15.21
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Wed, 22 Mar 2023 10:15:22 -0700 (PDT)
+From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+To: mturquette@baylibre.com, sboyd@kernel.org, robh+dt@kernel.org,
+ krzysztof.kozlowski+dt@linaro.org, p.zabel@pengutronix.de,
+ f.fainelli@gmail.com, jonas.gorski@gmail.com,
+ william.zhang@broadcom.com, linux-clk@vger.kernel.org,
+ devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
+Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>,
+ Rob Herring <robh@kernel.org>
+Subject: [PATCH v4 1/4] dt-bindings: clk: add BCM63268 timer clock definitions
+Date: Wed, 22 Mar 2023 18:15:12 +0100
+Message-Id: <20230322171515.120353-2-noltari@gmail.com>
+X-Mailer: git-send-email 2.30.2
+In-Reply-To: <20230322171515.120353-1-noltari@gmail.com>
+References: <20230322171515.120353-1-noltari@gmail.com>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <linux-clk.vger.kernel.org>
+X-Mailing-List: linux-clk@vger.kernel.org
+
+Add missing timer clock definitions for BCM63268.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Acked-by: Rob Herring <robh@kernel.org>
+---
+ v4: no changes
+ v3: no changes
+ v2: change commit title, as suggested by Stephen Boyd
+
+ include/dt-bindings/clock/bcm63268-clock.h | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/include/dt-bindings/clock/bcm63268-clock.h
++++ b/include/dt-bindings/clock/bcm63268-clock.h
+@@ -27,4 +27,17 @@
+ #define BCM63268_CLK_TBUS 27
+ #define BCM63268_CLK_ROBOSW250 31
+
++#define BCM63268_TCLK_EPHY1 0
++#define BCM63268_TCLK_EPHY2 1
++#define BCM63268_TCLK_EPHY3 2
++#define BCM63268_TCLK_GPHY1 3
++#define BCM63268_TCLK_DSL 4
++#define BCM63268_TCLK_WAKEON_EPHY 6
++#define BCM63268_TCLK_WAKEON_DSL 7
++#define BCM63268_TCLK_FAP1 11
++#define BCM63268_TCLK_FAP2 15
++#define BCM63268_TCLK_UTO_50 16
++#define BCM63268_TCLK_UTO_EXTIN 17
++#define BCM63268_TCLK_USB_REF 18
++
+ #endif /* __DT_BINDINGS_CLOCK_BCM63268_H */
diff --git a/target/linux/generic/pending-6.1/851-dt-bindings-reset-add-BCM63268-timer-reset-definitions.patch b/target/linux/generic/pending-6.1/851-dt-bindings-reset-add-BCM63268-timer-reset-definitions.patch
new file mode 100644
index 0000000000..5f1be105ac
--- /dev/null
+++ b/target/linux/generic/pending-6.1/851-dt-bindings-reset-add-BCM63268-timer-reset-definitions.patch
@@ -0,0 +1,107 @@
+From patchwork Wed Mar 22 17:15:13 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
+ <noltari@gmail.com>
+X-Patchwork-Id: 13184390
+Return-Path: <linux-clk-owner@vger.kernel.org>
+X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
+ aws-us-west-2-korg-lkml-1.web.codeaurora.org
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by smtp.lore.kernel.org (Postfix) with ESMTP id D0B1AC6FD1C
+ for <linux-clk@archiver.kernel.org>; Wed, 22 Mar 2023 17:16:08 +0000 (UTC)
+Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
+ id S231472AbjCVRQI (ORCPT <rfc822;linux-clk@archiver.kernel.org>);
+ Wed, 22 Mar 2023 13:16:08 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58934 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S231435AbjCVRP5 (ORCPT
+ <rfc822;linux-clk@vger.kernel.org>); Wed, 22 Mar 2023 13:15:57 -0400
+Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com
+ [IPv6:2a00:1450:4864:20::329])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9655064863;
+ Wed, 22 Mar 2023 10:15:25 -0700 (PDT)
+Received: by mail-wm1-x329.google.com with SMTP id
+ v4-20020a05600c470400b003ee4f06428fso2424553wmo.4;
+ Wed, 22 Mar 2023 10:15:25 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=gmail.com; s=20210112; t=1679505324;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:from:to:cc:subject:date
+ :message-id:reply-to;
+ bh=C7ykhArT1dO7P8wtmI92eo4c7KtPZI9w182/5+cB3T0=;
+ b=WRZRU2SM9n1LfUj4SgTPfQczADC2pfvoIrOsNpBLTym2eOfmkTetb/WbGSla5kw2Wb
+ SH5MIC2fFeScJg6T5FFAUOOLmRVW9xvl8Q3T3NKb3z/9wvPHO767nrdIbffRWMJFs7gW
+ wT/kuTpn8GYdfY0sZ/dMTkq41DVusEkxfX6GxtG85O98ZP8xMHQog8aPs9fRfUvI5ZKB
+ eGYcRz/Wn1cHhjey9jtWzQEEmZ/BT3b0HQTF9Tl88oofhiEgbyjFXr91+vRsLbsJpGXH
+ /1FjjaLG5DnonKubV9rmbuCU8KzwH331gi2KuRjvLD2V+OMewqSa5i+GvgVv8x2zC8y+
+ /mLQ==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20210112; t=1679505324;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
+ :subject:date:message-id:reply-to;
+ bh=C7ykhArT1dO7P8wtmI92eo4c7KtPZI9w182/5+cB3T0=;
+ b=bhd0fNh0jDOMlGSC4F+p5igV8AUlGEPj2cXUwgdgqRfSSuUy9z+Li8cT0MbY/aWH5Z
+ qInRVA+R1cWV3ubrDyKag6oEc0LDU234bnMFcP9b7MRlrM8Dpit9TFSyqJU4sDUWNDs5
+ KOe2k/SNIdat6munC9VOuEBDO0eB/UDMN+repKwXNdHChp/Toq9qMvW4Uy8uHxosbQlD
+ 8P88GbKFjynb1E8I8croGjfub7+y8PPsWB0xNUcafIv6xs3MnVOP1Mk4KwBCbqS509la
+ mfjsriXtIybO8XFqtn100ungjvbFWdogEplLdSPVdgAqdfF5J8gHxAoApoeYejYkL5/R
+ kOhQ==
+X-Gm-Message-State: AO0yUKWdzr3dMmjKhD8tF+ec4Dfdq9VGZ/WCU4d85npKQvxSwhNPZZ1J
+ 5WYRIqivh0suFC1OqEidwenpiJYvXedYjw==
+X-Google-Smtp-Source:
+ AK7set87ew2/mKWeShXTTW/YBbBJNR2zeGFV0CfuqLXhiJEU6tqFuyKcW+vFEoKHIbNUS8wRy1SzLA==
+X-Received: by 2002:a05:600c:290:b0:3ee:6d88:774a with SMTP id
+ 16-20020a05600c029000b003ee6d88774amr160734wmk.14.1679505323514;
+ Wed, 22 Mar 2023 10:15:23 -0700 (PDT)
+Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
+ [79.146.124.255])
+ by smtp.gmail.com with ESMTPSA id
+ v10-20020a05600c470a00b003ee11ac2288sm8414333wmo.21.2023.03.22.10.15.22
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Wed, 22 Mar 2023 10:15:23 -0700 (PDT)
+From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+To: mturquette@baylibre.com, sboyd@kernel.org, robh+dt@kernel.org,
+ krzysztof.kozlowski+dt@linaro.org, p.zabel@pengutronix.de,
+ f.fainelli@gmail.com, jonas.gorski@gmail.com,
+ william.zhang@broadcom.com, linux-clk@vger.kernel.org,
+ devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
+Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>,
+ Rob Herring <robh@kernel.org>
+Subject: [PATCH v4 2/4] dt-bindings: reset: add BCM63268 timer reset
+ definitions
+Date: Wed, 22 Mar 2023 18:15:13 +0100
+Message-Id: <20230322171515.120353-3-noltari@gmail.com>
+X-Mailer: git-send-email 2.30.2
+In-Reply-To: <20230322171515.120353-1-noltari@gmail.com>
+References: <20230322171515.120353-1-noltari@gmail.com>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <linux-clk.vger.kernel.org>
+X-Mailing-List: linux-clk@vger.kernel.org
+
+Add missing timer reset definitions for BCM63268.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+Acked-by: Rob Herring <robh@kernel.org>
+---
+ v4: no changes
+ v3: no changes
+ v2: change commit title, as suggested by Stephen Boyd
+
+ include/dt-bindings/reset/bcm63268-reset.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/include/dt-bindings/reset/bcm63268-reset.h
++++ b/include/dt-bindings/reset/bcm63268-reset.h
+@@ -23,4 +23,8 @@
+ #define BCM63268_RST_PCIE_HARD 17
+ #define BCM63268_RST_GPHY 18
+
++#define BCM63268_TRST_SW 29
++#define BCM63268_TRST_HW 30
++#define BCM63268_TRST_POR 31
++
+ #endif /* __DT_BINDINGS_RESET_BCM63268_H */
diff --git a/target/linux/generic/pending-6.1/852-clk-bcm-Add-BCM63268-timer-clock-and-reset-driver.patch b/target/linux/generic/pending-6.1/852-clk-bcm-Add-BCM63268-timer-clock-and-reset-driver.patch
new file mode 100644
index 0000000000..7e500cd1b5
--- /dev/null
+++ b/target/linux/generic/pending-6.1/852-clk-bcm-Add-BCM63268-timer-clock-and-reset-driver.patch
@@ -0,0 +1,345 @@
+From patchwork Wed Mar 22 17:15:15 2023
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
+ <noltari@gmail.com>
+X-Patchwork-Id: 13184392
+Return-Path: <linux-clk-owner@vger.kernel.org>
+X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
+ aws-us-west-2-korg-lkml-1.web.codeaurora.org
+Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
+ by smtp.lore.kernel.org (Postfix) with ESMTP id 199D9C76196
+ for <linux-clk@archiver.kernel.org>; Wed, 22 Mar 2023 17:16:11 +0000 (UTC)
+Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
+ id S231512AbjCVRQJ (ORCPT <rfc822;linux-clk@archiver.kernel.org>);
+ Wed, 22 Mar 2023 13:16:09 -0400
+Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58942 "EHLO
+ lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
+ with ESMTP id S231442AbjCVRP5 (ORCPT
+ <rfc822;linux-clk@vger.kernel.org>); Wed, 22 Mar 2023 13:15:57 -0400
+Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com
+ [IPv6:2a00:1450:4864:20::32c])
+ by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1DDB36487D;
+ Wed, 22 Mar 2023 10:15:27 -0700 (PDT)
+Received: by mail-wm1-x32c.google.com with SMTP id
+ i5-20020a05600c354500b003edd24054e0so6717370wmq.4;
+ Wed, 22 Mar 2023 10:15:27 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=gmail.com; s=20210112; t=1679505325;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:from:to:cc:subject:date
+ :message-id:reply-to;
+ bh=rkv/eZYA1ncHp5FnV2ZWc3hgYnAx28S86QA9vmcXFCY=;
+ b=Y1mva2Bt3sUbKxLgEUS331CJbGxUc4z8kTQW8qiHWGhYlFKtm+d5z4sT40E5BeZAnU
+ zmTbCI7jbroe9NYBxGUmSli6LNVDPjND80ChbhWTqbqMQTmeQFWut9KmeBWK6Oze2lC/
+ XMSOorUzowjcU2xtHNrzoq2KH2pstW573lsB8WnzFVfhMaRkE9DfRr6WNyA7zC8DyxM5
+ ezxlCQtCmgPfCqlyksbIDKrgrRf3GiUR0yUd6xRU+MssyvH1FkYGDCerPctDto6lGHBz
+ 8Y15jT3l6OnQMT6dkekgpPF5/XrSUY93u9g0B4U8+0dhNj+K7vmDen+jqdess+tpLnq/
+ gFrA==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20210112; t=1679505325;
+ h=content-transfer-encoding:mime-version:references:in-reply-to
+ :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
+ :subject:date:message-id:reply-to;
+ bh=rkv/eZYA1ncHp5FnV2ZWc3hgYnAx28S86QA9vmcXFCY=;
+ b=Ym4+u8bbTQGNkewUBrLf+89vE0EFJBQp2f1crwUxZFboKTROF9ltZonY1CGepo7b0B
+ fkx3TbWQy5X65g3ScuieqtClCI8WanPeNBJ48+JipJYO3ODVNBxnVaTuW/0FOIcahfqe
+ sG5GvggHhzRz+Yeybsbnupmzxnw8Ez0BpMl3p7zcjHL7BGZDdOOX2Zbw3zfyYa5sg2nX
+ UXYJT36zy2h39gxUsy9QkhQ76CG3w6omniohZpYidpojpiDjbOy0nKFky4kUe+YyA1fF
+ 4IBhjAm6mH+uh6wHSG1qj+NAXHs0xDDJps16PbJwAgL7Qt9K5WW+R/UAYPmHFgaRIHOw
+ /seA==
+X-Gm-Message-State: AO0yUKXRtoYO8Nfus6Ca8lhM39P1Xn6TGkhatEfoISd1YNOkTJJN2hW+
+ xRphLgxlzNfCLcVPlpGK9dk=
+X-Google-Smtp-Source:
+ AK7set9VnMEykugk8ZYnkXuqK41bX1dzlvKsAXHEjr8i2NZBld0buKhQLcGYEcwxnBgVTtC7eRGfXw==
+X-Received: by 2002:a1c:7c0b:0:b0:3e2:1dac:b071 with SMTP id
+ x11-20020a1c7c0b000000b003e21dacb071mr178053wmc.13.1679505325582;
+ Wed, 22 Mar 2023 10:15:25 -0700 (PDT)
+Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
+ [79.146.124.255])
+ by smtp.gmail.com with ESMTPSA id
+ v10-20020a05600c470a00b003ee11ac2288sm8414333wmo.21.2023.03.22.10.15.24
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Wed, 22 Mar 2023 10:15:25 -0700 (PDT)
+From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+To: mturquette@baylibre.com, sboyd@kernel.org, robh+dt@kernel.org,
+ krzysztof.kozlowski+dt@linaro.org, p.zabel@pengutronix.de,
+ f.fainelli@gmail.com, jonas.gorski@gmail.com,
+ william.zhang@broadcom.com, linux-clk@vger.kernel.org,
+ devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
+Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
+Subject: [PATCH v4 4/4] clk: bcm: Add BCM63268 timer clock and reset driver
+Date: Wed, 22 Mar 2023 18:15:15 +0100
+Message-Id: <20230322171515.120353-5-noltari@gmail.com>
+X-Mailer: git-send-email 2.30.2
+In-Reply-To: <20230322171515.120353-1-noltari@gmail.com>
+References: <20230322171515.120353-1-noltari@gmail.com>
+MIME-Version: 1.0
+Precedence: bulk
+List-ID: <linux-clk.vger.kernel.org>
+X-Mailing-List: linux-clk@vger.kernel.org
+
+Add driver for BCM63268 timer clock and reset controller.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+---
+ v4: add changes suggested by Stephen Boyd:
+ - Usage of of_device_get_match_data() isn't needed.
+ - Use devm_clk_hw_register_gate().
+ - Drop clk_hw_unregister_gate().
+ v3: add missing <linux/io.h> include to fix build warning
+ v2: add changes suggested by Stephen Boyd
+
+ drivers/clk/bcm/Kconfig | 9 ++
+ drivers/clk/bcm/Makefile | 1 +
+ drivers/clk/bcm/clk-bcm63268-timer.c | 215 +++++++++++++++++++++++++++
+ 3 files changed, 225 insertions(+)
+ create mode 100644 drivers/clk/bcm/clk-bcm63268-timer.c
+
+--- a/drivers/clk/bcm/Kconfig
++++ b/drivers/clk/bcm/Kconfig
+@@ -37,6 +37,15 @@ config CLK_BCM_63XX_GATE
+ Enable common clock framework support for Broadcom BCM63xx DSL SoCs
+ based on the MIPS architecture
+
++config CLK_BCM63268_TIMER
++ bool "Broadcom BCM63268 timer clock and reset support"
++ depends on BMIPS_GENERIC || COMPILE_TEST
++ default BMIPS_GENERIC
++ select RESET_CONTROLLER
++ help
++ Enable timer clock and reset support for Broadcom BCM63268 DSL SoCs
++ based on the MIPS architecture.
++
+ config CLK_BCM_KONA
+ bool "Broadcom Kona CCU clock support"
+ depends on ARCH_BCM_MOBILE || COMPILE_TEST
+--- a/drivers/clk/bcm/Makefile
++++ b/drivers/clk/bcm/Makefile
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-$(CONFIG_CLK_BCM_63XX) += clk-bcm63xx.o
+ obj-$(CONFIG_CLK_BCM_63XX_GATE) += clk-bcm63xx-gate.o
++obj-$(CONFIG_CLK_BCM63268_TIMER) += clk-bcm63268-timer.o
+ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o
+ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
+ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
+--- /dev/null
++++ b/drivers/clk/bcm/clk-bcm63268-timer.c
+@@ -0,0 +1,215 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * BCM63268 Timer Clock and Reset Controller Driver
++ *
++ * Copyright (C) 2023 Álvaro Fernández Rojas <noltari@gmail.com>
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/reset-controller.h>
++
++#include <dt-bindings/clock/bcm63268-clock.h>
++
++#define BCM63268_TIMER_RESET_SLEEP_MIN_US 10000
++#define BCM63268_TIMER_RESET_SLEEP_MAX_US 20000
++
++struct bcm63268_tclkrst_hw {
++ void __iomem *regs;
++ spinlock_t lock;
++
++ struct reset_controller_dev rcdev;
++ struct clk_hw_onecell_data data;
++};
++
++struct bcm63268_tclk_table_entry {
++ const char * const name;
++ u8 bit;
++};
++
++static const struct bcm63268_tclk_table_entry bcm63268_timer_clocks[] = {
++ {
++ .name = "ephy1",
++ .bit = BCM63268_TCLK_EPHY1,
++ }, {
++ .name = "ephy2",
++ .bit = BCM63268_TCLK_EPHY2,
++ }, {
++ .name = "ephy3",
++ .bit = BCM63268_TCLK_EPHY3,
++ }, {
++ .name = "gphy1",
++ .bit = BCM63268_TCLK_GPHY1,
++ }, {
++ .name = "dsl",
++ .bit = BCM63268_TCLK_DSL,
++ }, {
++ .name = "wakeon_ephy",
++ .bit = BCM63268_TCLK_WAKEON_EPHY,
++ }, {
++ .name = "wakeon_dsl",
++ .bit = BCM63268_TCLK_WAKEON_DSL,
++ }, {
++ .name = "fap1_pll",
++ .bit = BCM63268_TCLK_FAP1,
++ }, {
++ .name = "fap2_pll",
++ .bit = BCM63268_TCLK_FAP2,
++ }, {
++ .name = "uto_50",
++ .bit = BCM63268_TCLK_UTO_50,
++ }, {
++ .name = "uto_extin",
++ .bit = BCM63268_TCLK_UTO_EXTIN,
++ }, {
++ .name = "usb_ref",
++ .bit = BCM63268_TCLK_USB_REF,
++ }, {
++ /* sentinel */
++ }
++};
++
++static inline struct bcm63268_tclkrst_hw *
++to_bcm63268_timer_reset(struct reset_controller_dev *rcdev)
++{
++ return container_of(rcdev, struct bcm63268_tclkrst_hw, rcdev);
++}
++
++static int bcm63268_timer_reset_update(struct reset_controller_dev *rcdev,
++ unsigned long id, bool assert)
++{
++ struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev);
++ unsigned long flags;
++ uint32_t val;
++
++ spin_lock_irqsave(&reset->lock, flags);
++ val = __raw_readl(reset->regs);
++ if (assert)
++ val &= ~BIT(id);
++ else
++ val |= BIT(id);
++ __raw_writel(val, reset->regs);
++ spin_unlock_irqrestore(&reset->lock, flags);
++
++ return 0;
++}
++
++static int bcm63268_timer_reset_assert(struct reset_controller_dev *rcdev,
++ unsigned long id)
++{
++ return bcm63268_timer_reset_update(rcdev, id, true);
++}
++
++static int bcm63268_timer_reset_deassert(struct reset_controller_dev *rcdev,
++ unsigned long id)
++{
++ return bcm63268_timer_reset_update(rcdev, id, false);
++}
++
++static int bcm63268_timer_reset_reset(struct reset_controller_dev *rcdev,
++ unsigned long id)
++{
++ bcm63268_timer_reset_update(rcdev, id, true);
++ usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US,
++ BCM63268_TIMER_RESET_SLEEP_MAX_US);
++
++ bcm63268_timer_reset_update(rcdev, id, false);
++ /*
++ * Ensure component is taken out reset state by sleeping also after
++ * deasserting the reset. Otherwise, the component may not be ready
++ * for operation.
++ */
++ usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US,
++ BCM63268_TIMER_RESET_SLEEP_MAX_US);
++
++ return 0;
++}
++
++static int bcm63268_timer_reset_status(struct reset_controller_dev *rcdev,
++ unsigned long id)
++{
++ struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev);
++
++ return !(__raw_readl(reset->regs) & BIT(id));
++}
++
++static struct reset_control_ops bcm63268_timer_reset_ops = {
++ .assert = bcm63268_timer_reset_assert,
++ .deassert = bcm63268_timer_reset_deassert,
++ .reset = bcm63268_timer_reset_reset,
++ .status = bcm63268_timer_reset_status,
++};
++
++static int bcm63268_tclk_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ const struct bcm63268_tclk_table_entry *entry;
++ struct bcm63268_tclkrst_hw *hw;
++ struct clk_hw *clk;
++ u8 maxbit = 0;
++ int i, ret;
++
++ for (entry = bcm63268_timer_clocks; entry->name; entry++)
++ maxbit = max(maxbit, entry->bit);
++ maxbit++;
++
++ hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit),
++ GFP_KERNEL);
++ if (!hw)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, hw);
++
++ spin_lock_init(&hw->lock);
++
++ hw->data.num = maxbit;
++ for (i = 0; i < maxbit; i++)
++ hw->data.hws[i] = ERR_PTR(-ENODEV);
++
++ hw->regs = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(hw->regs))
++ return PTR_ERR(hw->regs);
++
++ for (entry = bcm63268_timer_clocks; entry->name; entry++) {
++ clk = devm_clk_hw_register_gate(dev, entry->name, NULL, 0,
++ hw->regs, entry->bit,
++ CLK_GATE_BIG_ENDIAN,
++ &hw->lock);
++ if (IS_ERR(clk))
++ return PTR_ERR(clk);
++
++ hw->data.hws[entry->bit] = clk;
++ }
++
++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
++ &hw->data);
++ if (ret)
++ return ret;
++
++ hw->rcdev.of_node = dev->of_node;
++ hw->rcdev.ops = &bcm63268_timer_reset_ops;
++
++ ret = devm_reset_controller_register(dev, &hw->rcdev);
++ if (ret)
++ dev_err(dev, "Failed to register reset controller\n");
++
++ return 0;
++}
++
++static const struct of_device_id bcm63268_tclk_dt_ids[] = {
++ { .compatible = "brcm,bcm63268-timer-clocks" },
++ { /* sentinel */ }
++};
++
++static struct platform_driver bcm63268_tclk = {
++ .probe = bcm63268_tclk_probe,
++ .driver = {
++ .name = "bcm63268-timer-clock",
++ .of_match_table = bcm63268_tclk_dt_ids,
++ },
++};
++builtin_platform_driver(bcm63268_tclk);
diff --git a/target/linux/generic/pending-6.1/920-mangle_bootargs.patch b/target/linux/generic/pending-6.1/920-mangle_bootargs.patch
new file mode 100644
index 0000000000..5f0a106ae4
--- /dev/null
+++ b/target/linux/generic/pending-6.1/920-mangle_bootargs.patch
@@ -0,0 +1,71 @@
+From: Imre Kaloz <kaloz@openwrt.org>
+Subject: init: add CONFIG_MANGLE_BOOTARGS and disable it by default
+
+Enabling this option renames the bootloader supplied root=
+and rootfstype= variables, which might have to be know but
+would break the automatisms OpenWrt uses.
+
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+---
+ init/Kconfig | 9 +++++++++
+ init/main.c | 24 ++++++++++++++++++++++++
+ 2 files changed, 33 insertions(+)
+
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -1814,6 +1814,15 @@ config EMBEDDED
+ an embedded system so certain expert options are available
+ for configuration.
+
++config MANGLE_BOOTARGS
++ bool "Rename offending bootargs"
++ depends on EXPERT
++ help
++ Sometimes the bootloader passed bogus root= and rootfstype=
++ parameters to the kernel, and while you want to ignore them,
++ you need to know the values f.e. to support dual firmware
++ layouts on the flash.
++
+ config HAVE_PERF_EVENTS
+ bool
+ help
+--- a/init/main.c
++++ b/init/main.c
+@@ -616,6 +616,29 @@ static inline void setup_nr_cpu_ids(void
+ static inline void smp_prepare_cpus(unsigned int maxcpus) { }
+ #endif
+
++#ifdef CONFIG_MANGLE_BOOTARGS
++static void __init mangle_bootargs(char *command_line)
++{
++ char *rootdev;
++ char *rootfs;
++
++ rootdev = strstr(command_line, "root=/dev/mtdblock");
++
++ if (rootdev)
++ strncpy(rootdev, "mangled_rootblock=", 18);
++
++ rootfs = strstr(command_line, "rootfstype");
++
++ if (rootfs)
++ strncpy(rootfs, "mangled_fs", 10);
++
++}
++#else
++static void __init mangle_bootargs(char *command_line)
++{
++}
++#endif
++
+ /*
+ * We need to store the untouched command line for future reference.
+ * We also need to store the touched command line since the parameter
+@@ -956,6 +979,7 @@ asmlinkage __visible void __init __no_sa
+ pr_notice("%s", linux_banner);
+ early_security_init();
+ setup_arch(&command_line);
++ mangle_bootargs(command_line);
+ setup_boot_config();
+ setup_command_line(command_line);
+ setup_nr_cpu_ids();