aboutsummaryrefslogtreecommitdiff
path: root/pkgs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs')
-rw-r--r--pkgs/default.nix13
-rw-r--r--pkgs/patches-linux-5.15/0001-PCI-mvebu-Replace-pci_ioremap_io-usage-by-devm_pci_r.patch62
-rw-r--r--pkgs/patches-linux-5.15/0002-PCI-mvebu-Remove-custom-mvebu_pci_host_probe-functio.patch82
-rw-r--r--pkgs/patches-linux-5.15/0003-arm-ioremap-Replace-pci_ioremap_io-usage-by-pci_rema.patch170
-rw-r--r--pkgs/patches-linux-5.15/0004-arm-ioremap-Remove-unused-ARM-specific-function-pci_.patch55
-rw-r--r--pkgs/patches-linux-5.15/0005-bus-mvebu-mbus-Export-symbols-for-public-API-window-.patch63
-rw-r--r--pkgs/patches-linux-5.15/0006-PCI-mvebu-Add-support-for-compiling-driver-as-module.patch175
-rw-r--r--pkgs/patches-linux-5.15/0007-PCI-mvebu-Check-for-valid-ports.patch54
-rw-r--r--pkgs/patches-linux-5.15/0008-PCI-mvebu-Check-that-PCI-bridge-specified-in-DT-has-.patch38
-rw-r--r--pkgs/patches-linux-5.15/0009-PCI-mvebu-Handle-invalid-size-of-read-config-request.patch35
-rw-r--r--pkgs/patches-linux-5.15/0010-PCI-mvebu-Disallow-mapping-interrupts-on-emulated-br.patch51
-rw-r--r--pkgs/patches-linux-5.15/0011-PCI-mvebu-Propagate-errors-when-updating-PCI_IO_BASE.patch195
-rw-r--r--pkgs/patches-linux-5.15/0012-PCI-mvebu-Set-PCI-Bridge-Class-Code-to-PCI-Bridge.patch73
-rw-r--r--pkgs/patches-linux-5.15/0013-MAINTAINERS-Add-Pali-Roh-r-as-pci-mvebu.c-maintainer.patch30
-rw-r--r--pkgs/patches-linux-5.15/0014-PCI-pci-bridge-emul-Make-struct-pci_bridge_emul_ops-.patch61
-rw-r--r--pkgs/patches-linux-5.15/0015-PCI-pci-bridge-emul-Rename-PCI_BRIDGE_EMUL_NO_PREFET.patch69
-rw-r--r--pkgs/patches-linux-5.15/0016-PCI-pci-bridge-emul-Add-support-for-new-flag-PCI_BRI.patch62
-rw-r--r--pkgs/patches-linux-5.15/0017-PCI-mvebu-Add-help-string-for-CONFIG_PCI_MVEBU-optio.patch34
-rw-r--r--pkgs/patches-linux-5.15/0018-PCI-mvebu-Remove-duplicate-nports-assignment.patch33
-rw-r--r--pkgs/patches-linux-5.15/0019-PCI-mvebu-Set-PCI_BRIDGE_EMUL_NO_IO_FORWARD-when-IO-.patch114
-rw-r--r--pkgs/patches-linux-5.15/0020-PCI-mvebu-Properly-initialize-vendor-device-and-revi.patch42
-rw-r--r--pkgs/patches-linux-5.15/0021-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCAP-register.patch34
-rw-r--r--pkgs/patches-linux-5.15/0022-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCTL-register.patch39
-rw-r--r--pkgs/patches-linux-5.15/0023-PCI-mvebu-Fix-reporting-Data-Link-Layer-Link-Active-.patch50
-rw-r--r--pkgs/patches-linux-5.15/0024-PCI-pci-bridge-emul-Re-arrange-register-tests.patch117
-rw-r--r--pkgs/patches-linux-5.15/0025-PCI-pci-bridge-emul-Add-support-for-PCIe-extended-ca.patch179
-rw-r--r--pkgs/patches-linux-5.15/0026-PCI-pci-bridge-emul-Add-support-for-PCI-Bridge-Subsy.patch169
-rw-r--r--pkgs/patches-linux-5.15/0027-dt-bindings-PCI-mvebu-Add-num-lanes-property.patch114
-rw-r--r--pkgs/patches-linux-5.15/0028-PCI-mvebu-Correctly-configure-x1-x4-mode.patch78
-rw-r--r--pkgs/patches-linux-5.15/0029-PCI-mvebu-Add-support-for-PCI-Bridge-Subsystem-Vendo.patch53
-rw-r--r--pkgs/patches-linux-5.15/0030-PCI-mvebu-Add-support-for-Advanced-Error-Reporting-r.patch125
-rw-r--r--pkgs/patches-linux-5.15/0031-PCI-mvebu-Use-child_ops-API.patch165
-rw-r--r--pkgs/patches-linux-5.15/0032-dt-bindings-PCI-mvebu-Update-information-about-intx-.patch34
-rw-r--r--pkgs/patches-linux-5.15/0033-PCI-mvebu-Fix-macro-names-and-comments-about-legacy-.patch79
-rw-r--r--pkgs/patches-linux-5.15/0034-PCI-mvebu-Implement-support-for-legacy-INTx-interrup.patch251
-rw-r--r--pkgs/patches-linux-5.15/0035-ARM-dts-kirkwood-Add-definitions-for-PCIe-legacy-INT.patch161
-rw-r--r--pkgs/patches-linux-5.15/0036-ARM-dts-dove-Add-definitions-for-PCIe-legacy-INTx-in.patch63
-rw-r--r--pkgs/patches-linux-5.15/0037-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-leg.patch79
-rw-r--r--pkgs/patches-linux-5.15/0038-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch50
-rw-r--r--pkgs/patches-linux-5.15/0039-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch166
-rw-r--r--pkgs/patches-linux-5.15/0040-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch282
-rw-r--r--pkgs/patches-linux-5.15/0041-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch311
-rw-r--r--pkgs/patches-linux-5.15/0042-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-leg.patch79
-rw-r--r--pkgs/patches-linux-5.15/0043-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-leg.patch108
-rw-r--r--pkgs/patches-linux-5.15/0044-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-leg.patch138
-rw-r--r--pkgs/patches-linux-5.15/0045-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-leg.patch137
-rw-r--r--pkgs/patches-linux-5.15/0046-PCI-Add-PCI_EXP_SLTCTL_ASPL_DISABLE-macro.patch33
-rw-r--r--pkgs/patches-linux-5.15/0047-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch46
-rw-r--r--pkgs/patches-linux-5.15/0048-PCI-Add-function-for-parsing-slot-power-limit-milliw.patch135
-rw-r--r--pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch210
-rw-r--r--pkgs/patches-linux-5.15/0050-ARM-dts-turris-omnia-Set-PCIe-slot-power-limit-milli.patch43
-rw-r--r--pkgs/patches-linux-5.15/0051-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch164
-rw-r--r--pkgs/patches-linux-5.15/0052-irqchip-armada-370-xp-Do-not-allow-mapping-IRQ-0-and.patch37
-rw-r--r--pkgs/patches-linux-5.15/0053-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch99
-rw-r--r--pkgs/patches-linux-5.15/0054-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch47
-rw-r--r--pkgs/patches-linux-5.15/0055-PCI-Assign-PCI-domain-by-ida_alloc.patch215
-rw-r--r--pkgs/patches-linux-5.15/0056-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch68
-rw-r--r--pkgs/patches-linux-5.15/0057-ARM-dts-dove-Fix-assigned-addresses-for-every-PCIe-R.patch35
-rw-r--r--pkgs/patches-linux-5.15/0058-ARM-dts-armada-370-Fix-assigned-addresses-for-every-.patch35
-rw-r--r--pkgs/patches-linux-5.15/0059-ARM-dts-armada-xp-Fix-assigned-addresses-for-every-P.patch141
-rw-r--r--pkgs/patches-linux-5.15/0060-ARM-dts-armada-375-Fix-assigned-addresses-for-every-.patch35
-rw-r--r--pkgs/patches-linux-5.15/0061-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch76
-rw-r--r--pkgs/patches-linux-5.15/0062-ARM-dts-armada-39x-Fix-assigned-addresses-for-every-.patch53
-rw-r--r--pkgs/patches-linux-5.15/0063-irqchip-armada-370-xp-Do-not-call-ipi_resume-when-IP.patch49
-rw-r--r--pkgs/patches-linux-5.15/0064-irqchip-armada-370-xp-Fix-comment-about-unmasking-mp.patch35
-rw-r--r--pkgs/patches-linux-5.15/0065-irqchip-armada-370-xp-Do-not-touch-IPI-registers-on-.patch67
-rw-r--r--pkgs/patches-linux-5.15/0066-irqchip-armada-370-xp-Add-support-for-32-MSI-interru.patch181
-rw-r--r--pkgs/patches-linux-5.15/0067-dt-bindings-PCI-mvebu-Update-information-about-error.patch32
-rw-r--r--pkgs/patches-linux-5.15/0068-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch437
-rw-r--r--pkgs/patches-linux-5.15/0069-ARM-dts-kirkwood-Add-definitions-for-PCIe-error-inte.patch94
-rw-r--r--pkgs/patches-linux-5.15/0070-ARM-dts-dove-Add-definitions-for-PCIe-error-interrup.patch46
-rw-r--r--pkgs/patches-linux-5.15/0071-dt-bindings-irqchip-armada-370-xp-Update-information.patch42
-rw-r--r--pkgs/patches-linux-5.15/0072-irqchip-armada-370-xp-Implement-SoC-Error-interrupts.patch348
-rw-r--r--pkgs/patches-linux-5.15/0073-ARM-dts-armada-370-xp.dtsi-Add-node-for-MPIC-SoC-Err.patch33
-rw-r--r--pkgs/patches-linux-5.15/0074-ARM-dts-armada-375.dtsi-Add-node-for-MPIC-SoC-Error-.patch33
-rw-r--r--pkgs/patches-linux-5.15/0075-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch35
-rw-r--r--pkgs/patches-linux-5.15/0076-ARM-dts-armada-39x.dtsi-Add-node-for-MPIC-SoC-Error-.patch33
-rw-r--r--pkgs/patches-linux-5.15/0077-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-err.patch43
-rw-r--r--pkgs/patches-linux-5.15/0078-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch79
-rw-r--r--pkgs/patches-linux-5.15/0079-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch124
-rw-r--r--pkgs/patches-linux-5.15/0080-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch136
-rw-r--r--pkgs/patches-linux-5.15/0081-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch32
-rw-r--r--pkgs/patches-linux-5.15/0082-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-err.patch43
-rw-r--r--pkgs/patches-linux-5.15/0083-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-err.patch57
-rw-r--r--pkgs/patches-linux-5.15/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch71
-rw-r--r--pkgs/patches-linux-5.15/0085-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-err.patch69
-rw-r--r--pkgs/patches-linux-5.15/0086-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch139
-rw-r--r--pkgs/patches-linux-5.15/0087-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch38
-rw-r--r--pkgs/patches-linux-5.15/0088-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch297
-rw-r--r--pkgs/patches-linux-5.15/0089-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch47
-rw-r--r--pkgs/patches-linux-5.15/0090-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch167
-rw-r--r--pkgs/patches-linux-5.15/0091-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch45
-rw-r--r--pkgs/patches-linux-5.15/0092-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch44
-rw-r--r--pkgs/patches-linux-5.15/0093-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch139
-rw-r--r--pkgs/patches-linux-5.15/0095-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch41
-rw-r--r--pkgs/patches-linux-5.15/0096-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch268
-rw-r--r--pkgs/patches-linux-5.15/0097-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch148
-rw-r--r--pkgs/patches-linux-5.15/0098-PCI-aardvark-Add-clock-support.patch93
-rw-r--r--pkgs/patches-linux-5.15/0099-PCI-aardvark-Add-suspend-to-RAM-support.patch74
-rw-r--r--pkgs/patches-linux-5.15/0100-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch51
-rw-r--r--pkgs/patches-linux-5.15/0101-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch39
-rw-r--r--pkgs/patches-linux-5.15/011-kbuild-export-SUBARCH.patch21
-rw-r--r--pkgs/patches-linux-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch28
-rw-r--r--pkgs/patches-linux-5.15/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch65
-rw-r--r--pkgs/patches-linux-5.15/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch31
-rw-r--r--pkgs/patches-linux-5.15/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch3078
-rw-r--r--pkgs/patches-linux-5.15/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch1005
-rw-r--r--pkgs/patches-linux-5.15/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch120
-rw-r--r--pkgs/patches-linux-5.15/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch61
-rw-r--r--pkgs/patches-linux-5.15/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch387
-rw-r--r--pkgs/patches-linux-5.15/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch52
-rw-r--r--pkgs/patches-linux-5.15/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch43
-rw-r--r--pkgs/patches-linux-5.15/100-aardvark-workaround-PCIe.patch81
-rw-r--r--pkgs/patches-linux-5.15/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch29
-rw-r--r--pkgs/patches-linux-5.15/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch37
-rw-r--r--pkgs/patches-linux-5.15/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch53
-rw-r--r--pkgs/patches-linux-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch26
-rw-r--r--pkgs/patches-linux-5.15/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch57
-rw-r--r--pkgs/patches-linux-5.15/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch125
-rw-r--r--pkgs/patches-linux-5.15/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch38
-rw-r--r--pkgs/patches-linux-5.15/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch36
-rw-r--r--pkgs/patches-linux-5.15/110-net-sfp-move-quirk-handling-into-sfp.c.patch299
-rw-r--r--pkgs/patches-linux-5.15/111-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch75
-rw-r--r--pkgs/patches-linux-5.15/112-net-sfp-move-Huawei-MA5671A-fixup.patch52
-rw-r--r--pkgs/patches-linux-5.15/113-net-sfp-add-support-for-HALNy-GPON-SFP.patch93
-rw-r--r--pkgs/patches-linux-5.15/114-fixup-net-sfp-add-support-for-HALNy-GPON-SFP.patch42
-rw-r--r--pkgs/patches-linux-5.15/115-net-sfp-redo-soft-state-polling.patch84
-rw-r--r--pkgs/patches-linux-5.15/116-net-sfp-change-HALNy-to-ignore-hardware-pins.patch40
-rw-r--r--pkgs/patches-linux-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch82
-rw-r--r--pkgs/patches-linux-5.15/130-add-linux-spidev-compatible-si3210.patch18
-rw-r--r--pkgs/patches-linux-5.15/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch81
-rw-r--r--pkgs/patches-linux-5.15/141-jffs2-add-RENAME_EXCHANGE-support.patch73
-rw-r--r--pkgs/patches-linux-5.15/142-jffs2-add-splice-ops.patch20
-rw-r--r--pkgs/patches-linux-5.15/150-bridge_allow_receiption_on_disabled_port.patch45
-rw-r--r--pkgs/patches-linux-5.15/190-rtc-rs5c372-support_alarms_up_to_1_week.patch94
-rw-r--r--pkgs/patches-linux-5.15/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch70
-rw-r--r--pkgs/patches-linux-5.15/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch49
-rw-r--r--pkgs/patches-linux-5.15/203-kallsyms_uncompressed.patch119
-rw-r--r--pkgs/patches-linux-5.15/204-module_strip.patch212
-rw-r--r--pkgs/patches-linux-5.15/205-backtrace_module_info.patch41
-rw-r--r--pkgs/patches-linux-5.15/205-kconfig-exit.patch20
-rw-r--r--pkgs/patches-linux-5.15/210-darwin_scripts_include.patch3053
-rw-r--r--pkgs/patches-linux-5.15/211-darwin-uuid-typedef-clash.patch22
-rw-r--r--pkgs/patches-linux-5.15/212-tools_portability.patch110
-rw-r--r--pkgs/patches-linux-5.15/214-spidev_h_portability.patch24
-rw-r--r--pkgs/patches-linux-5.15/220-arm-gc_sections.patch123
-rw-r--r--pkgs/patches-linux-5.15/221-module_exports.patch126
-rw-r--r--pkgs/patches-linux-5.15/230-openwrt_lzma_options.patch34
-rw-r--r--pkgs/patches-linux-5.15/240-remove-unsane-filenames-from-deps_initramfs-list.patch30
-rw-r--r--pkgs/patches-linux-5.15/249-udp-tunnel-selection.patch20
-rw-r--r--pkgs/patches-linux-5.15/250-netfilter_depends.patch27
-rw-r--r--pkgs/patches-linux-5.15/251-kconfig.patch199
-rw-r--r--pkgs/patches-linux-5.15/252-SATA_PMP.patch23
-rw-r--r--pkgs/patches-linux-5.15/253-ksmbd-config.patch32
-rw-r--r--pkgs/patches-linux-5.15/259-regmap_dynamic.patch144
-rw-r--r--pkgs/patches-linux-5.15/260-crypto_test_dependencies.patch52
-rw-r--r--pkgs/patches-linux-5.15/261-enable_wilink_platform_without_drivers.patch20
-rw-r--r--pkgs/patches-linux-5.15/261-lib-arc4-unhide.patch24
-rw-r--r--pkgs/patches-linux-5.15/280-rfkill-stubs.patch84
-rw-r--r--pkgs/patches-linux-5.15/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch64
-rw-r--r--pkgs/patches-linux-5.15/300-mips_expose_boot_raw.patch40
-rw-r--r--pkgs/patches-linux-5.15/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch208
-rw-r--r--pkgs/patches-linux-5.15/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch48
-rw-r--r--pkgs/patches-linux-5.15/301-mips_image_cmdline_hack.patch38
-rw-r--r--pkgs/patches-linux-5.15/301-mvebu-armada-38x-enable-libata-leds.patch10
-rw-r--r--pkgs/patches-linux-5.15/302-add_powertables.patch770
-rw-r--r--pkgs/patches-linux-5.15/302-mips_no_branch_likely.patch22
-rw-r--r--pkgs/patches-linux-5.15/304-revert_i2c_delay.patch15
-rw-r--r--pkgs/patches-linux-5.15/305-armada-385-rd-mtd-partitions.patch19
-rw-r--r--pkgs/patches-linux-5.15/305-mips_module_reloc.patch370
-rw-r--r--pkgs/patches-linux-5.15/306-ARM-mvebu-385-ap-Add-partitions.patch35
-rw-r--r--pkgs/patches-linux-5.15/307-armada-xp-linksys-mamba-broken-idle.patch10
-rw-r--r--pkgs/patches-linux-5.15/307-mips_highmem_offset.patch19
-rw-r--r--pkgs/patches-linux-5.15/308-armada-xp-linksys-mamba-wan.patch11
-rw-r--r--pkgs/patches-linux-5.15/308-mips32r2_tune.patch22
-rw-r--r--pkgs/patches-linux-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch136
-rw-r--r--pkgs/patches-linux-5.15/309-linksys-status-led.patch50
-rw-r--r--pkgs/patches-linux-5.15/310-arm_module_unresolved_weak_sym.patch22
-rw-r--r--pkgs/patches-linux-5.15/310-linksys-use-eth0-as-cpu-port.patch25
-rw-r--r--pkgs/patches-linux-5.15/311-adjust-compatible-for-linksys.patch68
-rw-r--r--pkgs/patches-linux-5.15/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch87
-rw-r--r--pkgs/patches-linux-5.15/313-helios4-dts-status-led-alias.patch28
-rw-r--r--pkgs/patches-linux-5.15/315-armada-xp-linksys-mamba-resize-kernel.patch37
-rw-r--r--pkgs/patches-linux-5.15/316-armada-370-dts-fix-crypto-engine.patch29
-rw-r--r--pkgs/patches-linux-5.15/321-powerpc_crtsavres_prereq.patch38
-rw-r--r--pkgs/patches-linux-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch283
-rw-r--r--pkgs/patches-linux-5.15/332-arc-add-OWRTDTB-section.patch84
-rw-r--r--pkgs/patches-linux-5.15/333-arc-enable-unaligned-access-in-kernel-mode.patch24
-rw-r--r--pkgs/patches-linux-5.15/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch25
-rw-r--r--pkgs/patches-linux-5.15/343-netfilter-nft_flow_offload-handle-netdevice-events-f.patch106
-rw-r--r--pkgs/patches-linux-5.15/400-find_active_root.patch60
-rw-r--r--pkgs/patches-linux-5.15/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch72
-rw-r--r--pkgs/patches-linux-5.15/401-v5.20-mtd-parsers-add-support-for-Sercomm-partitions.patch302
-rw-r--r--pkgs/patches-linux-5.15/402-mtd-blktrans-call-add-disks-after-mtd-device.patch98
-rw-r--r--pkgs/patches-linux-5.15/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch245
-rw-r--r--pkgs/patches-linux-5.15/402-v5.20-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch106
-rw-r--r--pkgs/patches-linux-5.15/410-block-fit-partition-parser.patch214
-rw-r--r--pkgs/patches-linux-5.15/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch33
-rw-r--r--pkgs/patches-linux-5.15/420-mtd-redboot_space.patch41
-rw-r--r--pkgs/patches-linux-5.15/420-mtd-set-rootfs-to-be-root-dev.patch39
-rw-r--r--pkgs/patches-linux-5.15/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch58
-rw-r--r--pkgs/patches-linux-5.15/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch33
-rw-r--r--pkgs/patches-linux-5.15/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch84
-rw-r--r--pkgs/patches-linux-5.15/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch91
-rw-r--r--pkgs/patches-linux-5.15/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch120
-rw-r--r--pkgs/patches-linux-5.15/430-mtd-add-myloader-partition-parser.patch229
-rw-r--r--pkgs/patches-linux-5.15/430-mtk-bmt-support.patch33
-rw-r--r--pkgs/patches-linux-5.15/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch68
-rw-r--r--pkgs/patches-linux-5.15/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch37
-rw-r--r--pkgs/patches-linux-5.15/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch25
-rw-r--r--pkgs/patches-linux-5.15/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch17
-rw-r--r--pkgs/patches-linux-5.15/465-m25p80-mx-disable-software-protection.patch18
-rw-r--r--pkgs/patches-linux-5.15/476-mtd-spi-nor-add-eon-en25q128.patch18
-rw-r--r--pkgs/patches-linux-5.15/479-mtd-spi-nor-add-xtx-xt25f128b.patch79
-rw-r--r--pkgs/patches-linux-5.15/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch22
-rw-r--r--pkgs/patches-linux-5.15/482-mtd-spi-nor-add-gd25q512.patch25
-rw-r--r--pkgs/patches-linux-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch178
-rw-r--r--pkgs/patches-linux-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch27
-rw-r--r--pkgs/patches-linux-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch28
-rw-r--r--pkgs/patches-linux-5.15/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch143
-rw-r--r--pkgs/patches-linux-5.15/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch97
-rw-r--r--pkgs/patches-linux-5.15/491-ubi-auto-create-ubiblock-device-for-rootfs.patch69
-rw-r--r--pkgs/patches-linux-5.15/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch53
-rw-r--r--pkgs/patches-linux-5.15/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch34
-rw-r--r--pkgs/patches-linux-5.15/494-mtd-ubi-add-EOF-marker-support.patch60
-rw-r--r--pkgs/patches-linux-5.15/495-mtd-core-add-get_mtd_device_by_node.patch75
-rw-r--r--pkgs/patches-linux-5.15/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch52
-rw-r--r--pkgs/patches-linux-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch216
-rw-r--r--pkgs/patches-linux-5.15/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch34
-rw-r--r--pkgs/patches-linux-5.15/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch30
-rw-r--r--pkgs/patches-linux-5.15/500-fs_cdrom_dependencies.patch62
-rw-r--r--pkgs/patches-linux-5.15/530-jffs2_make_lzma_available.patch5180
-rw-r--r--pkgs/patches-linux-5.15/531-debloat_lzma.patch1040
-rw-r--r--pkgs/patches-linux-5.15/532-jffs2_eofdetect.patch65
-rw-r--r--pkgs/patches-linux-5.15/600-netfilter_conntrack_flush.patch88
-rw-r--r--pkgs/patches-linux-5.15/610-netfilter_match_bypass_default_checks.patch110
-rw-r--r--pkgs/patches-linux-5.15/611-netfilter_match_bypass_default_table.patch106
-rw-r--r--pkgs/patches-linux-5.15/612-netfilter_match_reduce_memory_access.patch22
-rw-r--r--pkgs/patches-linux-5.15/613-netfilter_optional_tcp_window_check.patch83
-rw-r--r--pkgs/patches-linux-5.15/620-net_sched-codel-do-not-defer-queue-length-update.patch86
-rw-r--r--pkgs/patches-linux-5.15/630-packet_socket_type.patch138
-rw-r--r--pkgs/patches-linux-5.15/645-netfilter-connmark-introduce-set-dscpmark.patch212
-rw-r--r--pkgs/patches-linux-5.15/650-netfilter-add-xt_FLOWOFFLOAD-target.patch860
-rw-r--r--pkgs/patches-linux-5.15/651-wireless_mesh_header.patch24
-rw-r--r--pkgs/patches-linux-5.15/655-increase_skb_pad.patch20
-rw-r--r--pkgs/patches-linux-5.15/660-fq_codel_defaults.patch27
-rw-r--r--pkgs/patches-linux-5.15/661-kernel-ct-size-the-hashtable-more-adequately.patch25
-rw-r--r--pkgs/patches-linux-5.15/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch511
-rw-r--r--pkgs/patches-linux-5.15/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch263
-rw-r--r--pkgs/patches-linux-5.15/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch50
-rw-r--r--pkgs/patches-linux-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch149
-rw-r--r--pkgs/patches-linux-5.15/682-of_net-add-mac-address-increment-support.patch89
-rw-r--r--pkgs/patches-linux-5.15/683-of_net-add-mac-address-to-of-tree.patch59
-rw-r--r--pkgs/patches-linux-5.15/700-mvneta-tx-queue-workaround.patch38
-rw-r--r--pkgs/patches-linux-5.15/700-net-next-net-dsa-introduce-tagger-owned-storage-for-private.patch279
-rw-r--r--pkgs/patches-linux-5.15/701-net-dsa-make-tagging-protocols-connect-to-individual-switches.patch274
-rw-r--r--pkgs/patches-linux-5.15/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch66
-rw-r--r--pkgs/patches-linux-5.15/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch30
-rw-r--r--pkgs/patches-linux-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch38
-rw-r--r--pkgs/patches-linux-5.15/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch97
-rw-r--r--pkgs/patches-linux-5.15/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch29
-rw-r--r--pkgs/patches-linux-5.15/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch182
-rw-r--r--pkgs/patches-linux-5.15/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch174
-rw-r--r--pkgs/patches-linux-5.15/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch21
-rw-r--r--pkgs/patches-linux-5.15/720-net-phy-add-aqr-phys.patch142
-rw-r--r--pkgs/patches-linux-5.15/721-net-add-packet-mangeling.patch178
-rw-r--r--pkgs/patches-linux-5.15/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch154
-rw-r--r--pkgs/patches-linux-5.15/723-net-phy-aquantia-fix-system-side-protocol-mi.patch34
-rw-r--r--pkgs/patches-linux-5.15/724-net-phy-aquantia-Add-AQR113-driver-support.patch43
-rw-r--r--pkgs/patches-linux-5.15/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch63
-rw-r--r--pkgs/patches-linux-5.15/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch84
-rw-r--r--pkgs/patches-linux-5.15/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch54
-rw-r--r--pkgs/patches-linux-5.15/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch48
-rw-r--r--pkgs/patches-linux-5.15/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch131
-rw-r--r--pkgs/patches-linux-5.15/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch42
-rw-r--r--pkgs/patches-linux-5.15/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch32
-rw-r--r--pkgs/patches-linux-5.15/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch205
-rw-r--r--pkgs/patches-linux-5.15/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch65
-rw-r--r--pkgs/patches-linux-5.15/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch45
-rw-r--r--pkgs/patches-linux-5.15/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch95
-rw-r--r--pkgs/patches-linux-5.15/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch131
-rw-r--r--pkgs/patches-linux-5.15/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch91
-rw-r--r--pkgs/patches-linux-5.15/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch27
-rw-r--r--pkgs/patches-linux-5.15/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch127
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch80
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch30
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch127
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch29
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch153
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch295
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch33
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch65
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch37
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch92
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch32
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch78
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch159
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch124
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch26
-rw-r--r--pkgs/patches-linux-5.15/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch631
-rw-r--r--pkgs/patches-linux-5.15/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch28
-rw-r--r--pkgs/patches-linux-5.15/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch151
-rw-r--r--pkgs/patches-linux-5.15/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch47
-rw-r--r--pkgs/patches-linux-5.15/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch48
-rw-r--r--pkgs/patches-linux-5.15/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch46
-rw-r--r--pkgs/patches-linux-5.15/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch29
-rw-r--r--pkgs/patches-linux-5.15/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch507
-rw-r--r--pkgs/patches-linux-5.15/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch25
-rw-r--r--pkgs/patches-linux-5.15/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch46
-rw-r--r--pkgs/patches-linux-5.15/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch249
-rw-r--r--pkgs/patches-linux-5.15/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch120
-rw-r--r--pkgs/patches-linux-5.15/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch53
-rw-r--r--pkgs/patches-linux-5.15/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch78
-rw-r--r--pkgs/patches-linux-5.15/760-net-usb-r8152-add-LED-configuration-from-OF.patch74
-rw-r--r--pkgs/patches-linux-5.15/761-dt-bindings-net-add-RTL8152-binding-documentation.patch54
-rw-r--r--pkgs/patches-linux-5.15/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch142
-rw-r--r--pkgs/patches-linux-5.15/762-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch155
-rw-r--r--pkgs/patches-linux-5.15/763-net-next-net-dsa-qca8k-add-LAG-support.patch288
-rw-r--r--pkgs/patches-linux-5.15/764-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch40
-rw-r--r--pkgs/patches-linux-5.15/765-1-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch52
-rw-r--r--pkgs/patches-linux-5.15/765-2-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch34
-rw-r--r--pkgs/patches-linux-5.15/765-3-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch91
-rw-r--r--pkgs/patches-linux-5.15/765-4-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch78
-rw-r--r--pkgs/patches-linux-5.15/765-5-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch118
-rw-r--r--pkgs/patches-linux-5.15/765-6-net-next-net-dsa-setup-master-before-ports.patch115
-rw-r--r--pkgs/patches-linux-5.15/766-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch254
-rw-r--r--pkgs/patches-linux-5.15/766-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch89
-rw-r--r--pkgs/patches-linux-5.15/766-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch86
-rw-r--r--pkgs/patches-linux-5.15/766-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch71
-rw-r--r--pkgs/patches-linux-5.15/766-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch27
-rw-r--r--pkgs/patches-linux-5.15/766-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch110
-rw-r--r--pkgs/patches-linux-5.15/766-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch45
-rw-r--r--pkgs/patches-linux-5.15/766-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch116
-rw-r--r--pkgs/patches-linux-5.15/766-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch67
-rw-r--r--pkgs/patches-linux-5.15/766-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch363
-rw-r--r--pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch226
-rw-r--r--pkgs/patches-linux-5.15/766-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch287
-rw-r--r--pkgs/patches-linux-5.15/766-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch208
-rw-r--r--pkgs/patches-linux-5.15/766-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch164
-rw-r--r--pkgs/patches-linux-5.15/766-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch206
-rw-r--r--pkgs/patches-linux-5.15/766-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch104
-rw-r--r--pkgs/patches-linux-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch27
-rw-r--r--pkgs/patches-linux-5.15/773-bgmac-add-srab-switch.patch98
-rw-r--r--pkgs/patches-linux-5.15/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch77
-rw-r--r--pkgs/patches-linux-5.15/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch52
-rw-r--r--pkgs/patches-linux-5.15/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch141
-rw-r--r--pkgs/patches-linux-5.15/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch118
-rw-r--r--pkgs/patches-linux-5.15/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch270
-rw-r--r--pkgs/patches-linux-5.15/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch51
-rw-r--r--pkgs/patches-linux-5.15/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch61
-rw-r--r--pkgs/patches-linux-5.15/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch115
-rw-r--r--pkgs/patches-linux-5.15/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch57
-rw-r--r--pkgs/patches-linux-5.15/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch107
-rw-r--r--pkgs/patches-linux-5.15/775-v5.16-1-net-phy-add-phy_interface_t-bitmap-support.patch66
-rw-r--r--pkgs/patches-linux-5.15/775-v5.16-2-net-phylink-add-MAC-phy_interface_t-bitmap.patch29
-rw-r--r--pkgs/patches-linux-5.15/775-v5.16-3-net-phylink-use-supported_interfaces-for-phylink-val.patch105
-rw-r--r--pkgs/patches-linux-5.15/776-v5.16-1-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch1009
-rw-r--r--pkgs/patches-linux-5.15/776-v5.16-2-net-mvneta-populate-supported_interfaces-member.patch53
-rw-r--r--pkgs/patches-linux-5.15/776-v5.16-3-net-mvneta-remove-interface-checks-in-mvneta_validat.patch40
-rw-r--r--pkgs/patches-linux-5.15/776-v5.16-4-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch60
-rw-r--r--pkgs/patches-linux-5.15/777-v5.16-1-net-dpaa2-mac-add-support-for-more-ethtool-10G-link-.patch45
-rw-r--r--pkgs/patches-linux-5.15/777-v5.16-2-net-phylink-add-phylink_set_10g_modes-helper.patch52
-rw-r--r--pkgs/patches-linux-5.15/777-v5.16-3-net-ethernet-use-phylink_set_10g_modes.patch74
-rw-r--r--pkgs/patches-linux-5.15/779-v5.16-1-net-mvpp2-populate-supported_interfaces-member.patch62
-rw-r--r--pkgs/patches-linux-5.15/779-v5.16-2-net-mvpp2-remove-interface-checks-in-mvpp2_phylink_v.patch63
-rw-r--r--pkgs/patches-linux-5.15/779-v5.16-3-net-mvpp2-drop-use-of-phylink_helper_basex_speed.patch58
-rw-r--r--pkgs/patches-linux-5.15/779-v5.16-4-net-mvpp2-clean-up-mvpp2_phylink_validate.patch64
-rw-r--r--pkgs/patches-linux-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch61
-rw-r--r--pkgs/patches-linux-5.15/780-usb-net-MeigLink_modem_support.patch43
-rw-r--r--pkgs/patches-linux-5.15/780-v5.16-1-net-phylink-add-generic-validate-implementation.patch348
-rw-r--r--pkgs/patches-linux-5.15/780-v5.16-2-net-mvneta-use-phylink_generic_validate.patch77
-rw-r--r--pkgs/patches-linux-5.15/780-v5.16-3-net-mvpp2-use-phylink_generic_validate.patch107
-rw-r--r--pkgs/patches-linux-5.15/781-v5.17-1-net-dsa-populate-supported_interfaces-member.patch72
-rw-r--r--pkgs/patches-linux-5.15/781-v5.17-2-net-dsa-consolidate-phylink-creation.patch158
-rw-r--r--pkgs/patches-linux-5.15/781-v5.17-3-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch58
-rw-r--r--pkgs/patches-linux-5.15/781-v5.17-4-net-dsa-support-use-of-phylink_generic_validate.patch46
-rw-r--r--pkgs/patches-linux-5.15/781-v5.17-5-net-dsa-hellcreek-convert-to-phylink_generic_validat.patch78
-rw-r--r--pkgs/patches-linux-5.15/781-v5.17-6-net-dsa-lantiq-convert-to-phylink_generic_validate.patch196
-rw-r--r--pkgs/patches-linux-5.15/782-v5.17-1-net-phylink-tidy-up-disable-bit-clearing.patch70
-rw-r--r--pkgs/patches-linux-5.15/782-v5.17-2-net-mvneta-program-1ms-autonegotiation-clock-divisor.patch56
-rw-r--r--pkgs/patches-linux-5.15/784-v5.19-1-net-dsa-mv88e6xxx-add-mv88e6352_g2_scratch_port_has_.patch77
-rw-r--r--pkgs/patches-linux-5.15/784-v5.19-2-net-dsa-mv88e6xxx-populate-supported_interfaces-and-.patch548
-rw-r--r--pkgs/patches-linux-5.15/784-v5.19-3-net-dsa-mv88e6xxx-convert-to-phylink_generic_validat.patch407
-rw-r--r--pkgs/patches-linux-5.15/784-v5.19-4-net-dsa-mv88e6xxx-improve-88e6352-serdes-statistics-.patch125
-rw-r--r--pkgs/patches-linux-5.15/784-v5.19-5-net-dsa-mv88e6xxx-Fix-off-by-in-one-in-mv88e6185_phy.patch33
-rw-r--r--pkgs/patches-linux-5.15/784-v5.19-6-net-dsa-mv88e6xxx-Fix-validation-of-built-in-PHYs-on.patch73
-rw-r--r--pkgs/patches-linux-5.15/800-GPIO-add-named-gpio-exports.patch162
-rw-r--r--pkgs/patches-linux-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch73
-rw-r--r--pkgs/patches-linux-5.15/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch40
-rw-r--r--pkgs/patches-linux-5.15/800-v5.20-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch125
-rw-r--r--pkgs/patches-linux-5.15/800-v5.20-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch356
-rw-r--r--pkgs/patches-linux-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch222
-rw-r--r--pkgs/patches-linux-5.15/801-v5.20-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch30
-rw-r--r--pkgs/patches-linux-5.15/801-v5.20-0002-leds-add-help-info-about-BCM63138-module-name.patch28
-rw-r--r--pkgs/patches-linux-5.15/801-v5.20-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch30
-rw-r--r--pkgs/patches-linux-5.15/810-pci_disable_common_quirks.patch62
-rw-r--r--pkgs/patches-linux-5.15/811-pci_disable_usb_common_quirks.patch115
-rw-r--r--pkgs/patches-linux-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch26
-rw-r--r--pkgs/patches-linux-5.15/834-ledtrig-libata.patch149
-rw-r--r--pkgs/patches-linux-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch26
-rw-r--r--pkgs/patches-linux-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch59
-rw-r--r--pkgs/patches-linux-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch102
-rw-r--r--pkgs/patches-linux-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch217
-rw-r--r--pkgs/patches-linux-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch1564
-rw-r--r--pkgs/patches-linux-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch31
-rw-r--r--pkgs/patches-linux-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch61
-rw-r--r--pkgs/patches-linux-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch163
-rw-r--r--pkgs/patches-linux-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch36
-rw-r--r--pkgs/patches-linux-5.15/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch49
-rw-r--r--pkgs/patches-linux-5.15/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch71
-rw-r--r--pkgs/patches-linux-5.15/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch58
-rw-r--r--pkgs/patches-linux-5.15/8800-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch213
-rw-r--r--pkgs/patches-linux-5.15/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch118
-rw-r--r--pkgs/patches-linux-5.15/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch63
-rw-r--r--pkgs/patches-linux-5.15/900-v5.20-powerpc-add-Turris-1x-dts.patch508
-rw-r--r--pkgs/patches-linux-5.15/901-debloat_sock_diag.patch162
-rw-r--r--pkgs/patches-linux-5.15/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch218
-rw-r--r--pkgs/patches-linux-5.15/902-debloat_proc.patch408
-rw-r--r--pkgs/patches-linux-5.15/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch1034
-rw-r--r--pkgs/patches-linux-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch469
-rw-r--r--pkgs/patches-linux-5.15/904-debloat_dma_buf.patch92
-rw-r--r--pkgs/patches-linux-5.15/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch207
-rw-r--r--pkgs/patches-linux-5.15/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch82
-rw-r--r--pkgs/patches-linux-5.15/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch74
-rw-r--r--pkgs/patches-linux-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch41
-rw-r--r--pkgs/patches-linux-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch271
-rw-r--r--pkgs/patches-linux-5.15/910-kobject_uevent.patch32
-rw-r--r--pkgs/patches-linux-5.15/911-kobject_add_broadcast_uevent.patch76
-rw-r--r--pkgs/patches-linux-5.15/920-device_tree_cmdline.patch21
-rw-r--r--pkgs/patches-linux-5.15/920-mangle_bootargs.patch71
-rw-r--r--pkgs/patches-linux-5.15/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch30
431 files changed, 59929 insertions, 1 deletions
diff --git a/pkgs/default.nix b/pkgs/default.nix
index be0cc4f..082cca1 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -1,8 +1,11 @@
{ nixpkgs ? <nixpkgs>, nixlib ? nixpkgs.lib }:
+with builtins;
+with nixlib;
+
let
pkgs = nixpkgs // turrispkgs;
- callPackage = nixlib.callPackageWith pkgs;
+ callPackage = callPackageWith pkgs;
turrispkgs = with pkgs; {
@@ -11,6 +14,14 @@ let
mox-otp = python3Packages.callPackage ./mox-otp { };
crypto-wrapper = callPackage ./crypto-wrapper { };
+ # Turris kernels with patches
+ linux_turris_5_15 = callPackage
+ "${nixpkgs.path}/pkgs/os-specific/linux/kernel/linux-5.15.nix" {
+ kernelPatches = map (p: { name = toString p; patch = ./patches-linux-5.15 + "/${p}"; }) (
+ attrNames (filterAttrs (n: v: v == "regular") (
+ readDir ./patches-linux-5.15)));
+ };
+
# NOR Firmwares
armTrustedFirmwareTurrisMox = buildArmTrustedFirmware rec {
platform = "a3700";
diff --git a/pkgs/patches-linux-5.15/0001-PCI-mvebu-Replace-pci_ioremap_io-usage-by-devm_pci_r.patch b/pkgs/patches-linux-5.15/0001-PCI-mvebu-Replace-pci_ioremap_io-usage-by-devm_pci_r.patch
new file mode 100644
index 0000000..abe0360
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0001-PCI-mvebu-Replace-pci_ioremap_io-usage-by-devm_pci_r.patch
@@ -0,0 +1,62 @@
+From 42b54ca7612af1051a744abf00fdd5a04bccb627 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 23 Nov 2021 12:22:59 +0100
+Subject: [PATCH 01/90] PCI: mvebu: Replace pci_ioremap_io() usage by
+ devm_pci_remap_iospace()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Now when ARM architecture code also provides standard PCI core function
+pci_remap_iospace(), use its devm_pci_remap_iospace() variant in
+pci-mvebu.c driver instead of old ARM-specific pci_ioremap_io() function.
+
+Call devm_pci_remap_iospace() before adding IO resource to host bridge
+structure, at the place where it should be.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 13 ++++---------
+ 1 file changed, 4 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 2a3bf82aa4e2..12661311853b 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -1082,6 +1082,10 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie)
+ resource_size(&pcie->io) - 1);
+ pcie->realio.name = "PCI I/O";
+
++ ret = devm_pci_remap_iospace(dev, &pcie->realio, pcie->io.start);
++ if (ret)
++ return ret;
++
+ pci_add_resource(&bridge->windows, &pcie->realio);
+ ret = devm_request_resource(dev, &ioport_resource, &pcie->realio);
+ if (ret)
+@@ -1100,7 +1104,6 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie)
+ */
+ static int mvebu_pci_host_probe(struct pci_host_bridge *bridge)
+ {
+- struct mvebu_pcie *pcie;
+ struct pci_bus *bus, *child;
+ int ret;
+
+@@ -1110,14 +1113,6 @@ static int mvebu_pci_host_probe(struct pci_host_bridge *bridge)
+ return ret;
+ }
+
+- pcie = pci_host_bridge_priv(bridge);
+- if (resource_size(&pcie->io) != 0) {
+- unsigned int i;
+-
+- for (i = 0; i < resource_size(&pcie->realio); i += SZ_64K)
+- pci_ioremap_io(i, pcie->io.start + i);
+- }
+-
+ bus = bridge->bus;
+
+ /*
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0002-PCI-mvebu-Remove-custom-mvebu_pci_host_probe-functio.patch b/pkgs/patches-linux-5.15/0002-PCI-mvebu-Remove-custom-mvebu_pci_host_probe-functio.patch
new file mode 100644
index 0000000..ea40f97
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0002-PCI-mvebu-Remove-custom-mvebu_pci_host_probe-functio.patch
@@ -0,0 +1,82 @@
+From 74c5cf43b5e5de7832075264762c9ea85ef06b0a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 16 Nov 2021 20:19:52 +0100
+Subject: [PATCH 02/90] PCI: mvebu: Remove custom mvebu_pci_host_probe()
+ function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Now after pci_ioremap_io() usage was replaced by devm_pci_remap_iospace()
+function, there is no need to use custom mvebu_pci_host_probe() function.
+Current implementation of mvebu_pci_host_probe() is same as standard PCI
+core functionn pci_host_probe(). So replace mvebu_pci_host_probe() call by
+pci_host_probe() and remove custom mvebu_pci_host_probe() function.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 41 +-----------------------------
+ 1 file changed, 1 insertion(+), 40 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 12661311853b..9cbf10d6fc30 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -1095,45 +1095,6 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie)
+ return 0;
+ }
+
+-/*
+- * This is a copy of pci_host_probe(), except that it does the I/O
+- * remap as the last step, once we are sure we won't fail.
+- *
+- * It should be removed once the I/O remap error handling issue has
+- * been sorted out.
+- */
+-static int mvebu_pci_host_probe(struct pci_host_bridge *bridge)
+-{
+- struct pci_bus *bus, *child;
+- int ret;
+-
+- ret = pci_scan_root_bus_bridge(bridge);
+- if (ret < 0) {
+- dev_err(bridge->dev.parent, "Scanning root bridge failed");
+- return ret;
+- }
+-
+- bus = bridge->bus;
+-
+- /*
+- * We insert PCI resources into the iomem_resource and
+- * ioport_resource trees in either pci_bus_claim_resources()
+- * or pci_bus_assign_resources().
+- */
+- if (pci_has_flag(PCI_PROBE_ONLY)) {
+- pci_bus_claim_resources(bus);
+- } else {
+- pci_bus_size_bridges(bus);
+- pci_bus_assign_resources(bus);
+-
+- list_for_each_entry(child, &bus->children, node)
+- pcie_bus_configure_settings(child);
+- }
+-
+- pci_bus_add_devices(bus);
+- return 0;
+-}
+-
+ static int mvebu_pcie_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -1293,7 +1254,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+ bridge->ops = &mvebu_pcie_ops;
+ bridge->align_resource = mvebu_pcie_align_resource;
+
+- return mvebu_pci_host_probe(bridge);
++ return pci_host_probe(bridge);
+ }
+
+ static const struct of_device_id mvebu_pcie_of_match_table[] = {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0003-arm-ioremap-Replace-pci_ioremap_io-usage-by-pci_rema.patch b/pkgs/patches-linux-5.15/0003-arm-ioremap-Replace-pci_ioremap_io-usage-by-pci_rema.patch
new file mode 100644
index 0000000..eb1d636
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0003-arm-ioremap-Replace-pci_ioremap_io-usage-by-pci_rema.patch
@@ -0,0 +1,170 @@
+From f7b0b5aa413f2a38083e4155baf211117121674c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 23 Nov 2021 12:05:54 +0100
+Subject: [PATCH 03/90] arm: ioremap: Replace pci_ioremap_io() usage by
+ pci_remap_iospace()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Replace all usage of ARM specific pci_ioremap_io() function by standard PCI
+core API function pci_remap_iospace() in all drivers and arm march code.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/mach-dove/pcie.c | 9 +++++----
+ arch/arm/mach-iop32x/pci.c | 5 ++++-
+ arch/arm/mach-mv78xx0/pcie.c | 5 ++++-
+ arch/arm/mach-orion5x/pci.c | 10 ++++++++--
+ drivers/pcmcia/at91_cf.c | 6 +++++-
+ 5 files changed, 26 insertions(+), 9 deletions(-)
+
+diff --git a/arch/arm/mach-dove/pcie.c b/arch/arm/mach-dove/pcie.c
+index ee91ac6b5ebf..2a493bdfffc6 100644
+--- a/arch/arm/mach-dove/pcie.c
++++ b/arch/arm/mach-dove/pcie.c
+@@ -38,6 +38,7 @@ static int num_pcie_ports;
+ static int __init dove_pcie_setup(int nr, struct pci_sys_data *sys)
+ {
+ struct pcie_port *pp;
++ struct resource realio;
+
+ if (nr >= num_pcie_ports)
+ return 0;
+@@ -53,10 +54,10 @@ static int __init dove_pcie_setup(int nr, struct pci_sys_data *sys)
+
+ orion_pcie_setup(pp->base);
+
+- if (pp->index == 0)
+- pci_ioremap_io(sys->busnr * SZ_64K, DOVE_PCIE0_IO_PHYS_BASE);
+- else
+- pci_ioremap_io(sys->busnr * SZ_64K, DOVE_PCIE1_IO_PHYS_BASE);
++ realio.start = sys->busnr * SZ_64K;
++ realio.end = realio.start + SZ_64K - 1;
++ pci_remap_iospace(&realio, pp->index == 0 ? DOVE_PCIE0_IO_PHYS_BASE :
++ DOVE_PCIE1_IO_PHYS_BASE);
+
+ /*
+ * IORESOURCE_MEM
+diff --git a/arch/arm/mach-iop32x/pci.c b/arch/arm/mach-iop32x/pci.c
+index ab0010dc3145..7a215d2ee7e2 100644
+--- a/arch/arm/mach-iop32x/pci.c
++++ b/arch/arm/mach-iop32x/pci.c
+@@ -185,6 +185,7 @@ iop3xx_pci_abort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+ int iop3xx_pci_setup(int nr, struct pci_sys_data *sys)
+ {
+ struct resource *res;
++ struct resource realio;
+
+ if (nr != 0)
+ return 0;
+@@ -206,7 +207,9 @@ int iop3xx_pci_setup(int nr, struct pci_sys_data *sys)
+
+ pci_add_resource_offset(&sys->resources, res, sys->mem_offset);
+
+- pci_ioremap_io(0, IOP3XX_PCI_LOWER_IO_PA);
++ realio.start = 0;
++ realio.end = realio.start + SZ_64K - 1;
++ pci_remap_iospace(&realio, IOP3XX_PCI_LOWER_IO_PA);
+
+ return 1;
+ }
+diff --git a/arch/arm/mach-mv78xx0/pcie.c b/arch/arm/mach-mv78xx0/pcie.c
+index 636d84b40466..e15646af7f26 100644
+--- a/arch/arm/mach-mv78xx0/pcie.c
++++ b/arch/arm/mach-mv78xx0/pcie.c
+@@ -101,6 +101,7 @@ static void __init mv78xx0_pcie_preinit(void)
+ static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys)
+ {
+ struct pcie_port *pp;
++ struct resource realio;
+
+ if (nr >= num_pcie_ports)
+ return 0;
+@@ -115,7 +116,9 @@ static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys)
+ orion_pcie_set_local_bus_nr(pp->base, sys->busnr);
+ orion_pcie_setup(pp->base);
+
+- pci_ioremap_io(nr * SZ_64K, MV78XX0_PCIE_IO_PHYS_BASE(nr));
++ realio.start = nr * SZ_64K;
++ realio.end = realio.start + SZ_64K - 1;
++ pci_remap_iospace(&realio, MV78XX0_PCIE_IO_PHYS_BASE(nr));
+
+ pci_add_resource_offset(&sys->resources, &pp->res, sys->mem_offset);
+
+diff --git a/arch/arm/mach-orion5x/pci.c b/arch/arm/mach-orion5x/pci.c
+index 76951bfbacf5..92e938bba20d 100644
+--- a/arch/arm/mach-orion5x/pci.c
++++ b/arch/arm/mach-orion5x/pci.c
+@@ -142,6 +142,7 @@ static struct pci_ops pcie_ops = {
+ static int __init pcie_setup(struct pci_sys_data *sys)
+ {
+ struct resource *res;
++ struct resource realio;
+ int dev;
+
+ /*
+@@ -164,7 +165,9 @@ static int __init pcie_setup(struct pci_sys_data *sys)
+ pcie_ops.read = pcie_rd_conf_wa;
+ }
+
+- pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCIE_IO_PHYS_BASE);
++ realio.start = sys->busnr * SZ_64K;
++ realio.end = realio.start + SZ_64K - 1;
++ pci_remap_iospace(&realio, ORION5X_PCIE_IO_PHYS_BASE);
+
+ /*
+ * Request resources.
+@@ -466,6 +469,7 @@ static void __init orion5x_setup_pci_wins(void)
+ static int __init pci_setup(struct pci_sys_data *sys)
+ {
+ struct resource *res;
++ struct resource realio;
+
+ /*
+ * Point PCI unit MBUS decode windows to DRAM space.
+@@ -482,7 +486,9 @@ static int __init pci_setup(struct pci_sys_data *sys)
+ */
+ orion5x_setbits(PCI_CMD, PCI_CMD_HOST_REORDER);
+
+- pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCI_IO_PHYS_BASE);
++ realio.start = sys->busnr * SZ_64K;
++ realio.end = realio.start + SZ_64K - 1;
++ pci_remap_iospace(&realio, ORION5X_PCI_IO_PHYS_BASE);
+
+ /*
+ * Request resources
+diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c
+index 6b1edfc890a3..92df2c2c5d07 100644
+--- a/drivers/pcmcia/at91_cf.c
++++ b/drivers/pcmcia/at91_cf.c
+@@ -20,6 +20,7 @@
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+ #include <linux/of_gpio.h>
++#include <linux/pci.h>
+ #include <linux/regmap.h>
+
+ #include <pcmcia/ss.h>
+@@ -230,6 +231,7 @@ static int at91_cf_probe(struct platform_device *pdev)
+ struct at91_cf_socket *cf;
+ struct at91_cf_data *board;
+ struct resource *io;
++ struct resource realio;
+ int status;
+
+ board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
+@@ -307,7 +309,9 @@ static int at91_cf_probe(struct platform_device *pdev)
+ * io_offset is set to 0x10000 to avoid the check in static_find_io().
+ * */
+ cf->socket.io_offset = 0x10000;
+- status = pci_ioremap_io(0x10000, cf->phys_baseaddr + CF_IO_PHYS);
++ realio.start = cf->socket.io_offset;
++ realio.end = realio.start + SZ_64K - 1;
++ status = pci_remap_iospace(&realio, cf->phys_baseaddr + CF_IO_PHYS);
+ if (status)
+ goto fail0a;
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0004-arm-ioremap-Remove-unused-ARM-specific-function-pci_.patch b/pkgs/patches-linux-5.15/0004-arm-ioremap-Remove-unused-ARM-specific-function-pci_.patch
new file mode 100644
index 0000000..78a5b02
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0004-arm-ioremap-Remove-unused-ARM-specific-function-pci_.patch
@@ -0,0 +1,55 @@
+From 084358229bf6939d2f5e54e4d3e9aa84bc9240fa Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 23 Nov 2021 12:24:04 +0100
+Subject: [PATCH 04/90] arm: ioremap: Remove unused ARM-specific function
+ pci_ioremap_io()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This function is not used by any driver anymore. So completely remove it.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/include/asm/io.h | 2 --
+ arch/arm/mm/ioremap.c | 11 -----------
+ 2 files changed, 13 deletions(-)
+
+diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
+index 79d246ac93ab..2d551fa12a34 100644
+--- a/arch/arm/include/asm/io.h
++++ b/arch/arm/include/asm/io.h
+@@ -179,8 +179,6 @@ void pci_ioremap_set_mem_type(int mem_type);
+ static inline void pci_ioremap_set_mem_type(int mem_type) {}
+ #endif
+
+-extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr);
+-
+ /*
+ * PCI configuration space mapping function.
+ *
+diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c
+index 2660bdfcad4d..0e67162c978d 100644
+--- a/arch/arm/mm/ioremap.c
++++ b/arch/arm/mm/ioremap.c
+@@ -453,17 +453,6 @@ void pci_ioremap_set_mem_type(int mem_type)
+ pci_ioremap_mem_type = mem_type;
+ }
+
+-int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr)
+-{
+- BUG_ON(offset + SZ_64K - 1 > IO_SPACE_LIMIT);
+-
+- return ioremap_page_range(PCI_IO_VIRT_BASE + offset,
+- PCI_IO_VIRT_BASE + offset + SZ_64K,
+- phys_addr,
+- __pgprot(get_mem_type(pci_ioremap_mem_type)->prot_pte));
+-}
+-EXPORT_SYMBOL_GPL(pci_ioremap_io);
+-
+ void __iomem *pci_remap_cfgspace(resource_size_t res_cookie, size_t size)
+ {
+ return arch_ioremap_caller(res_cookie, size, MT_UNCACHED,
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0005-bus-mvebu-mbus-Export-symbols-for-public-API-window-.patch b/pkgs/patches-linux-5.15/0005-bus-mvebu-mbus-Export-symbols-for-public-API-window-.patch
new file mode 100644
index 0000000..87a7e9b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0005-bus-mvebu-mbus-Export-symbols-for-public-API-window-.patch
@@ -0,0 +1,63 @@
+From c472757ed4c9b9f3383301842b06f83b1444d743 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sun, 31 Oct 2021 22:50:14 +0100
+Subject: [PATCH 05/90] bus: mvebu-mbus: Export symbols for public API window
+ functions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This would allow to compile pci-mvebu.c driver as module.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/bus/mvebu-mbus.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
+index ea0424922de7..db612045616f 100644
+--- a/drivers/bus/mvebu-mbus.c
++++ b/drivers/bus/mvebu-mbus.c
+@@ -914,6 +914,7 @@ int mvebu_mbus_add_window_remap_by_id(unsigned int target,
+
+ return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute);
+ }
++EXPORT_SYMBOL_GPL(mvebu_mbus_add_window_remap_by_id);
+
+ int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute,
+ phys_addr_t base, size_t size)
+@@ -921,6 +922,7 @@ int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute,
+ return mvebu_mbus_add_window_remap_by_id(target, attribute, base,
+ size, MVEBU_MBUS_NO_REMAP);
+ }
++EXPORT_SYMBOL_GPL(mvebu_mbus_add_window_by_id);
+
+ int mvebu_mbus_del_window(phys_addr_t base, size_t size)
+ {
+@@ -933,6 +935,7 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size)
+ mvebu_mbus_disable_window(&mbus_state, win);
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(mvebu_mbus_del_window);
+
+ void mvebu_mbus_get_pcie_mem_aperture(struct resource *res)
+ {
+@@ -940,6 +943,7 @@ void mvebu_mbus_get_pcie_mem_aperture(struct resource *res)
+ return;
+ *res = mbus_state.pcie_mem_aperture;
+ }
++EXPORT_SYMBOL_GPL(mvebu_mbus_get_pcie_mem_aperture);
+
+ void mvebu_mbus_get_pcie_io_aperture(struct resource *res)
+ {
+@@ -947,6 +951,7 @@ void mvebu_mbus_get_pcie_io_aperture(struct resource *res)
+ return;
+ *res = mbus_state.pcie_io_aperture;
+ }
++EXPORT_SYMBOL_GPL(mvebu_mbus_get_pcie_io_aperture);
+
+ int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr)
+ {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0006-PCI-mvebu-Add-support-for-compiling-driver-as-module.patch b/pkgs/patches-linux-5.15/0006-PCI-mvebu-Add-support-for-compiling-driver-as-module.patch
new file mode 100644
index 0000000..2ff5eef
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0006-PCI-mvebu-Add-support-for-compiling-driver-as-module.patch
@@ -0,0 +1,175 @@
+From 2d12fdead984145ea791cac44c0b0ce25504f4c5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sun, 31 Oct 2021 22:55:20 +0100
+Subject: [PATCH 06/90] PCI: mvebu: Add support for compiling driver as module
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Now when driver uses devm_pci_remap_iospace() function, it is possible
+implement ->remove() callback for unbinding device from driver.
+
+Implement mvebu_pcie_remove() callback with proper cleanup phase, drop
+driver's suppress_bind_attrs flag and switch type of CONFIG_PCI_MVEBU
+option from bool to tristate.
+
+This allows to compile pci-mvebu.c driver as loadable module pci-mvebu.ko
+with ability to unload it.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/Kconfig | 2 +-
+ drivers/pci/controller/pci-mvebu.c | 91 +++++++++++++++++++++++++-----
+ 2 files changed, 77 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
+index 326f7d13024f..a4c99ebff85e 100644
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -4,7 +4,7 @@ menu "PCI controller drivers"
+ depends on PCI
+
+ config PCI_MVEBU
+- bool "Marvell EBU PCIe controller"
++ tristate "Marvell EBU PCIe controller"
+ depends on ARCH_MVEBU || ARCH_DOVE || COMPILE_TEST
+ depends on MVEBU_MBUS
+ depends on ARM
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 9cbf10d6fc30..504eb9b94c0f 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -6,6 +6,7 @@
+ */
+
+ #include <linux/kernel.h>
++#include <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+@@ -154,22 +155,13 @@ static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr)
+ mvebu_writel(port, stat, PCIE_STAT_OFF);
+ }
+
+-/*
+- * Setup PCIE BARs and Address Decode Wins:
+- * BAR[0] -> internal registers (needed for MSI)
+- * BAR[1] -> covers all DRAM banks
+- * BAR[2] -> Disabled
+- * WIN[0-3] -> DRAM bank[0-3]
+- */
+-static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
++static void mvebu_pcie_disable_wins(struct mvebu_pcie_port *port)
+ {
+- const struct mbus_dram_target_info *dram;
+- u32 size;
+ int i;
+
+- dram = mv_mbus_dram_info();
++ mvebu_writel(port, 0, PCIE_BAR_LO_OFF(0));
++ mvebu_writel(port, 0, PCIE_BAR_HI_OFF(0));
+
+- /* First, disable and clear BARs and windows. */
+ for (i = 1; i < 3; i++) {
+ mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i));
+ mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i));
+@@ -185,6 +177,25 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
+ mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF);
+ mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF);
+ mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF);
++}
++
++/*
++ * Setup PCIE BARs and Address Decode Wins:
++ * BAR[0] -> internal registers (needed for MSI)
++ * BAR[1] -> covers all DRAM banks
++ * BAR[2] -> Disabled
++ * WIN[0-3] -> DRAM bank[0-3]
++ */
++static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
++{
++ const struct mbus_dram_target_info *dram;
++ u32 size;
++ int i;
++
++ dram = mv_mbus_dram_info();
++
++ /* First, disable and clear BARs and windows. */
++ mvebu_pcie_disable_wins(port);
+
+ /* Setup windows for DDR banks. Count total DDR size on the fly. */
+ size = 0;
+@@ -1257,6 +1268,52 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+ return pci_host_probe(bridge);
+ }
+
++static int mvebu_pcie_remove(struct platform_device *pdev)
++{
++ struct mvebu_pcie *pcie = platform_get_drvdata(pdev);
++ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++ u32 cmd;
++ int i;
++
++ /* Remove PCI bus with all devices. */
++ pci_lock_rescan_remove();
++ pci_stop_root_bus(bridge->bus);
++ pci_remove_root_bus(bridge->bus);
++ pci_unlock_rescan_remove();
++
++ for (i = 0; i < pcie->nports; i++) {
++ struct mvebu_pcie_port *port = &pcie->ports[i];
++
++ if (!port->base)
++ continue;
++
++ /* Disable Root Bridge I/O space, memory space and bus mastering. */
++ cmd = mvebu_readl(port, PCIE_CMD_OFF);
++ cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
++ mvebu_writel(port, cmd, PCIE_CMD_OFF);
++
++ /* Mask all interrupt sources. */
++ mvebu_writel(port, 0, PCIE_MASK_OFF);
++
++ /* Free config space for emulated root bridge. */
++ pci_bridge_emul_cleanup(&port->bridge);
++
++ /* Disable and clear BARs and windows. */
++ mvebu_pcie_disable_wins(port);
++
++ /* Delete PCIe IO and MEM windows. */
++ if (port->iowin.size)
++ mvebu_pcie_del_windows(port, port->iowin.base, port->iowin.size);
++ if (port->memwin.size)
++ mvebu_pcie_del_windows(port, port->memwin.base, port->memwin.size);
++
++ /* Power down card and disable clocks. Must be the last step. */
++ mvebu_pcie_powerdown(port);
++ }
++
++ return 0;
++}
++
+ static const struct of_device_id mvebu_pcie_of_match_table[] = {
+ { .compatible = "marvell,armada-xp-pcie", },
+ { .compatible = "marvell,armada-370-pcie", },
+@@ -1273,10 +1330,14 @@ static struct platform_driver mvebu_pcie_driver = {
+ .driver = {
+ .name = "mvebu-pcie",
+ .of_match_table = mvebu_pcie_of_match_table,
+- /* driver unloading/unbinding currently not supported */
+- .suppress_bind_attrs = true,
+ .pm = &mvebu_pcie_pm_ops,
+ },
+ .probe = mvebu_pcie_probe,
++ .remove = mvebu_pcie_remove,
+ };
+-builtin_platform_driver(mvebu_pcie_driver);
++module_platform_driver(mvebu_pcie_driver);
++
++MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@bootlin.com>");
++MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
++MODULE_DESCRIPTION("Marvell EBU PCIe controller");
++MODULE_LICENSE("GPL v2");
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0007-PCI-mvebu-Check-for-valid-ports.patch b/pkgs/patches-linux-5.15/0007-PCI-mvebu-Check-for-valid-ports.patch
new file mode 100644
index 0000000..9ffd1bb
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0007-PCI-mvebu-Check-for-valid-ports.patch
@@ -0,0 +1,54 @@
+From 848b0bb310d53bfdf4492605ea23f85f5534d7d3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 16 Nov 2021 10:51:43 +0100
+Subject: [PATCH 07/90] PCI: mvebu: Check for valid ports
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some mvebu ports do not have to be initialized. So skip these uninitialized
+mvebu ports in every port iteration function to prevent access to unmapped
+memory or dereferencing NULL pointers. Uninitialized mvebu port has base
+address set to NULL.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Cc: stable@vger.kernel.org
+---
+ drivers/pci/controller/pci-mvebu.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 504eb9b94c0f..e4c6dc73159f 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -707,6 +707,9 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = &pcie->ports[i];
+
++ if (!port->base)
++ continue;
++
+ if (bus->number == 0 && port->devfn == devfn)
+ return port;
+ if (bus->number != 0 &&
+@@ -882,6 +885,8 @@ static int mvebu_pcie_suspend(struct device *dev)
+ pcie = dev_get_drvdata(dev);
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = pcie->ports + i;
++ if (!port->base)
++ continue;
+ port->saved_pcie_stat = mvebu_readl(port, PCIE_STAT_OFF);
+ }
+
+@@ -896,6 +901,8 @@ static int mvebu_pcie_resume(struct device *dev)
+ pcie = dev_get_drvdata(dev);
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = pcie->ports + i;
++ if (!port->base)
++ continue;
+ mvebu_writel(port, port->saved_pcie_stat, PCIE_STAT_OFF);
+ mvebu_pcie_setup_hw(port);
+ }
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0008-PCI-mvebu-Check-that-PCI-bridge-specified-in-DT-has-.patch b/pkgs/patches-linux-5.15/0008-PCI-mvebu-Check-that-PCI-bridge-specified-in-DT-has-.patch
new file mode 100644
index 0000000..3b943f7
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0008-PCI-mvebu-Check-that-PCI-bridge-specified-in-DT-has-.patch
@@ -0,0 +1,38 @@
+From 978d8acb29959079c59f700fbd9df105147a3c44 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 11:39:48 +0200
+Subject: [PATCH 08/90] PCI: mvebu: Check that PCI bridge specified in DT has
+ function number zero
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Driver cannot handle PCI bridges at non-zero function address. So add
+appropriate check. Currently all in-tree kernel DTS files set PCI bridge
+function to zero.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Cc: stable@vger.kernel.org
+---
+ drivers/pci/controller/pci-mvebu.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index e4c6dc73159f..b0965067e4ce 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -946,6 +946,11 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ port->devfn = of_pci_get_devfn(child);
+ if (port->devfn < 0)
+ goto skip;
++ if (PCI_FUNC(port->devfn) != 0) {
++ dev_err(dev, "%s: invalid function number, must be zero\n",
++ port->name);
++ goto skip;
++ }
+
+ ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM,
+ &port->mem_target, &port->mem_attr);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0009-PCI-mvebu-Handle-invalid-size-of-read-config-request.patch b/pkgs/patches-linux-5.15/0009-PCI-mvebu-Handle-invalid-size-of-read-config-request.patch
new file mode 100644
index 0000000..93aa727
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0009-PCI-mvebu-Handle-invalid-size-of-read-config-request.patch
@@ -0,0 +1,35 @@
+From f117e4e1d4d4ba03cdf376392dc996e84c04ddb5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 13:13:52 +0200
+Subject: [PATCH 09/90] PCI: mvebu: Handle invalid size of read config request
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Function mvebu_pcie_hw_rd_conf() does not handle invalid size. So correctly
+set read value to all-ones and return appropriate error return value
+PCIBIOS_BAD_REGISTER_NUMBER like in mvebu_pcie_hw_wr_conf() function.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Cc: stable@vger.kernel.org
+---
+ drivers/pci/controller/pci-mvebu.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index b0965067e4ce..5bf1889cfb38 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -273,6 +273,9 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,
+ case 4:
+ *val = readl_relaxed(conf_data);
+ break;
++ default:
++ *val = 0xffffffff;
++ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0010-PCI-mvebu-Disallow-mapping-interrupts-on-emulated-br.patch b/pkgs/patches-linux-5.15/0010-PCI-mvebu-Disallow-mapping-interrupts-on-emulated-br.patch
new file mode 100644
index 0000000..cfcb1e5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0010-PCI-mvebu-Disallow-mapping-interrupts-on-emulated-br.patch
@@ -0,0 +1,51 @@
+From 7931a2a131998d990e5f9732fc3dc448ec672046 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 13:42:10 +0200
+Subject: [PATCH 10/90] PCI: mvebu: Disallow mapping interrupts on emulated
+ bridges
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Interrupt support on mvebu emulated bridges is not implemented yet.
+
+So properly indicate return value to callers that they cannot request
+interrupts from emulated bridge.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Cc: stable@vger.kernel.org
+---
+ drivers/pci/controller/pci-mvebu.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 5bf1889cfb38..0a8b552364aa 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -787,6 +787,15 @@ static struct pci_ops mvebu_pcie_ops = {
+ .write = mvebu_pcie_wr_conf,
+ };
+
++static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
++{
++ /* Interrupt support on mvebu emulated bridges is not implemented yet */
++ if (dev->bus->number == 0)
++ return 0; /* Proper return code 0 == NO_IRQ */
++
++ return of_irq_parse_and_map_pci(dev, slot, pin);
++}
++
+ static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
+ const struct resource *res,
+ resource_size_t start,
+@@ -1279,6 +1288,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+ bridge->sysdata = pcie;
+ bridge->ops = &mvebu_pcie_ops;
+ bridge->align_resource = mvebu_pcie_align_resource;
++ bridge->map_irq = mvebu_pcie_map_irq;
+
+ return pci_host_probe(bridge);
+ }
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0011-PCI-mvebu-Propagate-errors-when-updating-PCI_IO_BASE.patch b/pkgs/patches-linux-5.15/0011-PCI-mvebu-Propagate-errors-when-updating-PCI_IO_BASE.patch
new file mode 100644
index 0000000..225965c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0011-PCI-mvebu-Propagate-errors-when-updating-PCI_IO_BASE.patch
@@ -0,0 +1,195 @@
+From 14d8c749e493bd09b00e5766ba96631794e1a109 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 11:34:50 +0200
+Subject: [PATCH 11/90] PCI: mvebu: Propagate errors when updating PCI_IO_BASE
+ and PCI_MEM_BASE registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Properly propagate failure from mvebu_pcie_add_windows() function back to
+the caller mvebu_pci_bridge_emul_base_conf_write() and correctly updates
+PCI_IO_BASE, PCI_MEM_BASE and PCI_IO_BASE_UPPER16 registers on error.
+On error set base value higher than limit value which indicates that
+address range is disabled. When IO is unsupported then let IO registers
+zeroed as required by PCIe base specification.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Cc: stable@vger.kernel.org
+---
+ drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++++++----------
+ 1 file changed, 55 insertions(+), 27 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 0a8b552364aa..32aa78059e96 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -329,7 +329,7 @@ static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port,
+ * areas each having a power of two size. We start from the largest
+ * one (i.e highest order bit set in the size).
+ */
+-static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
++static int mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
+ unsigned int target, unsigned int attribute,
+ phys_addr_t base, size_t size,
+ phys_addr_t remap)
+@@ -350,7 +350,7 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
+ &base, &end, ret);
+ mvebu_pcie_del_windows(port, base - size_mapped,
+ size_mapped);
+- return;
++ return ret;
+ }
+
+ size -= sz;
+@@ -359,16 +359,20 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
+ if (remap != MVEBU_MBUS_NO_REMAP)
+ remap += sz;
+ }
++
++ return 0;
+ }
+
+-static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
++static int mvebu_pcie_set_window(struct mvebu_pcie_port *port,
+ unsigned int target, unsigned int attribute,
+ const struct mvebu_pcie_window *desired,
+ struct mvebu_pcie_window *cur)
+ {
++ int ret;
++
+ if (desired->base == cur->base && desired->remap == cur->remap &&
+ desired->size == cur->size)
+- return;
++ return 0;
+
+ if (cur->size != 0) {
+ mvebu_pcie_del_windows(port, cur->base, cur->size);
+@@ -383,30 +387,35 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
+ }
+
+ if (desired->size == 0)
+- return;
++ return 0;
++
++ ret = mvebu_pcie_add_windows(port, target, attribute, desired->base,
++ desired->size, desired->remap);
++ if (ret) {
++ cur->size = 0;
++ cur->base = 0;
++ return ret;
++ }
+
+- mvebu_pcie_add_windows(port, target, attribute, desired->base,
+- desired->size, desired->remap);
+ *cur = *desired;
++ return 0;
+ }
+
+-static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
++static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
+ {
+ struct mvebu_pcie_window desired = {};
+ struct pci_bridge_emul_conf *conf = &port->bridge.conf;
+
+ /* Are the new iobase/iolimit values invalid? */
+ if (conf->iolimit < conf->iobase ||
+- conf->iolimitupper < conf->iobaseupper) {
+- mvebu_pcie_set_window(port, port->io_target, port->io_attr,
+- &desired, &port->iowin);
+- return;
+- }
++ conf->iolimitupper < conf->iobaseupper)
++ return mvebu_pcie_set_window(port, port->io_target, port->io_attr,
++ &desired, &port->iowin);
+
+ if (!mvebu_has_ioport(port)) {
+ dev_WARN(&port->pcie->pdev->dev,
+ "Attempt to set IO when IO is disabled\n");
+- return;
++ return -EOPNOTSUPP;
+ }
+
+ /*
+@@ -424,21 +433,19 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
+ desired.remap) +
+ 1;
+
+- mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired,
+- &port->iowin);
++ return mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired,
++ &port->iowin);
+ }
+
+-static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
++static int mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
+ {
+ struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
+ struct pci_bridge_emul_conf *conf = &port->bridge.conf;
+
+ /* Are the new membase/memlimit values invalid? */
+- if (conf->memlimit < conf->membase) {
+- mvebu_pcie_set_window(port, port->mem_target, port->mem_attr,
+- &desired, &port->memwin);
+- return;
+- }
++ if (conf->memlimit < conf->membase)
++ return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr,
++ &desired, &port->memwin);
+
+ /*
+ * We read the PCI-to-PCI bridge emulated registers, and
+@@ -450,8 +457,8 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
+ desired.size = (((conf->memlimit & 0xFFF0) << 16) | 0xFFFFF) -
+ desired.base + 1;
+
+- mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired,
+- &port->memwin);
++ return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired,
++ &port->memwin);
+ }
+
+ static pci_bridge_emul_read_status_t
+@@ -576,15 +583,36 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
+ break;
+
+ case PCI_IO_BASE:
+- mvebu_pcie_handle_iobase_change(port);
++ if ((mask & 0xffff) && mvebu_pcie_handle_iobase_change(port)) {
++ /* On error disable IO range */
++ conf->iobase &= ~0xf0;
++ conf->iolimit &= ~0xf0;
++ conf->iobaseupper = cpu_to_le16(0x0000);
++ conf->iolimitupper = cpu_to_le16(0x0000);
++ if (mvebu_has_ioport(port))
++ conf->iobase |= 0xf0;
++ }
+ break;
+
+ case PCI_MEMORY_BASE:
+- mvebu_pcie_handle_membase_change(port);
++ if (mvebu_pcie_handle_membase_change(port)) {
++ /* On error disable mem range */
++ conf->membase = cpu_to_le16(le16_to_cpu(conf->membase) & ~0xfff0);
++ conf->memlimit = cpu_to_le16(le16_to_cpu(conf->memlimit) & ~0xfff0);
++ conf->membase = cpu_to_le16(le16_to_cpu(conf->membase) | 0xfff0);
++ }
+ break;
+
+ case PCI_IO_BASE_UPPER16:
+- mvebu_pcie_handle_iobase_change(port);
++ if (mvebu_pcie_handle_iobase_change(port)) {
++ /* On error disable IO range */
++ conf->iobase &= ~0xf0;
++ conf->iolimit &= ~0xf0;
++ conf->iobaseupper = cpu_to_le16(0x0000);
++ conf->iolimitupper = cpu_to_le16(0x0000);
++ if (mvebu_has_ioport(port))
++ conf->iobase |= 0xf0;
++ }
+ break;
+
+ case PCI_PRIMARY_BUS:
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0012-PCI-mvebu-Set-PCI-Bridge-Class-Code-to-PCI-Bridge.patch b/pkgs/patches-linux-5.15/0012-PCI-mvebu-Set-PCI-Bridge-Class-Code-to-PCI-Bridge.patch
new file mode 100644
index 0000000..10d6b49
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0012-PCI-mvebu-Set-PCI-Bridge-Class-Code-to-PCI-Bridge.patch
@@ -0,0 +1,73 @@
+From 0d7c624cae34b5a31715fc3bd2614315d2982bd9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 11:51:43 +0200
+Subject: [PATCH 12/90] PCI: mvebu: Set PCI Bridge Class Code to PCI Bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The default value of Class Code of this bridge corresponds to a Memory
+controller, though. This is probably relict from the past when old
+Marvell/Galileo PCI-based controllers were used as standalone PCI device
+for connecting SDRAM or workaround for PCs with broken BIOS. Details are
+in commit 36de23a4c5f0 ("MIPS: Cobalt: Explain GT64111 early PCI fixup").
+
+Change the Class Code to correspond to a PCI Bridge.
+
+Add comment explaining this change.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Cc: stable@vger.kernel.org
+---
+ drivers/pci/controller/pci-mvebu.c | 28 +++++++++++++++++++++++++++-
+ 1 file changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 32aa78059e96..68aa94a258ff 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -233,7 +233,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
+
+ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ {
+- u32 ctrl, cmd, mask;
++ u32 ctrl, cmd, dev_rev, mask;
+
+ /* Setup PCIe controller to Root Complex mode. */
+ ctrl = mvebu_readl(port, PCIE_CTRL_OFF);
+@@ -245,6 +245,32 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ mvebu_writel(port, cmd, PCIE_CMD_OFF);
+
++ /*
++ * Change Class Code of PCI Bridge device to PCI Bridge (0x6004)
++ * because default value is Memory controller (0x5080).
++ *
++ * Note that this mvebu PCI Bridge does not have compliant Type 1
++ * Configuration Space. Header Type is reported as Type 0 and it
++ * has format of Type 0 config space.
++ *
++ * Moreover Type 0 BAR registers (ranges 0x10 - 0x28 and 0x30 - 0x34)
++ * have the same format in Marvell's specification as in PCIe
++ * specification, but their meaning is totally different and they do
++ * different things: they are aliased into internal mvebu registers
++ * (e.g. PCIE_BAR_LO_OFF) and these should not be changed or
++ * reconfigured by pci device drivers.
++ *
++ * Therefore driver uses emulation of PCI Bridge which emulates
++ * access to configuration space via internal mvebu registers or
++ * emulated configuration buffer. Driver access these PCI Bridge
++ * directly for simplification, but these registers can be accessed
++ * also via standard mvebu way for accessing PCI config space.
++ */
++ dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF);
++ dev_rev &= ~0xffffff00;
++ dev_rev |= (PCI_CLASS_BRIDGE_PCI << 8) << 8;
++ mvebu_writel(port, dev_rev, PCIE_DEV_REV_OFF);
++
+ /* Point PCIe unit MBUS decode windows to DRAM space. */
+ mvebu_pcie_setup_wins(port);
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0013-MAINTAINERS-Add-Pali-Roh-r-as-pci-mvebu.c-maintainer.patch b/pkgs/patches-linux-5.15/0013-MAINTAINERS-Add-Pali-Roh-r-as-pci-mvebu.c-maintainer.patch
new file mode 100644
index 0000000..96c4dd3
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0013-MAINTAINERS-Add-Pali-Roh-r-as-pci-mvebu.c-maintainer.patch
@@ -0,0 +1,30 @@
+From b8db9ac56b79c68e4501491b259b19487eccd7db Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 25 Nov 2021 13:51:15 +0100
+Subject: [PATCH 13/90] =?UTF-8?q?MAINTAINERS:=20Add=20Pali=20Roh=C3=A1r=20?=
+ =?UTF-8?q?as=20pci-mvebu.c=20maintainer?=
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
+---
+ MAINTAINERS | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index edc32575828b..5bb588d01805 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -14396,6 +14396,7 @@ F: drivers/pci/controller/mobiveil/pcie-mobiveil*
+
+ PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
+ M: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
++M: Pali Rohár <pali@kernel.org>
+ L: linux-pci@vger.kernel.org
+ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+ S: Maintained
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0014-PCI-pci-bridge-emul-Make-struct-pci_bridge_emul_ops-.patch b/pkgs/patches-linux-5.15/0014-PCI-pci-bridge-emul-Make-struct-pci_bridge_emul_ops-.patch
new file mode 100644
index 0000000..014b632
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0014-PCI-pci-bridge-emul-Make-struct-pci_bridge_emul_ops-.patch
@@ -0,0 +1,61 @@
+From 002ef7f63fb628d49f079a02ab0d8f3e08efb94a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 16 Nov 2021 10:54:50 +0100
+Subject: [PATCH 14/90] PCI: pci-bridge-emul: Make struct pci_bridge_emul_ops
+ as const
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It is read-only constant structure, so properly mark it with const keyword.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 2 +-
+ drivers/pci/controller/pci-mvebu.c | 2 +-
+ drivers/pci/pci-bridge-emul.h | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index 7cc2c54daad0..3fe4d3329267 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -944,7 +944,7 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ }
+ }
+
+-static struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
++static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
+ .read_base = advk_pci_bridge_emul_base_conf_read,
+ .write_base = advk_pci_bridge_emul_base_conf_write,
+ .read_pcie = advk_pci_bridge_emul_pcie_conf_read,
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 68aa94a258ff..2ecc1ab12249 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -709,7 +709,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ }
+ }
+
+-static struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
++static const struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
+ .read_base = mvebu_pci_bridge_emul_base_conf_read,
+ .write_base = mvebu_pci_bridge_emul_base_conf_write,
+ .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
+diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h
+index 49bbd37ee318..0690b6369755 100644
+--- a/drivers/pci/pci-bridge-emul.h
++++ b/drivers/pci/pci-bridge-emul.h
+@@ -112,7 +112,7 @@ struct pci_bridge_reg_behavior;
+ struct pci_bridge_emul {
+ struct pci_bridge_emul_conf conf;
+ struct pci_bridge_emul_pcie_conf pcie_conf;
+- struct pci_bridge_emul_ops *ops;
++ const struct pci_bridge_emul_ops *ops;
+ struct pci_bridge_reg_behavior *pci_regs_behavior;
+ struct pci_bridge_reg_behavior *pcie_cap_regs_behavior;
+ void *data;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0015-PCI-pci-bridge-emul-Rename-PCI_BRIDGE_EMUL_NO_PREFET.patch b/pkgs/patches-linux-5.15/0015-PCI-pci-bridge-emul-Rename-PCI_BRIDGE_EMUL_NO_PREFET.patch
new file mode 100644
index 0000000..2226c30
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0015-PCI-pci-bridge-emul-Rename-PCI_BRIDGE_EMUL_NO_PREFET.patch
@@ -0,0 +1,69 @@
+From bb5a2178ce576f3100ae3679599777d823f459a4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 16 Nov 2021 10:59:52 +0100
+Subject: [PATCH 15/90] PCI: pci-bridge-emul: Rename
+ PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR to PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This flag describe whether PCI bridge supports forwarding of prefetchable
+memory requests in given range between primary and secondary buses. It does
+not specify if bridge has support for prefetchable memory BAR (moreover
+this pci-bridge-emul.c driver does not provide support for BARs).
+
+So change name of this flag to be less misleading and add comment.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 2 +-
+ drivers/pci/pci-bridge-emul.c | 2 +-
+ drivers/pci/pci-bridge-emul.h | 6 +++++-
+ 3 files changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 2ecc1ab12249..2e10ade660a1 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -747,7 +747,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ bridge->data = port;
+ bridge->ops = &mvebu_pci_bridge_emul_ops;
+
+- return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR);
++ return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD);
+ }
+
+ static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
+diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
+index c994ebec2360..ceacba03e714 100644
+--- a/drivers/pci/pci-bridge-emul.c
++++ b/drivers/pci/pci-bridge-emul.c
+@@ -377,7 +377,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
+ ~(BIT(10) << 16);
+ }
+
+- if (flags & PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR) {
++ if (flags & PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD) {
+ bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].ro = ~0;
+ bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0;
+ }
+diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h
+index 0690b6369755..087b5fa01bcf 100644
+--- a/drivers/pci/pci-bridge-emul.h
++++ b/drivers/pci/pci-bridge-emul.h
+@@ -120,7 +120,11 @@ struct pci_bridge_emul {
+ };
+
+ enum {
+- PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR = BIT(0),
++ /*
++ * PCI bridge does not support forwarding of prefetchable memory
++ * requests between primary and secondary buses.
++ */
++ PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD = BIT(0),
+ };
+
+ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0016-PCI-pci-bridge-emul-Add-support-for-new-flag-PCI_BRI.patch b/pkgs/patches-linux-5.15/0016-PCI-pci-bridge-emul-Add-support-for-new-flag-PCI_BRI.patch
new file mode 100644
index 0000000..5d0dad6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0016-PCI-pci-bridge-emul-Add-support-for-new-flag-PCI_BRI.patch
@@ -0,0 +1,62 @@
+From 369e0346a0e54c96ef8776bd32d810c4e1cbbb1d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 16 Nov 2021 16:53:33 +0100
+Subject: [PATCH 16/90] PCI: pci-bridge-emul: Add support for new flag
+ PCI_BRIDGE_EMUL_NO_IO_FORWARD
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Like PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD, this new flag specifies that
+emulated PCI bridge does not support forwarding of IO requests in given
+range between primary and secondary buses. This flag should be used as
+argument for pci_bridge_emul_init() for hardware setup without IO support.
+
+Setting this flag cause that IO base and limit registers are read-only.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/pci-bridge-emul.c | 9 +++++++++
+ drivers/pci/pci-bridge-emul.h | 6 ++++++
+ 2 files changed, 15 insertions(+)
+
+diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
+index ceacba03e714..a16f9e30099e 100644
+--- a/drivers/pci/pci-bridge-emul.c
++++ b/drivers/pci/pci-bridge-emul.c
+@@ -382,6 +382,15 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
+ bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0;
+ }
+
++ if (flags & PCI_BRIDGE_EMUL_NO_IO_FORWARD) {
++ bridge->pci_regs_behavior[PCI_COMMAND / 4].ro |= PCI_COMMAND_IO;
++ bridge->pci_regs_behavior[PCI_COMMAND / 4].rw &= ~PCI_COMMAND_IO;
++ bridge->pci_regs_behavior[PCI_IO_BASE / 4].ro |= GENMASK(15, 0);
++ bridge->pci_regs_behavior[PCI_IO_BASE / 4].rw &= ~GENMASK(15, 0);
++ bridge->pci_regs_behavior[PCI_IO_BASE_UPPER16 / 4].ro = ~0;
++ bridge->pci_regs_behavior[PCI_IO_BASE_UPPER16 / 4].rw = 0;
++ }
++
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(pci_bridge_emul_init);
+diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h
+index 087b5fa01bcf..4953274cac18 100644
+--- a/drivers/pci/pci-bridge-emul.h
++++ b/drivers/pci/pci-bridge-emul.h
+@@ -125,6 +125,12 @@ enum {
+ * requests between primary and secondary buses.
+ */
+ PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD = BIT(0),
++
++ /*
++ * PCI bridge does not support forwarding of IO requests between
++ * primary and secondary buses.
++ */
++ PCI_BRIDGE_EMUL_NO_IO_FORWARD = BIT(1),
+ };
+
+ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0017-PCI-mvebu-Add-help-string-for-CONFIG_PCI_MVEBU-optio.patch b/pkgs/patches-linux-5.15/0017-PCI-mvebu-Add-help-string-for-CONFIG_PCI_MVEBU-optio.patch
new file mode 100644
index 0000000..36e7147
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0017-PCI-mvebu-Add-help-string-for-CONFIG_PCI_MVEBU-optio.patch
@@ -0,0 +1,34 @@
+From 3eaa66f27317eff5741cc91279e19e00c22f5643 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sun, 31 Oct 2021 22:57:40 +0100
+Subject: [PATCH 17/90] PCI: mvebu: Add help string for CONFIG_PCI_MVEBU option
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There is no description for CONFIG_PCI_MVEBU option. Add it.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
+---
+ drivers/pci/controller/Kconfig | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
+index a4c99ebff85e..46fa9bbff177 100644
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -10,6 +10,10 @@ config PCI_MVEBU
+ depends on ARM
+ depends on OF
+ select PCI_BRIDGE_EMUL
++ help
++ Add support for Marvell EBU PCIe controller. This PCIe controller
++ is used on 32-bit Marvell ARM SoCs: Dove, Kirkwood, Armada 370,
++ Armada XP, Armada 375, Armada 38x and Armada 39x.
+
+ config PCI_AARDVARK
+ tristate "Aardvark PCIe controller"
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0018-PCI-mvebu-Remove-duplicate-nports-assignment.patch b/pkgs/patches-linux-5.15/0018-PCI-mvebu-Remove-duplicate-nports-assignment.patch
new file mode 100644
index 0000000..616ca2b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0018-PCI-mvebu-Remove-duplicate-nports-assignment.patch
@@ -0,0 +1,33 @@
+From 4a4519b81a342e4fe0616d839f07c215325aed86 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 16 Nov 2021 20:18:18 +0100
+Subject: [PATCH 18/90] PCI: mvebu: Remove duplicate nports assignment
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Member pcie->nports is initialized to correct value before the previous
+for-loop. There is not need to initialize it more times.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
+---
+ drivers/pci/controller/pci-mvebu.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 2e10ade660a1..016f709b3067 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -1337,8 +1337,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+ mvebu_pcie_set_local_bus_nr(port, 0);
+ }
+
+- pcie->nports = i;
+-
+ bridge->sysdata = pcie;
+ bridge->ops = &mvebu_pcie_ops;
+ bridge->align_resource = mvebu_pcie_align_resource;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0019-PCI-mvebu-Set-PCI_BRIDGE_EMUL_NO_IO_FORWARD-when-IO-.patch b/pkgs/patches-linux-5.15/0019-PCI-mvebu-Set-PCI_BRIDGE_EMUL_NO_IO_FORWARD-when-IO-.patch
new file mode 100644
index 0000000..02e364a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0019-PCI-mvebu-Set-PCI_BRIDGE_EMUL_NO_IO_FORWARD-when-IO-.patch
@@ -0,0 +1,114 @@
+From 1269339f0ff159e788117fb1329159b527da490f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 16 Nov 2021 17:22:03 +0100
+Subject: [PATCH 19/90] PCI: mvebu: Set PCI_BRIDGE_EMUL_NO_IO_FORWARD when IO
+ is unsupported
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This will make PCI bridge to return zeros when accessing IO base and limit
+registers, as required by PCIe base specification.
+
+This allows to remove adhoc checks around mvebu_pcie_handle_iobase_change()
+function for unsupported IO ranges. PCI_BRIDGE_EMUL_NO_IO_FORWARD ensures
+that there will be no non-zeros write to IO registers when IO is not
+supported.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 29 ++++++++++-------------------
+ 1 file changed, 10 insertions(+), 19 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 016f709b3067..551f55af5226 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -438,12 +438,6 @@ static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
+ return mvebu_pcie_set_window(port, port->io_target, port->io_attr,
+ &desired, &port->iowin);
+
+- if (!mvebu_has_ioport(port)) {
+- dev_WARN(&port->pcie->pdev->dev,
+- "Attempt to set IO when IO is disabled\n");
+- return -EOPNOTSUPP;
+- }
+-
+ /*
+ * We read the PCI-to-PCI bridge emulated registers, and
+ * calculate the base address and size of the address decoding
+@@ -599,24 +593,18 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
+
+ switch (reg) {
+ case PCI_COMMAND:
+- if (!mvebu_has_ioport(port)) {
+- conf->command = cpu_to_le16(
+- le16_to_cpu(conf->command) & ~PCI_COMMAND_IO);
+- new &= ~PCI_COMMAND_IO;
+- }
+-
+ mvebu_writel(port, new, PCIE_CMD_OFF);
+ break;
+
+ case PCI_IO_BASE:
+- if ((mask & 0xffff) && mvebu_pcie_handle_iobase_change(port)) {
++ if ((mask & 0xffff) && mvebu_has_ioport(port) &&
++ mvebu_pcie_handle_iobase_change(port)) {
+ /* On error disable IO range */
+ conf->iobase &= ~0xf0;
+ conf->iolimit &= ~0xf0;
++ conf->iobase |= 0xf0;
+ conf->iobaseupper = cpu_to_le16(0x0000);
+ conf->iolimitupper = cpu_to_le16(0x0000);
+- if (mvebu_has_ioport(port))
+- conf->iobase |= 0xf0;
+ }
+ break;
+
+@@ -630,14 +618,14 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
+ break;
+
+ case PCI_IO_BASE_UPPER16:
+- if (mvebu_pcie_handle_iobase_change(port)) {
++ if (mvebu_has_ioport(port) &&
++ mvebu_pcie_handle_iobase_change(port)) {
+ /* On error disable IO range */
+ conf->iobase &= ~0xf0;
+ conf->iolimit &= ~0xf0;
++ conf->iobase |= 0xf0;
+ conf->iobaseupper = cpu_to_le16(0x0000);
+ conf->iolimitupper = cpu_to_le16(0x0000);
+- if (mvebu_has_ioport(port))
+- conf->iobase |= 0xf0;
+ }
+ break;
+
+@@ -722,6 +710,7 @@ static const struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
+ */
+ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ {
++ unsigned int bridge_flags = PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD;
+ struct pci_bridge_emul *bridge = &port->bridge;
+ u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP);
+ u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS);
+@@ -735,6 +724,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ /* We support 32 bits I/O addressing */
+ bridge->conf.iobase = PCI_IO_RANGE_TYPE_32;
+ bridge->conf.iolimit = PCI_IO_RANGE_TYPE_32;
++ } else {
++ bridge_flags |= PCI_BRIDGE_EMUL_NO_IO_FORWARD;
+ }
+
+ /*
+@@ -747,7 +738,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ bridge->data = port;
+ bridge->ops = &mvebu_pci_bridge_emul_ops;
+
+- return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD);
++ return pci_bridge_emul_init(bridge, bridge_flags);
+ }
+
+ static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0020-PCI-mvebu-Properly-initialize-vendor-device-and-revi.patch b/pkgs/patches-linux-5.15/0020-PCI-mvebu-Properly-initialize-vendor-device-and-revi.patch
new file mode 100644
index 0000000..3025003
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0020-PCI-mvebu-Properly-initialize-vendor-device-and-revi.patch
@@ -0,0 +1,42 @@
+From 4ae6a8f60faf1d918e7421410d04386ee8b06894 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 14:48:21 +0200
+Subject: [PATCH 20/90] PCI: mvebu: Properly initialize vendor, device and
+ revision of emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+With this change also PCI vendor id is read from mvebu registers.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 551f55af5226..94ef00b6d697 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -712,13 +712,14 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ {
+ unsigned int bridge_flags = PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD;
+ struct pci_bridge_emul *bridge = &port->bridge;
++ u32 dev_id = mvebu_readl(port, PCIE_DEV_ID_OFF);
++ u32 dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF);
+ u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP);
+ u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS);
+
+- bridge->conf.vendor = PCI_VENDOR_ID_MARVELL;
+- bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
+- bridge->conf.class_revision =
+- mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff;
++ bridge->conf.vendor = cpu_to_le16(dev_id & 0xffff);
++ bridge->conf.device = cpu_to_le16(dev_id >> 16);
++ bridge->conf.class_revision = cpu_to_le32(dev_rev & 0xff);
+
+ if (mvebu_has_ioport(port)) {
+ /* We support 32 bits I/O addressing */
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0021-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCAP-register.patch b/pkgs/patches-linux-5.15/0021-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCAP-register.patch
new file mode 100644
index 0000000..1239337
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0021-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCAP-register.patch
@@ -0,0 +1,34 @@
+From 91948e605b7b0f3f65919d6e19ef4e39535faf6a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 11:54:37 +0200
+Subject: [PATCH 21/90] PCI: mvebu: Update comment for PCI_EXP_LNKCAP register
+ on emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reason for clearing this bit is because mvebu hw returns incorrectly this bit set to 1.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 94ef00b6d697..1aac65977b97 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -546,8 +546,8 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+
+ case PCI_EXP_LNKCAP:
+ /*
+- * PCIe requires the clock power management capability to be
+- * hard-wired to zero for downstream ports
++ * PCIe requires that the Clock Power Management capability bit
++ * is hard-wired to zero for downstream ports but HW returns 1.
+ */
+ *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) &
+ ~PCI_EXP_LNKCAP_CLKPM;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0022-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCTL-register.patch b/pkgs/patches-linux-5.15/0022-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCTL-register.patch
new file mode 100644
index 0000000..49bd9e2
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0022-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCTL-register.patch
@@ -0,0 +1,39 @@
+From b47763b59b70859fdcb98e228ac5d762cca39ab7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 11:57:20 +0200
+Subject: [PATCH 22/90] PCI: mvebu: Update comment for PCI_EXP_LNKCTL register
+ on emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Logic and code for clearing PCI_EXP_LNKCTL_CLKREQ_EN bit is correct, but
+comment describing it is misleading. PCI_EXP_LNKCTL_CLKREQ_EN bit should be
+hardwired to zero but mvebu hw allows to change it.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 1aac65977b97..dffa330de174 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -663,10 +663,9 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+
+ case PCI_EXP_LNKCTL:
+ /*
+- * If we don't support CLKREQ, we must ensure that the
+- * CLKREQ enable bit always reads zero. Since we haven't
+- * had this capability, and it's dependent on board wiring,
+- * disable it for the time being.
++ * PCIe requires that the Enable Clock Power Management bit
++ * is hard-wired to zero for downstream ports but HW allows
++ * to change it.
+ */
+ new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0023-PCI-mvebu-Fix-reporting-Data-Link-Layer-Link-Active-.patch b/pkgs/patches-linux-5.15/0023-PCI-mvebu-Fix-reporting-Data-Link-Layer-Link-Active-.patch
new file mode 100644
index 0000000..9c4b93c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0023-PCI-mvebu-Fix-reporting-Data-Link-Layer-Link-Active-.patch
@@ -0,0 +1,50 @@
+From 65913d4ca9e1f51ddd086b530373da620c934502 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 13:16:37 +0200
+Subject: [PATCH 23/90] PCI: mvebu: Fix reporting Data Link Layer Link Active
+ on emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add support for reporting PCI_EXP_LNKSTA_DLLLA bit in Link Control register
+on emulated bridge via PCIE_STAT_OFF reg. Function mvebu_pcie_link_up()
+already parses this register and returns if Data Link is Active or not.
+
+Also correctly indicate DLLLA capability via PCI_EXP_LNKCAP_DLLLARC bit in
+Link Control Capability register which is required for reporting DLLLA bit.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index dffa330de174..a075ba26cff1 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -548,13 +548,18 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+ /*
+ * PCIe requires that the Clock Power Management capability bit
+ * is hard-wired to zero for downstream ports but HW returns 1.
++ * Additionally enable Data Link Layer Link Active Reporting
++ * Capable bit as DL_Active indication is provided too.
+ */
+- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) &
+- ~PCI_EXP_LNKCAP_CLKPM;
++ *value = (mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) &
++ ~PCI_EXP_LNKCAP_CLKPM) | PCI_EXP_LNKCAP_DLLLARC;
+ break;
+
+ case PCI_EXP_LNKCTL:
+- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
++ /* DL_Active indication is provided via PCIE_STAT_OFF */
++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL) |
++ (mvebu_pcie_link_up(port) ?
++ (PCI_EXP_LNKSTA_DLLLA << 16) : 0);
+ break;
+
+ case PCI_EXP_SLTCTL:
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0024-PCI-pci-bridge-emul-Re-arrange-register-tests.patch b/pkgs/patches-linux-5.15/0024-PCI-pci-bridge-emul-Re-arrange-register-tests.patch
new file mode 100644
index 0000000..6fee553
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0024-PCI-pci-bridge-emul-Re-arrange-register-tests.patch
@@ -0,0 +1,117 @@
+From fd539c7270dca28ee9943f7b93d445481ab71721 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 2 Feb 2021 13:45:28 +0000
+Subject: [PATCH 24/90] PCI: pci-bridge-emul: Re-arrange register tests
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Re-arrange the tests for which sets of registers are being accessed so that
+it is easier to add further regions later. No functional change.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+[pali: Fix reading old value in pci_bridge_emul_conf_write]
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/pci-bridge-emul.c | 61 ++++++++++++++++++-----------------
+ 1 file changed, 31 insertions(+), 30 deletions(-)
+
+diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
+index a16f9e30099e..a956408834d6 100644
+--- a/drivers/pci/pci-bridge-emul.c
++++ b/drivers/pci/pci-bridge-emul.c
+@@ -422,25 +422,25 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
+ __le32 *cfgspace;
+ const struct pci_bridge_reg_behavior *behavior;
+
+- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) {
+- *value = 0;
+- return PCIBIOS_SUCCESSFUL;
+- }
+-
+- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) {
++ if (reg < PCI_BRIDGE_CONF_END) {
++ /* Emulated PCI space */
++ read_op = bridge->ops->read_base;
++ cfgspace = (__le32 *) &bridge->conf;
++ behavior = bridge->pci_regs_behavior;
++ } else if (!bridge->has_pcie) {
++ /* PCIe space is not implemented, and no PCI capabilities */
+ *value = 0;
+ return PCIBIOS_SUCCESSFUL;
+- }
+-
+- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) {
++ } else if (reg < PCI_CAP_PCIE_END) {
++ /* Our emulated PCIe capability */
+ reg -= PCI_CAP_PCIE_START;
+ read_op = bridge->ops->read_pcie;
+ cfgspace = (__le32 *) &bridge->pcie_conf;
+ behavior = bridge->pcie_cap_regs_behavior;
+ } else {
+- read_op = bridge->ops->read_base;
+- cfgspace = (__le32 *) &bridge->conf;
+- behavior = bridge->pci_regs_behavior;
++ /* Beyond our PCIe space */
++ *value = 0;
++ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (read_op)
+@@ -484,11 +484,27 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+ __le32 *cfgspace;
+ const struct pci_bridge_reg_behavior *behavior;
+
+- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END)
+- return PCIBIOS_SUCCESSFUL;
++ ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old);
++ if (ret != PCIBIOS_SUCCESSFUL)
++ return ret;
+
+- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END)
++ if (reg < PCI_BRIDGE_CONF_END) {
++ /* Emulated PCI space */
++ write_op = bridge->ops->write_base;
++ cfgspace = (__le32 *) &bridge->conf;
++ behavior = bridge->pci_regs_behavior;
++ } else if (!bridge->has_pcie) {
++ /* PCIe space is not implemented, and no PCI capabilities */
+ return PCIBIOS_SUCCESSFUL;
++ } else if (reg < PCI_CAP_PCIE_END) {
++ /* Our emulated PCIe capability */
++ reg -= PCI_CAP_PCIE_START;
++ write_op = bridge->ops->write_pcie;
++ cfgspace = (__le32 *) &bridge->pcie_conf;
++ behavior = bridge->pcie_cap_regs_behavior;
++ } else {
++ return PCIBIOS_SUCCESSFUL;
++ }
+
+ shift = (where & 0x3) * 8;
+
+@@ -501,21 +517,6 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+- ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old);
+- if (ret != PCIBIOS_SUCCESSFUL)
+- return ret;
+-
+- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) {
+- reg -= PCI_CAP_PCIE_START;
+- write_op = bridge->ops->write_pcie;
+- cfgspace = (__le32 *) &bridge->pcie_conf;
+- behavior = bridge->pcie_cap_regs_behavior;
+- } else {
+- write_op = bridge->ops->write_base;
+- cfgspace = (__le32 *) &bridge->conf;
+- behavior = bridge->pci_regs_behavior;
+- }
+-
+ /* Keep all bits, except the RW bits */
+ new = old & (~mask | ~behavior[reg / 4].rw);
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0025-PCI-pci-bridge-emul-Add-support-for-PCIe-extended-ca.patch b/pkgs/patches-linux-5.15/0025-PCI-pci-bridge-emul-Add-support-for-PCIe-extended-ca.patch
new file mode 100644
index 0000000..27e7b57
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0025-PCI-pci-bridge-emul-Add-support-for-PCIe-extended-ca.patch
@@ -0,0 +1,179 @@
+From 09ccac06bf207e939bd00487bb8e2b7bdc1cddf2 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 2 Feb 2021 13:57:04 +0000
+Subject: [PATCH 25/90] PCI: pci-bridge-emul: Add support for PCIe extended
+ capabilities
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add support for PCIe extended capabilities, which we just redirect to the
+emulating driver.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+[pali: Fix writing new value with W1C bits]
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/pci-bridge-emul.c | 77 +++++++++++++++++++++++------------
+ drivers/pci/pci-bridge-emul.h | 15 +++++++
+ 2 files changed, 67 insertions(+), 25 deletions(-)
+
+diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
+index a956408834d6..c4b9837006ff 100644
+--- a/drivers/pci/pci-bridge-emul.c
++++ b/drivers/pci/pci-bridge-emul.c
+@@ -437,10 +437,16 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
+ read_op = bridge->ops->read_pcie;
+ cfgspace = (__le32 *) &bridge->pcie_conf;
+ behavior = bridge->pcie_cap_regs_behavior;
+- } else {
+- /* Beyond our PCIe space */
++ } else if (reg < PCI_CFG_SPACE_SIZE) {
++ /* Rest of PCI space not implemented */
+ *value = 0;
+ return PCIBIOS_SUCCESSFUL;
++ } else {
++ /* PCIe extended capability space */
++ reg -= PCI_CFG_SPACE_SIZE;
++ read_op = bridge->ops->read_ext;
++ cfgspace = NULL;
++ behavior = NULL;
+ }
+
+ if (read_op)
+@@ -448,15 +454,20 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
+ else
+ ret = PCI_BRIDGE_EMUL_NOT_HANDLED;
+
+- if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED)
+- *value = le32_to_cpu(cfgspace[reg / 4]);
++ if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) {
++ if (cfgspace)
++ *value = le32_to_cpu(cfgspace[reg / 4]);
++ else
++ *value = 0;
++ }
+
+ /*
+ * Make sure we never return any reserved bit with a value
+ * different from 0.
+ */
+- *value &= behavior[reg / 4].ro | behavior[reg / 4].rw |
+- behavior[reg / 4].w1c;
++ if (behavior)
++ *value &= behavior[reg / 4].ro | behavior[reg / 4].rw |
++ behavior[reg / 4].w1c;
+
+ if (size == 1)
+ *value = (*value >> (8 * (where & 3))) & 0xff;
+@@ -502,8 +513,15 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+ write_op = bridge->ops->write_pcie;
+ cfgspace = (__le32 *) &bridge->pcie_conf;
+ behavior = bridge->pcie_cap_regs_behavior;
+- } else {
++ } else if (reg < PCI_CFG_SPACE_SIZE) {
++ /* Rest of PCI space not implemented */
+ return PCIBIOS_SUCCESSFUL;
++ } else {
++ /* PCIe extended capability space */
++ reg -= PCI_CFG_SPACE_SIZE;
++ write_op = bridge->ops->write_ext;
++ cfgspace = NULL;
++ behavior = NULL;
+ }
+
+ shift = (where & 0x3) * 8;
+@@ -517,29 +535,38 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+- /* Keep all bits, except the RW bits */
+- new = old & (~mask | ~behavior[reg / 4].rw);
++ if (behavior) {
++ /* Keep all bits, except the RW bits */
++ new = old & (~mask | ~behavior[reg / 4].rw);
+
+- /* Update the value of the RW bits */
+- new |= (value << shift) & (behavior[reg / 4].rw & mask);
++ /* Update the value of the RW bits */
++ new |= (value << shift) & (behavior[reg / 4].rw & mask);
+
+- /* Clear the W1C bits */
+- new &= ~((value << shift) & (behavior[reg / 4].w1c & mask));
++ /* Clear the W1C bits */
++ new &= ~((value << shift) & (behavior[reg / 4].w1c & mask));
++ } else {
++ new = old & ~mask;
++ new |= (value << shift) & mask;
++ }
+
+- /* Save the new value with the cleared W1C bits into the cfgspace */
+- cfgspace[reg / 4] = cpu_to_le32(new);
++ if (cfgspace) {
++ /* Save the new value with the cleared W1C bits into the cfgspace */
++ cfgspace[reg / 4] = cpu_to_le32(new);
++ }
+
+- /*
+- * Clear the W1C bits not specified by the write mask, so that the
+- * write_op() does not clear them.
+- */
+- new &= ~(behavior[reg / 4].w1c & ~mask);
++ if (behavior) {
++ /*
++ * Clear the W1C bits not specified by the write mask, so that the
++ * write_op() does not clear them.
++ */
++ new &= ~(behavior[reg / 4].w1c & ~mask);
+
+- /*
+- * Set the W1C bits specified by the write mask, so that write_op()
+- * knows about that they are to be cleared.
+- */
+- new |= (value << shift) & (behavior[reg / 4].w1c & mask);
++ /*
++ * Set the W1C bits specified by the write mask, so that write_op()
++ * knows about that they are to be cleared.
++ */
++ new |= (value << shift) & (behavior[reg / 4].w1c & mask);
++ }
+
+ if (write_op)
+ write_op(bridge, reg, old, new, mask);
+diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h
+index 4953274cac18..6b5f75b2ad02 100644
+--- a/drivers/pci/pci-bridge-emul.h
++++ b/drivers/pci/pci-bridge-emul.h
+@@ -90,6 +90,14 @@ struct pci_bridge_emul_ops {
+ */
+ pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge,
+ int reg, u32 *value);
++
++ /*
++ * Same as ->read_base(), except it is for reading from the
++ * PCIe extended capability configuration space.
++ */
++ pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge,
++ int reg, u32 *value);
++
+ /*
+ * Called when writing to the regular PCI bridge configuration
+ * space. old is the current value, new is the new value being
+@@ -105,6 +113,13 @@ struct pci_bridge_emul_ops {
+ */
+ void (*write_pcie)(struct pci_bridge_emul *bridge, int reg,
+ u32 old, u32 new, u32 mask);
++
++ /*
++ * Same as ->write_base(), except it is for writing from the
++ * PCIe extended capability configuration space.
++ */
++ void (*write_ext)(struct pci_bridge_emul *bridge, int reg,
++ u32 old, u32 new, u32 mask);
+ };
+
+ struct pci_bridge_reg_behavior;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0026-PCI-pci-bridge-emul-Add-support-for-PCI-Bridge-Subsy.patch b/pkgs/patches-linux-5.15/0026-PCI-pci-bridge-emul-Add-support-for-PCI-Bridge-Subsy.patch
new file mode 100644
index 0000000..bcaa3d1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0026-PCI-pci-bridge-emul-Add-support-for-PCI-Bridge-Subsy.patch
@@ -0,0 +1,169 @@
+From 097c7cbac9190481535ff83ac2271ede383e8f0a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 8 Oct 2021 11:39:06 +0200
+Subject: [PATCH 26/90] PCI: pci-bridge-emul: Add support for PCI Bridge
+ Subsystem Vendor ID capability
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This is read-only capability in PCI config space. Put it between base PCI
+capability and base PCI Express capability.
+
+Driver just have to specify subsystem_vendor_id and subsystem_id fields in
+emulated bridge structure and pci-bridge-emul takes care of correctly
+compose PCI Bridge Subsystem Vendor ID capability.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/pci-bridge-emul.c | 69 +++++++++++++++++++++++++----------
+ drivers/pci/pci-bridge-emul.h | 2 +
+ 2 files changed, 51 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
+index c4b9837006ff..a5b662cc89d0 100644
+--- a/drivers/pci/pci-bridge-emul.c
++++ b/drivers/pci/pci-bridge-emul.c
+@@ -21,8 +21,11 @@
+ #include "pci-bridge-emul.h"
+
+ #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF
++#define PCI_CAP_SSID_SIZEOF (PCI_SSVID_DEVICE_ID + 2)
++#define PCI_CAP_SSID_START PCI_BRIDGE_CONF_END
++#define PCI_CAP_SSID_END (PCI_CAP_SSID_START + PCI_CAP_SSID_SIZEOF)
+ #define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2)
+-#define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END
++#define PCI_CAP_PCIE_START PCI_CAP_SSID_END
+ #define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF)
+
+ /**
+@@ -315,6 +318,25 @@ struct pci_bridge_reg_behavior pcie_cap_regs_behavior[PCI_CAP_PCIE_SIZEOF / 4] =
+ },
+ };
+
++static pci_bridge_emul_read_status_t
++pci_bridge_emul_read_ssid(struct pci_bridge_emul *bridge, int reg, u32 *value)
++{
++ switch (reg) {
++ case PCI_CAP_LIST_ID:
++ *value = PCI_CAP_ID_SSVID |
++ (bridge->has_pcie ? (PCI_CAP_PCIE_START << 8) : 0);
++ return PCI_BRIDGE_EMUL_HANDLED;
++
++ case PCI_SSVID_VENDOR_ID:
++ *value = bridge->subsystem_vendor_id |
++ (bridge->subsystem_id << 16);
++ return PCI_BRIDGE_EMUL_HANDLED;
++
++ default:
++ return PCI_BRIDGE_EMUL_NOT_HANDLED;
++ }
++}
++
+ /*
+ * Initialize a pci_bridge_emul structure to represent a fake PCI
+ * bridge configuration space. The caller needs to have initialized
+@@ -341,9 +363,17 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
+ if (!bridge->pci_regs_behavior)
+ return -ENOMEM;
+
+- if (bridge->has_pcie) {
++ if (bridge->subsystem_vendor_id)
++ bridge->conf.capabilities_pointer = PCI_CAP_SSID_START;
++ else if (bridge->has_pcie)
+ bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START;
++ else
++ bridge->conf.capabilities_pointer = 0;
++
++ if (bridge->conf.capabilities_pointer)
+ bridge->conf.status |= cpu_to_le16(PCI_STATUS_CAP_LIST);
++
++ if (bridge->has_pcie) {
+ bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP;
+ bridge->pcie_conf.cap |= cpu_to_le16(PCI_EXP_TYPE_ROOT_PORT << 4);
+ bridge->pcie_cap_regs_behavior =
+@@ -427,26 +457,28 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
+ read_op = bridge->ops->read_base;
+ cfgspace = (__le32 *) &bridge->conf;
+ behavior = bridge->pci_regs_behavior;
+- } else if (!bridge->has_pcie) {
+- /* PCIe space is not implemented, and no PCI capabilities */
+- *value = 0;
+- return PCIBIOS_SUCCESSFUL;
+- } else if (reg < PCI_CAP_PCIE_END) {
++ } else if (reg >= PCI_CAP_SSID_START && reg < PCI_CAP_SSID_END && bridge->subsystem_vendor_id) {
++ /* Emulated PCI Bridge Subsystem Vendor ID capability */
++ reg -= PCI_CAP_SSID_START;
++ read_op = pci_bridge_emul_read_ssid;
++ cfgspace = NULL;
++ behavior = NULL;
++ } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) {
+ /* Our emulated PCIe capability */
+ reg -= PCI_CAP_PCIE_START;
+ read_op = bridge->ops->read_pcie;
+ cfgspace = (__le32 *) &bridge->pcie_conf;
+ behavior = bridge->pcie_cap_regs_behavior;
+- } else if (reg < PCI_CFG_SPACE_SIZE) {
+- /* Rest of PCI space not implemented */
+- *value = 0;
+- return PCIBIOS_SUCCESSFUL;
+- } else {
++ } else if (reg >= PCI_CFG_SPACE_SIZE && bridge->has_pcie) {
+ /* PCIe extended capability space */
+ reg -= PCI_CFG_SPACE_SIZE;
+ read_op = bridge->ops->read_ext;
+ cfgspace = NULL;
+ behavior = NULL;
++ } else {
++ /* Not implemented */
++ *value = 0;
++ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (read_op)
+@@ -504,24 +536,21 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+ write_op = bridge->ops->write_base;
+ cfgspace = (__le32 *) &bridge->conf;
+ behavior = bridge->pci_regs_behavior;
+- } else if (!bridge->has_pcie) {
+- /* PCIe space is not implemented, and no PCI capabilities */
+- return PCIBIOS_SUCCESSFUL;
+- } else if (reg < PCI_CAP_PCIE_END) {
++ } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) {
+ /* Our emulated PCIe capability */
+ reg -= PCI_CAP_PCIE_START;
+ write_op = bridge->ops->write_pcie;
+ cfgspace = (__le32 *) &bridge->pcie_conf;
+ behavior = bridge->pcie_cap_regs_behavior;
+- } else if (reg < PCI_CFG_SPACE_SIZE) {
+- /* Rest of PCI space not implemented */
+- return PCIBIOS_SUCCESSFUL;
+- } else {
++ } else if (reg >= PCI_CFG_SPACE_SIZE && bridge->has_pcie) {
+ /* PCIe extended capability space */
+ reg -= PCI_CFG_SPACE_SIZE;
+ write_op = bridge->ops->write_ext;
+ cfgspace = NULL;
+ behavior = NULL;
++ } else {
++ /* Not implemented */
++ return PCIBIOS_SUCCESSFUL;
+ }
+
+ shift = (where & 0x3) * 8;
+diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h
+index 6b5f75b2ad02..71392b67471d 100644
+--- a/drivers/pci/pci-bridge-emul.h
++++ b/drivers/pci/pci-bridge-emul.h
+@@ -132,6 +132,8 @@ struct pci_bridge_emul {
+ struct pci_bridge_reg_behavior *pcie_cap_regs_behavior;
+ void *data;
+ bool has_pcie;
++ u16 subsystem_vendor_id;
++ u16 subsystem_id;
+ };
+
+ enum {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0027-dt-bindings-PCI-mvebu-Add-num-lanes-property.patch b/pkgs/patches-linux-5.15/0027-dt-bindings-PCI-mvebu-Add-num-lanes-property.patch
new file mode 100644
index 0000000..339456b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0027-dt-bindings-PCI-mvebu-Add-num-lanes-property.patch
@@ -0,0 +1,114 @@
+From cca87cdc18be3c7ab387aad99cbc3d2e2a5e16dc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 3 Nov 2021 14:32:52 +0100
+Subject: [PATCH 27/90] dt-bindings: PCI: mvebu: Add num-lanes property
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Controller driver needs to correctly configure PCIe link if it contains 1
+or 4 SerDes PCIe lanes. Therefore add a new 'num-lanes' DT property for
+mvebu PCIe controller. Property 'num-lanes' seems to be de-facto standard
+way how number of lanes is specified in other PCIe controllers.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Rob Herring <robh@kernel.org>
+---
+ Documentation/devicetree/bindings/pci/mvebu-pci.txt | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt
+index 6173af6885f8..24225852bce0 100644
+--- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt
++++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt
+@@ -77,6 +77,7 @@ and the following optional properties:
+ - marvell,pcie-lane: the physical PCIe lane number, for ports having
+ multiple lanes. If this property is not found, we assume that the
+ value is 0.
++- num-lanes: number of SerDes PCIe lanes for this link (1 or 4)
+ - reset-gpios: optional GPIO to PERST#
+ - reset-delay-us: delay in us to wait after reset de-assertion, if not
+ specified will default to 100ms, as required by the PCIe specification.
+@@ -141,6 +142,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 58>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
++ num-lanes = <1>;
+ /* low-active PERST# reset on GPIO 25 */
+ reset-gpios = <&gpio0 25 1>;
+ /* wait 20ms for device settle after reset deassertion */
+@@ -161,6 +163,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 59>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <1>;
++ num-lanes = <1>;
+ clocks = <&gateclk 6>;
+ };
+
+@@ -177,6 +180,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 60>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <2>;
++ num-lanes = <1>;
+ clocks = <&gateclk 7>;
+ };
+
+@@ -193,6 +197,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 61>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <3>;
++ num-lanes = <1>;
+ clocks = <&gateclk 8>;
+ };
+
+@@ -209,6 +214,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 62>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
++ num-lanes = <1>;
+ clocks = <&gateclk 9>;
+ };
+
+@@ -225,6 +231,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 63>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <1>;
++ num-lanes = <1>;
+ clocks = <&gateclk 10>;
+ };
+
+@@ -241,6 +248,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 64>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <2>;
++ num-lanes = <1>;
+ clocks = <&gateclk 11>;
+ };
+
+@@ -257,6 +265,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 65>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <3>;
++ num-lanes = <1>;
+ clocks = <&gateclk 12>;
+ };
+
+@@ -273,6 +282,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 99>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
++ num-lanes = <1>;
+ clocks = <&gateclk 26>;
+ };
+
+@@ -289,6 +299,7 @@ pcie-controller {
+ interrupt-map = <0 0 0 0 &mpic 103>;
+ marvell,pcie-port = <3>;
+ marvell,pcie-lane = <0>;
++ num-lanes = <1>;
+ clocks = <&gateclk 27>;
+ };
+ };
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0028-PCI-mvebu-Correctly-configure-x1-x4-mode.patch b/pkgs/patches-linux-5.15/0028-PCI-mvebu-Correctly-configure-x1-x4-mode.patch
new file mode 100644
index 0000000..bb87163
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0028-PCI-mvebu-Correctly-configure-x1-x4-mode.patch
@@ -0,0 +1,78 @@
+From 2137824f2a4e824b5d54b17c12d1005a6455ca12 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 11 Oct 2021 11:30:05 +0200
+Subject: [PATCH 28/90] PCI: mvebu: Correctly configure x1/x4 mode
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If x1/x4 mode is not set correctly then link with endpoint card is not
+established.
+
+Use DTS property 'num-lanes' to deteriminate x1/x4 mode.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index a075ba26cff1..0f2ec0a17874 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -93,6 +93,7 @@ struct mvebu_pcie_port {
+ void __iomem *base;
+ u32 port;
+ u32 lane;
++ bool is_x4;
+ int devfn;
+ unsigned int mem_target;
+ unsigned int mem_attr;
+@@ -233,13 +234,25 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
+
+ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ {
+- u32 ctrl, cmd, dev_rev, mask;
++ u32 ctrl, lnkcap, cmd, dev_rev, mask;
+
+ /* Setup PCIe controller to Root Complex mode. */
+ ctrl = mvebu_readl(port, PCIE_CTRL_OFF);
+ ctrl |= PCIE_CTRL_RC_MODE;
+ mvebu_writel(port, ctrl, PCIE_CTRL_OFF);
+
++ /*
++ * Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link
++ * Capability register. This register is defined by PCIe specification
++ * as read-only but this mvebu controller has it as read-write and must
++ * be set to number of SerDes PCIe lanes (1 or 4). If this register is
++ * not set correctly then link with endpoint card is not established.
++ */
++ lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP);
++ lnkcap &= ~PCI_EXP_LNKCAP_MLW;
++ lnkcap |= (port->is_x4 ? 4 : 1) << 4;
++ mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP);
++
+ /* Disable Root Bridge I/O space, memory space and bus mastering. */
+ cmd = mvebu_readl(port, PCIE_CMD_OFF);
+ cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+@@ -986,6 +999,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ struct device *dev = &pcie->pdev->dev;
+ enum of_gpio_flags flags;
+ int reset_gpio, ret;
++ u32 num_lanes;
+
+ port->pcie = pcie;
+
+@@ -998,6 +1012,9 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane))
+ port->lane = 0;
+
++ if (!of_property_read_u32(child, "num-lanes", &num_lanes) && num_lanes == 4)
++ port->is_x4 = true;
++
+ port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port,
+ port->lane);
+ if (!port->name) {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0029-PCI-mvebu-Add-support-for-PCI-Bridge-Subsystem-Vendo.patch b/pkgs/patches-linux-5.15/0029-PCI-mvebu-Add-support-for-PCI-Bridge-Subsystem-Vendo.patch
new file mode 100644
index 0000000..7f26476
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0029-PCI-mvebu-Add-support-for-PCI-Bridge-Subsystem-Vendo.patch
@@ -0,0 +1,53 @@
+From 1192a8856544109e30555ca15aed7353297bae79 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 8 Oct 2021 11:47:35 +0200
+Subject: [PATCH 29/90] PCI: mvebu: Add support for PCI Bridge Subsystem Vendor
+ ID on emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Register with Subsystem Device/Vendor ID is at offset 0x2c. Export is via
+emulated bridge.
+
+After this change Subsystem ID is visible in lspci output at line:
+
+ Capabilities: [40] Subsystem
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 0f2ec0a17874..811af9e6ede5 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -32,6 +32,7 @@
+ #define PCIE_DEV_REV_OFF 0x0008
+ #define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
+ #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
++#define PCIE_SSDEV_ID_OFF 0x002c
+ #define PCIE_CAP_PCIEXP 0x0060
+ #define PCIE_HEADER_LOG_4_OFF 0x0128
+ #define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
+@@ -731,6 +732,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ struct pci_bridge_emul *bridge = &port->bridge;
+ u32 dev_id = mvebu_readl(port, PCIE_DEV_ID_OFF);
+ u32 dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF);
++ u32 ssdev_id = mvebu_readl(port, PCIE_SSDEV_ID_OFF);
+ u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP);
+ u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS);
+
+@@ -752,6 +754,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ */
+ bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver);
+
++ bridge->subsystem_vendor_id = ssdev_id & 0xffff;
++ bridge->subsystem_id = ssdev_id >> 16;
+ bridge->has_pcie = true;
+ bridge->data = port;
+ bridge->ops = &mvebu_pci_bridge_emul_ops;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0030-PCI-mvebu-Add-support-for-Advanced-Error-Reporting-r.patch b/pkgs/patches-linux-5.15/0030-PCI-mvebu-Add-support-for-Advanced-Error-Reporting-r.patch
new file mode 100644
index 0000000..fb806e5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0030-PCI-mvebu-Add-support-for-Advanced-Error-Reporting-r.patch
@@ -0,0 +1,125 @@
+From 6ccfcdf4eef16aaac2bddc3b625a2dabe357bcb1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 13:46:44 +0200
+Subject: [PATCH 30/90] PCI: mvebu: Add support for Advanced Error Reporting
+ registers on emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+AER registers start at mvebu offset 0x0100. Registers PCI_ERR_ROOT_COMMAND,
+PCI_ERR_ROOT_STATUS and PCI_ERR_ROOT_ERR_SRC are not supported on pre-XP
+hardware and returns zeros.
+
+Note that AER interrupt is not supported yet as mvebu emulated bridge does
+not implement interrupts support at all yet.
+
+Also remove custom macro PCIE_HEADER_LOG_4_OFF as it is unused and
+correctly this register should be referenced via standard macros with
+offset, e.g. as: PCIE_CAP_PCIERR_OFF + PCI_ERR_HEADER_LOG + 4.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 67 +++++++++++++++++++++++++++++-
+ 1 file changed, 66 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 811af9e6ede5..9ea2f6a7c2b0 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -34,7 +34,7 @@
+ #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
+ #define PCIE_SSDEV_ID_OFF 0x002c
+ #define PCIE_CAP_PCIEXP 0x0060
+-#define PCIE_HEADER_LOG_4_OFF 0x0128
++#define PCIE_CAP_PCIERR_OFF 0x0100
+ #define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
+ #define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
+ #define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4))
+@@ -603,6 +603,37 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+ return PCI_BRIDGE_EMUL_HANDLED;
+ }
+
++static pci_bridge_emul_read_status_t
++mvebu_pci_bridge_emul_ext_conf_read(struct pci_bridge_emul *bridge,
++ int reg, u32 *value)
++{
++ struct mvebu_pcie_port *port = bridge->data;
++
++ switch (reg) {
++ case 0:
++ case PCI_ERR_UNCOR_STATUS:
++ case PCI_ERR_UNCOR_MASK:
++ case PCI_ERR_UNCOR_SEVER:
++ case PCI_ERR_COR_STATUS:
++ case PCI_ERR_COR_MASK:
++ case PCI_ERR_CAP:
++ case PCI_ERR_HEADER_LOG+0:
++ case PCI_ERR_HEADER_LOG+4:
++ case PCI_ERR_HEADER_LOG+8:
++ case PCI_ERR_HEADER_LOG+12:
++ case PCI_ERR_ROOT_COMMAND:
++ case PCI_ERR_ROOT_STATUS:
++ case PCI_ERR_ROOT_ERR_SRC:
++ *value = mvebu_readl(port, PCIE_CAP_PCIERR_OFF + reg);
++ break;
++
++ default:
++ return PCI_BRIDGE_EMUL_NOT_HANDLED;
++ }
++
++ return PCI_BRIDGE_EMUL_HANDLED;
++}
++
+ static void
+ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
+ int reg, u32 old, u32 new, u32 mask)
+@@ -715,11 +746,45 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ }
+ }
+
++static void
++mvebu_pci_bridge_emul_ext_conf_write(struct pci_bridge_emul *bridge,
++ int reg, u32 old, u32 new, u32 mask)
++{
++ struct mvebu_pcie_port *port = bridge->data;
++
++ switch (reg) {
++ /* These are W1C registers, so clear other bits */
++ case PCI_ERR_UNCOR_STATUS:
++ case PCI_ERR_COR_STATUS:
++ case PCI_ERR_ROOT_STATUS:
++ new &= mask;
++ fallthrough;
++
++ case PCI_ERR_UNCOR_MASK:
++ case PCI_ERR_UNCOR_SEVER:
++ case PCI_ERR_COR_MASK:
++ case PCI_ERR_CAP:
++ case PCI_ERR_HEADER_LOG+0:
++ case PCI_ERR_HEADER_LOG+4:
++ case PCI_ERR_HEADER_LOG+8:
++ case PCI_ERR_HEADER_LOG+12:
++ case PCI_ERR_ROOT_COMMAND:
++ case PCI_ERR_ROOT_ERR_SRC:
++ mvebu_writel(port, new, PCIE_CAP_PCIERR_OFF + reg);
++ break;
++
++ default:
++ break;
++ }
++}
++
+ static const struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
+ .read_base = mvebu_pci_bridge_emul_base_conf_read,
+ .write_base = mvebu_pci_bridge_emul_base_conf_write,
+ .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
+ .write_pcie = mvebu_pci_bridge_emul_pcie_conf_write,
++ .read_ext = mvebu_pci_bridge_emul_ext_conf_read,
++ .write_ext = mvebu_pci_bridge_emul_ext_conf_write,
+ };
+
+ /*
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0031-PCI-mvebu-Use-child_ops-API.patch b/pkgs/patches-linux-5.15/0031-PCI-mvebu-Use-child_ops-API.patch
new file mode 100644
index 0000000..41d7430
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0031-PCI-mvebu-Use-child_ops-API.patch
@@ -0,0 +1,165 @@
+From a32d68b41dac1a1aeab94a2c8b2239534ac59c15 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 16 Nov 2021 10:43:17 +0100
+Subject: [PATCH 31/90] PCI: mvebu: Use child_ops API
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Split struct pci_ops between ops and child_ops. Member ops is used for
+accessing PCIe Root Ports via pci-bridge-emul.c driver and child_ops for
+accessing real PCIe cards.
+
+There is no need to mix these two struct pci_ops into one as PCI core code
+already provides separate callbacks via bridge->ops and bridge->child_ops.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++--------------
+ 1 file changed, 44 insertions(+), 38 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 9ea2f6a7c2b0..1e90ab888075 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ mvebu_writel(port, mask, PCIE_MASK_OFF);
+ }
+
+-static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,
+- struct pci_bus *bus,
+- u32 devfn, int where, int size, u32 *val)
++static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
++ struct pci_bus *bus,
++ int devfn);
++
++static int mvebu_pcie_child_rd_conf(struct pci_bus *bus, u32 devfn, int where,
++ int size, u32 *val)
+ {
+- void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
++ struct mvebu_pcie *pcie = bus->sysdata;
++ struct mvebu_pcie_port *port;
++ void __iomem *conf_data;
++
++ port = mvebu_pcie_find_port(pcie, bus, devfn);
++ if (!port) {
++ *val = 0xffffffff;
++ return PCIBIOS_DEVICE_NOT_FOUND;
++ }
++
++ if (!mvebu_pcie_link_up(port)) {
++ *val = 0xffffffff;
++ return PCIBIOS_DEVICE_NOT_FOUND;
++ }
++
++ conf_data = port->base + PCIE_CONF_DATA_OFF;
+
+ mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
+ PCIE_CONF_ADDR_OFF);
+@@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+-static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
+- struct pci_bus *bus,
+- u32 devfn, int where, int size, u32 val)
++static int mvebu_pcie_child_wr_conf(struct pci_bus *bus, u32 devfn,
++ int where, int size, u32 val)
+ {
+- void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
++ struct mvebu_pcie *pcie = bus->sysdata;
++ struct mvebu_pcie_port *port;
++ void __iomem *conf_data;
++
++ port = mvebu_pcie_find_port(pcie, bus, devfn);
++ if (!port)
++ return PCIBIOS_DEVICE_NOT_FOUND;
++
++ if (!mvebu_pcie_link_up(port))
++ return PCIBIOS_DEVICE_NOT_FOUND;
++
++ conf_data = port->base + PCIE_CONF_DATA_OFF;
+
+ mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
+ PCIE_CONF_ADDR_OFF);
+@@ -347,6 +375,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
+ return PCIBIOS_SUCCESSFUL;
+ }
+
++static struct pci_ops mvebu_pcie_child_ops = {
++ .read = mvebu_pcie_child_rd_conf,
++ .write = mvebu_pcie_child_wr_conf,
++};
++
+ /*
+ * Remove windows, starting from the largest ones to the smallest
+ * ones.
+@@ -862,25 +895,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+ {
+ struct mvebu_pcie *pcie = bus->sysdata;
+ struct mvebu_pcie_port *port;
+- int ret;
+
+ port = mvebu_pcie_find_port(pcie, bus, devfn);
+ if (!port)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+- /* Access the emulated PCI-to-PCI bridge */
+- if (bus->number == 0)
+- return pci_bridge_emul_conf_write(&port->bridge, where,
+- size, val);
+-
+- if (!mvebu_pcie_link_up(port))
+- return PCIBIOS_DEVICE_NOT_FOUND;
+-
+- /* Access the real PCIe interface */
+- ret = mvebu_pcie_hw_wr_conf(port, bus, devfn,
+- where, size, val);
+-
+- return ret;
++ return pci_bridge_emul_conf_write(&port->bridge, where, size, val);
+ }
+
+ /* PCI configuration space read function */
+@@ -889,7 +909,6 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+ {
+ struct mvebu_pcie *pcie = bus->sysdata;
+ struct mvebu_pcie_port *port;
+- int ret;
+
+ port = mvebu_pcie_find_port(pcie, bus, devfn);
+ if (!port) {
+@@ -897,21 +916,7 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+- /* Access the emulated PCI-to-PCI bridge */
+- if (bus->number == 0)
+- return pci_bridge_emul_conf_read(&port->bridge, where,
+- size, val);
+-
+- if (!mvebu_pcie_link_up(port)) {
+- *val = 0xffffffff;
+- return PCIBIOS_DEVICE_NOT_FOUND;
+- }
+-
+- /* Access the real PCIe interface */
+- ret = mvebu_pcie_hw_rd_conf(port, bus, devfn,
+- where, size, val);
+-
+- return ret;
++ return pci_bridge_emul_conf_read(&port->bridge, where, size, val);
+ }
+
+ static struct pci_ops mvebu_pcie_ops = {
+@@ -1421,6 +1426,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+
+ bridge->sysdata = pcie;
+ bridge->ops = &mvebu_pcie_ops;
++ bridge->child_ops = &mvebu_pcie_child_ops;
+ bridge->align_resource = mvebu_pcie_align_resource;
+ bridge->map_irq = mvebu_pcie_map_irq;
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0032-dt-bindings-PCI-mvebu-Update-information-about-intx-.patch b/pkgs/patches-linux-5.15/0032-dt-bindings-PCI-mvebu-Update-information-about-intx-.patch
new file mode 100644
index 0000000..9e71020
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0032-dt-bindings-PCI-mvebu-Update-information-about-intx-.patch
@@ -0,0 +1,34 @@
+From 3ef4b961298bb64ba871616f51a0d869cb7c4555 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 14:43:39 +0200
+Subject: [PATCH 32/90] dt-bindings: PCI: mvebu: Update information about intx
+ interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Rob Herring <robh@kernel.org>
+---
+ Documentation/devicetree/bindings/pci/mvebu-pci.txt | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt
+index 24225852bce0..6d022a9d36ee 100644
+--- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt
++++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt
+@@ -81,6 +81,11 @@ and the following optional properties:
+ - reset-gpios: optional GPIO to PERST#
+ - reset-delay-us: delay in us to wait after reset de-assertion, if not
+ specified will default to 100ms, as required by the PCIe specification.
++- interrupt-names: list of interrupt names, supported are:
++ - "intx" - interrupt line triggered by one of the legacy interrupt
++- interrupts or interrupts-extended: List of the interrupt sources which
++ corresponding to the "interrupt-names". If non-empty then also additional
++ 'interrupt-controller' subnode must be defined.
+
+ Example:
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0033-PCI-mvebu-Fix-macro-names-and-comments-about-legacy-.patch b/pkgs/patches-linux-5.15/0033-PCI-mvebu-Fix-macro-names-and-comments-about-legacy-.patch
new file mode 100644
index 0000000..185b1c2
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0033-PCI-mvebu-Fix-macro-names-and-comments-about-legacy-.patch
@@ -0,0 +1,79 @@
+From 8fa82d166233f0c6b4ec82b4b92dbb356cd69675 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 14 Feb 2022 13:12:48 +0100
+Subject: [PATCH 33/90] PCI: mvebu: Fix macro names and comments about legacy
+ interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Register 0x1910 unmasks interrupts and legacy INTx interrupts are unmasked
+because driver does not support individual masking yet.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 26 ++++++++++++++++++--------
+ 1 file changed, 18 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 1e90ab888075..5f8b8b4ddbea 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -54,9 +54,10 @@
+ PCIE_CONF_ADDR_EN)
+ #define PCIE_CONF_DATA_OFF 0x18fc
+ #define PCIE_INT_CAUSE_OFF 0x1900
++#define PCIE_INT_UNMASK_OFF 0x1910
++#define PCIE_INT_INTX(i) BIT(24+i)
+ #define PCIE_INT_PM_PME BIT(28)
+-#define PCIE_MASK_OFF 0x1910
+-#define PCIE_MASK_ENABLE_INTS 0x0f000000
++#define PCIE_INT_ALL_MASK GENMASK(31, 0)
+ #define PCIE_CTRL_OFF 0x1a00
+ #define PCIE_CTRL_X1_MODE 0x0001
+ #define PCIE_CTRL_RC_MODE BIT(1)
+@@ -235,7 +236,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
+
+ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ {
+- u32 ctrl, lnkcap, cmd, dev_rev, mask;
++ u32 ctrl, lnkcap, cmd, dev_rev, unmask;
+
+ /* Setup PCIe controller to Root Complex mode. */
+ ctrl = mvebu_readl(port, PCIE_CTRL_OFF);
+@@ -288,10 +289,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ /* Point PCIe unit MBUS decode windows to DRAM space. */
+ mvebu_pcie_setup_wins(port);
+
+- /* Enable interrupt lines A-D. */
+- mask = mvebu_readl(port, PCIE_MASK_OFF);
+- mask |= PCIE_MASK_ENABLE_INTS;
+- mvebu_writel(port, mask, PCIE_MASK_OFF);
++ /*
++ * Unmask all legacy INTx interrupts as driver does not provide a way
++ * for masking and unmasking of individual legacy INTx interrupts.
++ * Legacy INTx are reported via one shared GIC source and therefore
++ * kernel cannot distinguish which individual legacy INTx was triggered.
++ * These interrupts are shared, so it should not cause any issue. Just
++ * performance penalty as every PCIe interrupt handler needs to be
++ * called when some interrupt is triggered.
++ */
++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ unmask |= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) |
++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3);
++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
+ }
+
+ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
+@@ -1458,7 +1468,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+ mvebu_writel(port, cmd, PCIE_CMD_OFF);
+
+ /* Mask all interrupt sources. */
+- mvebu_writel(port, 0, PCIE_MASK_OFF);
++ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF);
+
+ /* Free config space for emulated root bridge. */
+ pci_bridge_emul_cleanup(&port->bridge);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0034-PCI-mvebu-Implement-support-for-legacy-INTx-interrup.patch b/pkgs/patches-linux-5.15/0034-PCI-mvebu-Implement-support-for-legacy-INTx-interrup.patch
new file mode 100644
index 0000000..d02a2c4
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0034-PCI-mvebu-Implement-support-for-legacy-INTx-interrup.patch
@@ -0,0 +1,251 @@
+From 09e38b818e55358009d7c20aeddb7c59f0b3a3e1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 2 Nov 2021 10:30:20 +0100
+Subject: [PATCH 34/90] PCI: mvebu: Implement support for legacy INTx
+ interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This adds support for legacy INTx interrupts received from other PCIe
+devices and which are reported by a new INTx irq chip.
+
+With this change, kernel can distinguish between INTA, INTB, INTC and INTD
+interrupts.
+
+Note that for this support, device tree files has to be properly adjusted
+to provide "interrupts" or "interrupts-extended" property with intx
+interrupt source, "interrupt-names" property with "intx" string and also
+'interrupt-controller' subnode must be defined.
+
+If device tree files do not provide these nodes then driver would work as
+before.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 159 +++++++++++++++++++++++++++++
+ 1 file changed, 159 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 5f8b8b4ddbea..c9311829dfe3 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -111,6 +111,9 @@ struct mvebu_pcie_port {
+ struct mvebu_pcie_window iowin;
+ u32 saved_pcie_stat;
+ struct resource regs;
++ struct irq_domain *intx_irq_domain;
++ raw_spinlock_t irq_lock;
++ int intx_irq;
+ };
+
+ static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg)
+@@ -289,7 +292,18 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ /* Point PCIe unit MBUS decode windows to DRAM space. */
+ mvebu_pcie_setup_wins(port);
+
++ /* Mask all interrupt sources. */
++ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF);
++
++ /* Clear all interrupt causes. */
++ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF);
++
++ /* Check if "intx" interrupt was specified in DT. */
++ if (port->intx_irq > 0)
++ return;
++
+ /*
++ * Fallback code when "intx" interrupt was not specified in DT:
+ * Unmask all legacy INTx interrupts as driver does not provide a way
+ * for masking and unmasking of individual legacy INTx interrupts.
+ * Legacy INTx are reported via one shared GIC source and therefore
+@@ -934,6 +948,108 @@ static struct pci_ops mvebu_pcie_ops = {
+ .write = mvebu_pcie_wr_conf,
+ };
+
++static void mvebu_pcie_intx_irq_mask(struct irq_data *d)
++{
++ struct mvebu_pcie_port *port = d->domain->host_data;
++ irq_hw_number_t hwirq = irqd_to_hwirq(d);
++ unsigned long flags;
++ u32 unmask;
++
++ raw_spin_lock_irqsave(&port->irq_lock, flags);
++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ unmask &= ~PCIE_INT_INTX(hwirq);
++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
++ raw_spin_unlock_irqrestore(&port->irq_lock, flags);
++}
++
++static void mvebu_pcie_intx_irq_unmask(struct irq_data *d)
++{
++ struct mvebu_pcie_port *port = d->domain->host_data;
++ irq_hw_number_t hwirq = irqd_to_hwirq(d);
++ unsigned long flags;
++ u32 unmask;
++
++ raw_spin_lock_irqsave(&port->irq_lock, flags);
++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ unmask |= PCIE_INT_INTX(hwirq);
++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
++ raw_spin_unlock_irqrestore(&port->irq_lock, flags);
++}
++
++static struct irq_chip intx_irq_chip = {
++ .name = "mvebu-INTx",
++ .irq_mask = mvebu_pcie_intx_irq_mask,
++ .irq_unmask = mvebu_pcie_intx_irq_unmask,
++};
++
++static int mvebu_pcie_intx_irq_map(struct irq_domain *h,
++ unsigned int virq, irq_hw_number_t hwirq)
++{
++ struct mvebu_pcie_port *port = h->host_data;
++
++ irq_set_status_flags(virq, IRQ_LEVEL);
++ irq_set_chip_and_handler(virq, &intx_irq_chip, handle_level_irq);
++ irq_set_chip_data(virq, port);
++
++ return 0;
++}
++
++static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = {
++ .map = mvebu_pcie_intx_irq_map,
++ .xlate = irq_domain_xlate_onecell,
++};
++
++static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port)
++{
++ struct device *dev = &port->pcie->pdev->dev;
++ struct device_node *pcie_intc_node;
++
++ raw_spin_lock_init(&port->irq_lock);
++
++ pcie_intc_node = of_get_next_child(port->dn, NULL);
++ if (!pcie_intc_node) {
++ dev_err(dev, "No PCIe Intc node found for %s\n", port->name);
++ return -ENODEV;
++ }
++
++ port->intx_irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
++ &mvebu_pcie_intx_irq_domain_ops,
++ port);
++ of_node_put(pcie_intc_node);
++ if (!port->intx_irq_domain) {
++ dev_err(dev, "Failed to get INTx IRQ domain for %s\n", port->name);
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static void mvebu_pcie_irq_handler(struct irq_desc *desc)
++{
++ struct mvebu_pcie_port *port = irq_desc_get_handler_data(desc);
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct device *dev = &port->pcie->pdev->dev;
++ u32 cause, unmask, status;
++ int i;
++
++ chained_irq_enter(chip, desc);
++
++ cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF);
++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ status = cause & unmask;
++
++ /* Process legacy INTx interrupts */
++ for (i = 0; i < PCI_NUM_INTX; i++) {
++ if (!(status & PCIE_INT_INTX(i)))
++ continue;
++
++ if (generic_handle_domain_irq(port->intx_irq_domain, i) == -EINVAL)
++ dev_err_ratelimited(dev, "unexpected INT%c IRQ\n", (char)i+'A');
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
+ static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+ {
+ /* Interrupt support on mvebu emulated bridges is not implemented yet */
+@@ -1131,6 +1247,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ port->io_attr = -1;
+ }
+
++ /*
++ * Old DT bindings do not contain "intx" interrupt
++ * so do not fail probing driver when interrupt does not exist.
++ */
++ port->intx_irq = of_irq_get_byname(child, "intx");
++ if (port->intx_irq == -EPROBE_DEFER) {
++ ret = port->intx_irq;
++ goto err;
++ }
++ if (port->intx_irq <= 0) {
++ dev_warn(dev, "%s: legacy INTx interrupts cannot be masked individually, "
++ "%pOF does not contain intx interrupt\n",
++ port->name, child);
++ }
++
+ reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags);
+ if (reset_gpio == -EPROBE_DEFER) {
+ ret = reset_gpio;
+@@ -1327,6 +1458,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = &pcie->ports[i];
++ int irq = port->intx_irq;
+
+ child = port->dn;
+ if (!child)
+@@ -1354,6 +1486,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+ continue;
+ }
+
++ if (irq > 0) {
++ ret = mvebu_pcie_init_irq_domain(port);
++ if (ret) {
++ dev_err(dev, "%s: cannot init irq domain\n",
++ port->name);
++ pci_bridge_emul_cleanup(&port->bridge);
++ devm_iounmap(dev, port->base);
++ port->base = NULL;
++ mvebu_pcie_powerdown(port);
++ continue;
++ }
++ irq_set_chained_handler_and_data(irq,
++ mvebu_pcie_irq_handler,
++ port);
++ }
++
+ /*
+ * PCIe topology exported by mvebu hw is quite complicated. In
+ * reality has something like N fully independent host bridges
+@@ -1458,6 +1606,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = &pcie->ports[i];
++ int irq = port->intx_irq;
+
+ if (!port->base)
+ continue;
+@@ -1470,6 +1619,16 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+ /* Mask all interrupt sources. */
+ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF);
+
++ /* Clear all interrupt causes. */
++ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF);
++
++ if (irq > 0)
++ irq_set_chained_handler_and_data(irq, NULL, NULL);
++
++ /* Remove IRQ domains. */
++ if (port->intx_irq_domain)
++ irq_domain_remove(port->intx_irq_domain);
++
+ /* Free config space for emulated root bridge. */
+ pci_bridge_emul_cleanup(&port->bridge);
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0035-ARM-dts-kirkwood-Add-definitions-for-PCIe-legacy-INT.patch b/pkgs/patches-linux-5.15/0035-ARM-dts-kirkwood-Add-definitions-for-PCIe-legacy-INT.patch
new file mode 100644
index 0000000..5f72bc4
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0035-ARM-dts-kirkwood-Add-definitions-for-PCIe-legacy-INT.patch
@@ -0,0 +1,161 @@
+From 17c0e92f81db8b06c2fccc47da25bc14af58e03c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 20:14:33 +0200
+Subject: [PATCH 35/90] ARM: dts: kirkwood: Add definitions for PCIe legacy
+ INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/kirkwood-6192.dtsi | 14 ++++++++++--
+ arch/arm/boot/dts/kirkwood-6281.dtsi | 14 ++++++++++--
+ arch/arm/boot/dts/kirkwood-6282.dtsi | 28 ++++++++++++++++++++----
+ arch/arm/boot/dts/kirkwood-98dx4122.dtsi | 14 ++++++++++--
+ 4 files changed, 60 insertions(+), 10 deletions(-)
+
+diff --git a/arch/arm/boot/dts/kirkwood-6192.dtsi b/arch/arm/boot/dts/kirkwood-6192.dtsi
+index 396bcba08adb..07f4f7f98c0c 100644
+--- a/arch/arm/boot/dts/kirkwood-6192.dtsi
++++ b/arch/arm/boot/dts/kirkwood-6192.dtsi
+@@ -26,12 +26,22 @@ pcie0: pcie@1,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &intc 9>;
++ interrupt-names = "intx";
++ interrupts = <9>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie_intc 0>,
++ <0 0 0 2 &pcie_intc 1>,
++ <0 0 0 3 &pcie_intc 2>,
++ <0 0 0 4 &pcie_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gate_clk 2>;
+ status = "disabled";
++
++ pcie_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/kirkwood-6281.dtsi b/arch/arm/boot/dts/kirkwood-6281.dtsi
+index faa05849a40d..d08a9a5ecc26 100644
+--- a/arch/arm/boot/dts/kirkwood-6281.dtsi
++++ b/arch/arm/boot/dts/kirkwood-6281.dtsi
+@@ -26,12 +26,22 @@ pcie0: pcie@1,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &intc 9>;
++ interrupt-names = "intx";
++ interrupts = <9>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie_intc 0>,
++ <0 0 0 2 &pcie_intc 1>,
++ <0 0 0 3 &pcie_intc 2>,
++ <0 0 0 4 &pcie_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gate_clk 2>;
+ status = "disabled";
++
++ pcie_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/kirkwood-6282.dtsi b/arch/arm/boot/dts/kirkwood-6282.dtsi
+index e84c54b77dea..2eea5b304f47 100644
+--- a/arch/arm/boot/dts/kirkwood-6282.dtsi
++++ b/arch/arm/boot/dts/kirkwood-6282.dtsi
+@@ -30,12 +30,22 @@ pcie0: pcie@1,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &intc 9>;
++ interrupt-names = "intx";
++ interrupts = <9>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie0_intc 0>,
++ <0 0 0 2 &pcie0_intc 1>,
++ <0 0 0 3 &pcie0_intc 2>,
++ <0 0 0 4 &pcie0_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gate_clk 2>;
+ status = "disabled";
++
++ pcie0_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie1: pcie@2,0 {
+@@ -48,12 +58,22 @@ pcie1: pcie@2,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &intc 10>;
++ interrupt-names = "intx";
++ interrupts = <10>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gate_clk 18>;
+ status = "disabled";
++
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+ };
+diff --git a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi
+index 299c147298c3..070bc13242b8 100644
+--- a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi
++++ b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi
+@@ -26,12 +26,22 @@ pcie0: pcie@1,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &intc 9>;
++ interrupt-names = "intx";
++ interrupts = <9>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie_intc 0>,
++ <0 0 0 2 &pcie_intc 1>,
++ <0 0 0 3 &pcie_intc 2>,
++ <0 0 0 4 &pcie_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gate_clk 2>;
+ status = "disabled";
++
++ pcie_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+ };
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0036-ARM-dts-dove-Add-definitions-for-PCIe-legacy-INTx-in.patch b/pkgs/patches-linux-5.15/0036-ARM-dts-dove-Add-definitions-for-PCIe-legacy-INTx-in.patch
new file mode 100644
index 0000000..fd511e5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0036-ARM-dts-dove-Add-definitions-for-PCIe-legacy-INTx-in.patch
@@ -0,0 +1,63 @@
+From bec69dde1c0cbabc5a35d32af0ff3d6dbb4e4fec Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 20:17:27 +0200
+Subject: [PATCH 36/90] ARM: dts: dove: Add definitions for PCIe legacy INTx
+ interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/dove.dtsi | 28 ++++++++++++++++++++++++----
+ 1 file changed, 24 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
+index 89e0bdaf3a85..96ba47c061a7 100644
+--- a/arch/arm/boot/dts/dove.dtsi
++++ b/arch/arm/boot/dts/dove.dtsi
+@@ -122,8 +122,18 @@ pcie0: pcie@1 {
+ bus-range = <0x00 0xff>;
+
+ #interrupt-cells = <1>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &intc 16>;
++ interrupt-names = "intx";
++ interrupts = <16>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie0_intc 0>,
++ <0 0 0 2 &pcie0_intc 1>,
++ <0 0 0 3 &pcie0_intc 2>,
++ <0 0 0 4 &pcie0_intc 3>;
++
++ pcie0_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie1: pcie@2 {
+@@ -141,8 +151,18 @@ pcie1: pcie@2 {
+ bus-range = <0x00 0xff>;
+
+ #interrupt-cells = <1>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &intc 18>;
++ interrupt-names = "intx";
++ interrupts = <18>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
++
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0037-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0037-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-leg.patch
new file mode 100644
index 0000000..02d4717
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0037-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-leg.patch
@@ -0,0 +1,79 @@
+From 19977423f7549278ed5f4fae7f171d96c31f5817 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 16:24:58 +0200
+Subject: [PATCH 37/90] ARM: dts: armada-370.dtsi: Add definitions for PCIe
+ legacy INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-370.dtsi | 28 ++++++++++++++++++++++++----
+ 1 file changed, 24 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi
+index 46e6d3ed8f35..9dc928859ad3 100644
+--- a/arch/arm/boot/dts/armada-370.dtsi
++++ b/arch/arm/boot/dts/armada-370.dtsi
+@@ -60,16 +60,26 @@ pcie0: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 58>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 58>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie0_intc 0>,
++ <0 0 0 2 &pcie0_intc 1>,
++ <0 0 0 3 &pcie0_intc 2>,
++ <0 0 0 4 &pcie0_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
++
++ pcie0_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie2: pcie@2,0 {
+@@ -78,16 +88,26 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 62>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 62>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie2_intc 0>,
++ <0 0 0 2 &pcie2_intc 1>,
++ <0 0 0 3 &pcie2_intc 2>,
++ <0 0 0 4 &pcie2_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 9>;
+ status = "disabled";
++
++ pcie2_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0038-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch b/pkgs/patches-linux-5.15/0038-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch
new file mode 100644
index 0000000..f2dfbae
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0038-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch
@@ -0,0 +1,50 @@
+From c1c851bad2eb2c04a24f80dca20bfeb69d8263f3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 17:02:23 +0200
+Subject: [PATCH 38/90] ARM: dts: armada-xp-98dx3236.dtsi: Add definitions for
+ PCIe legacy INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-xp-98dx3236.dtsi | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-xp-98dx3236.dtsi b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi
+index 38a052a0312d..b21ffb819b1d 100644
+--- a/arch/arm/boot/dts/armada-xp-98dx3236.dtsi
++++ b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi
+@@ -76,16 +76,26 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 58>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 58>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
++
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0039-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0039-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch
new file mode 100644
index 0000000..a6a60a5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0039-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch
@@ -0,0 +1,166 @@
+From 7cf39d981b03d76923f797115dabbf8d8db9bd74 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 19:26:59 +0200
+Subject: [PATCH 39/90] ARM: dts: armada-xp-mv78230.dtsi: Add definitions for
+ PCIe legacy INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-xp-mv78230.dtsi | 70 ++++++++++++++++++++----
+ 1 file changed, 60 insertions(+), 10 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-xp-mv78230.dtsi b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
+index 8558bf6bb54c..bf9360f41e0a 100644
+--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
+@@ -83,16 +83,26 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 58>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 58>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
++
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie2: pcie@2,0 {
+@@ -101,16 +111,26 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 59>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 59>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie2_intc 0>,
++ <0 0 0 2 &pcie2_intc 1>,
++ <0 0 0 3 &pcie2_intc 2>,
++ <0 0 0 4 &pcie2_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
++
++ pcie2_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie3: pcie@3,0 {
+@@ -119,16 +139,26 @@ pcie3: pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 60>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 60>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie3_intc 0>,
++ <0 0 0 2 &pcie3_intc 1>,
++ <0 0 0 3 &pcie3_intc 2>,
++ <0 0 0 4 &pcie3_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <2>;
+ clocks = <&gateclk 7>;
+ status = "disabled";
++
++ pcie3_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie4: pcie@4,0 {
+@@ -137,16 +167,26 @@ pcie4: pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 61>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 61>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie4_intc 0>,
++ <0 0 0 2 &pcie4_intc 1>,
++ <0 0 0 3 &pcie4_intc 2>,
++ <0 0 0 4 &pcie4_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <3>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
++
++ pcie4_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie5: pcie@5,0 {
+@@ -155,16 +195,26 @@ pcie5: pcie@5,0 {
+ reg = <0x2800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 62>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0
+ 0x81000000 0 0 0x81000000 0x5 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 62>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie5_intc 0>,
++ <0 0 0 2 &pcie5_intc 1>,
++ <0 0 0 3 &pcie5_intc 2>,
++ <0 0 0 4 &pcie5_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 9>;
+ status = "disabled";
++
++ pcie5_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0040-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0040-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch
new file mode 100644
index 0000000..9ac3d91
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0040-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch
@@ -0,0 +1,282 @@
+From 4c45fc119653486c76c2931cd4cfb05c4770f1e8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 19:54:06 +0200
+Subject: [PATCH 40/90] ARM: dts: armada-xp-mv78260.dtsi: Add definitions for
+ PCIe legacy INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-xp-mv78260.dtsi | 126 +++++++++++++++++++----
+ 1 file changed, 108 insertions(+), 18 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-xp-mv78260.dtsi b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
+index 2d85fe8ac327..0714af52e607 100644
+--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
+@@ -98,16 +98,26 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 58>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 58>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
++
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie2: pcie@2,0 {
+@@ -116,16 +126,26 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 59>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 59>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie2_intc 0>,
++ <0 0 0 2 &pcie2_intc 1>,
++ <0 0 0 3 &pcie2_intc 2>,
++ <0 0 0 4 &pcie2_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
++
++ pcie2_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie3: pcie@3,0 {
+@@ -134,16 +154,26 @@ pcie3: pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 60>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 60>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie3_intc 0>,
++ <0 0 0 2 &pcie3_intc 1>,
++ <0 0 0 3 &pcie3_intc 2>,
++ <0 0 0 4 &pcie3_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <2>;
+ clocks = <&gateclk 7>;
+ status = "disabled";
++
++ pcie3_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie4: pcie@4,0 {
+@@ -152,16 +182,26 @@ pcie4: pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 61>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 61>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie4_intc 0>,
++ <0 0 0 2 &pcie4_intc 1>,
++ <0 0 0 3 &pcie4_intc 2>,
++ <0 0 0 4 &pcie4_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <3>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
++
++ pcie4_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie5: pcie@5,0 {
+@@ -170,16 +210,26 @@ pcie5: pcie@5,0 {
+ reg = <0x2800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 62>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0
+ 0x81000000 0 0 0x81000000 0x5 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 62>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie5_intc 0>,
++ <0 0 0 2 &pcie5_intc 1>,
++ <0 0 0 3 &pcie5_intc 2>,
++ <0 0 0 4 &pcie5_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 9>;
+ status = "disabled";
++
++ pcie5_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie6: pcie@6,0 {
+@@ -188,16 +238,26 @@ pcie6: pcie@6,0 {
+ reg = <0x3000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 63>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x6 0 1 0
+ 0x81000000 0 0 0x81000000 0x6 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 63>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie6_intc 0>,
++ <0 0 0 2 &pcie6_intc 1>,
++ <0 0 0 3 &pcie6_intc 2>,
++ <0 0 0 4 &pcie6_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 10>;
+ status = "disabled";
++
++ pcie6_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie7: pcie@7,0 {
+@@ -206,16 +266,26 @@ pcie7: pcie@7,0 {
+ reg = <0x3800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 64>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x7 0 1 0
+ 0x81000000 0 0 0x81000000 0x7 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 64>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie7_intc 0>,
++ <0 0 0 2 &pcie7_intc 1>,
++ <0 0 0 3 &pcie7_intc 2>,
++ <0 0 0 4 &pcie7_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <2>;
+ clocks = <&gateclk 11>;
+ status = "disabled";
++
++ pcie7_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie8: pcie@8,0 {
+@@ -224,16 +294,26 @@ pcie8: pcie@8,0 {
+ reg = <0x4000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 65>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x8 0 1 0
+ 0x81000000 0 0 0x81000000 0x8 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 65>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie8_intc 0>,
++ <0 0 0 2 &pcie8_intc 1>,
++ <0 0 0 3 &pcie8_intc 2>,
++ <0 0 0 4 &pcie8_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <3>;
+ clocks = <&gateclk 12>;
+ status = "disabled";
++
++ pcie8_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie9: pcie@9,0 {
+@@ -242,16 +322,26 @@ pcie9: pcie@9,0 {
+ reg = <0x4800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 99>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x9 0 1 0
+ 0x81000000 0 0 0x81000000 0x9 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 99>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie9_intc 0>,
++ <0 0 0 2 &pcie9_intc 1>,
++ <0 0 0 3 &pcie9_intc 2>,
++ <0 0 0 4 &pcie9_intc 3>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 26>;
+ status = "disabled";
++
++ pcie9_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0041-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0041-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch
new file mode 100644
index 0000000..e987e42
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0041-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch
@@ -0,0 +1,311 @@
+From 7d19ddf1d053706976824ccc9153c2bd7caf4f94 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 19:58:06 +0200
+Subject: [PATCH 41/90] ARM: dts: armada-xp-mv78460.dtsi: Add definitions for
+ PCIe legacy INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-xp-mv78460.dtsi | 140 +++++++++++++++++++----
+ 1 file changed, 120 insertions(+), 20 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-xp-mv78460.dtsi b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
+index 230a3fd36b30..16185edf9aa5 100644
+--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
+@@ -119,16 +119,26 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 58>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 58>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
++
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie2: pcie@2,0 {
+@@ -137,16 +147,26 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 59>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 59>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie2_intc 0>,
++ <0 0 0 2 &pcie2_intc 1>,
++ <0 0 0 3 &pcie2_intc 2>,
++ <0 0 0 4 &pcie2_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
++
++ pcie2_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie3: pcie@3,0 {
+@@ -155,16 +175,26 @@ pcie3: pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 60>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 60>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie3_intc 0>,
++ <0 0 0 2 &pcie3_intc 1>,
++ <0 0 0 3 &pcie3_intc 2>,
++ <0 0 0 4 &pcie3_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <2>;
+ clocks = <&gateclk 7>;
+ status = "disabled";
++
++ pcie3_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie4: pcie@4,0 {
+@@ -173,16 +203,26 @@ pcie4: pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 61>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 61>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie4_intc 0>,
++ <0 0 0 2 &pcie4_intc 1>,
++ <0 0 0 3 &pcie4_intc 2>,
++ <0 0 0 4 &pcie4_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <3>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
++
++ pcie4_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie5: pcie@5,0 {
+@@ -191,16 +231,26 @@ pcie5: pcie@5,0 {
+ reg = <0x2800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 62>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0
+ 0x81000000 0 0 0x81000000 0x5 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 62>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie5_intc 0>,
++ <0 0 0 2 &pcie5_intc 1>,
++ <0 0 0 3 &pcie5_intc 2>,
++ <0 0 0 4 &pcie5_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 9>;
+ status = "disabled";
++
++ pcie5_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie6: pcie@6,0 {
+@@ -209,16 +259,26 @@ pcie6: pcie@6,0 {
+ reg = <0x3000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 63>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x6 0 1 0
+ 0x81000000 0 0 0x81000000 0x6 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 63>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie6_intc 0>,
++ <0 0 0 2 &pcie6_intc 1>,
++ <0 0 0 3 &pcie6_intc 2>,
++ <0 0 0 4 &pcie6_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 10>;
+ status = "disabled";
++
++ pcie6_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie7: pcie@7,0 {
+@@ -227,16 +287,26 @@ pcie7: pcie@7,0 {
+ reg = <0x3800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 64>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x7 0 1 0
+ 0x81000000 0 0 0x81000000 0x7 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 64>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie7_intc 0>,
++ <0 0 0 2 &pcie7_intc 1>,
++ <0 0 0 3 &pcie7_intc 2>,
++ <0 0 0 4 &pcie7_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <2>;
+ clocks = <&gateclk 11>;
+ status = "disabled";
++
++ pcie7_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie8: pcie@8,0 {
+@@ -245,16 +315,26 @@ pcie8: pcie@8,0 {
+ reg = <0x4000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 65>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x8 0 1 0
+ 0x81000000 0 0 0x81000000 0x8 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 65>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie8_intc 0>,
++ <0 0 0 2 &pcie8_intc 1>,
++ <0 0 0 3 &pcie8_intc 2>,
++ <0 0 0 4 &pcie8_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <3>;
+ clocks = <&gateclk 12>;
+ status = "disabled";
++
++ pcie8_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie9: pcie@9,0 {
+@@ -263,16 +343,26 @@ pcie9: pcie@9,0 {
+ reg = <0x4800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 99>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x9 0 1 0
+ 0x81000000 0 0 0x81000000 0x9 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 99>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie9_intc 0>,
++ <0 0 0 2 &pcie9_intc 1>,
++ <0 0 0 3 &pcie9_intc 2>,
++ <0 0 0 4 &pcie9_intc 3>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 26>;
+ status = "disabled";
++
++ pcie9_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie10: pcie@a,0 {
+@@ -281,16 +371,26 @@ pcie10: pcie@a,0 {
+ reg = <0x5000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&mpic 103>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0xa 0 1 0
+ 0x81000000 0 0 0x81000000 0xa 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &mpic 103>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie10_intc 0>,
++ <0 0 0 2 &pcie10_intc 1>,
++ <0 0 0 3 &pcie10_intc 2>,
++ <0 0 0 4 &pcie10_intc 3>;
+ marvell,pcie-port = <3>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 27>;
+ status = "disabled";
++
++ pcie10_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0042-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0042-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-leg.patch
new file mode 100644
index 0000000..0f5ce95
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0042-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-leg.patch
@@ -0,0 +1,79 @@
+From d1655b563fd705b6a9bf25a37e85076716bb1c93 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 16:38:11 +0200
+Subject: [PATCH 42/90] ARM: dts: armada-375.dtsi: Add definitions for PCIe
+ legacy INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-375.dtsi | 28 ++++++++++++++++++++++++----
+ 1 file changed, 24 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi
+index 7f2f24a29e6c..929deaf312a5 100644
+--- a/arch/arm/boot/dts/armada-375.dtsi
++++ b/arch/arm/boot/dts/armada-375.dtsi
+@@ -568,16 +568,26 @@ pcie0: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie0_intc 0>,
++ <0 0 0 2 &pcie0_intc 1>,
++ <0 0 0 3 &pcie0_intc 2>,
++ <0 0 0 4 &pcie0_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
++
++ pcie0_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ pcie1: pcie@2,0 {
+@@ -586,16 +596,26 @@ pcie1: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <1>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
++
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ };
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0043-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0043-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-leg.patch
new file mode 100644
index 0000000..319396c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0043-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-leg.patch
@@ -0,0 +1,108 @@
+From c13a941742f4496f0a73ccc9f1fcb535548d7067 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 17:39:06 +0200
+Subject: [PATCH 43/90] ARM: dts: armada-380.dtsi: Add definitions for PCIe
+ legacy INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-380.dtsi | 42 ++++++++++++++++++++++++++-----
+ 1 file changed, 36 insertions(+), 6 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-380.dtsi b/arch/arm/boot/dts/armada-380.dtsi
+index cff1269f3fbf..ce1dddb2269b 100644
+--- a/arch/arm/boot/dts/armada-380.dtsi
++++ b/arch/arm/boot/dts/armada-380.dtsi
+@@ -64,16 +64,26 @@ pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
++
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ /* x1 port */
+@@ -83,16 +93,26 @@ pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie2_intc 0>,
++ <0 0 0 2 &pcie2_intc 1>,
++ <0 0 0 3 &pcie2_intc 2>,
++ <0 0 0 4 &pcie2_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
++
++ pcie2_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ /* x1 port */
+@@ -102,16 +122,26 @@ pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie3_intc 0>,
++ <0 0 0 2 &pcie3_intc 1>,
++ <0 0 0 3 &pcie3_intc 2>,
++ <0 0 0 4 &pcie3_intc 3>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
++
++ pcie3_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+ };
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0044-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0044-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-leg.patch
new file mode 100644
index 0000000..be96c72
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0044-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-leg.patch
@@ -0,0 +1,138 @@
+From 080d8811f806c957992baa43edffa2ec017be274 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 14:56:58 +0200
+Subject: [PATCH 44/90] ARM: dts: armada-385.dtsi: Add definitions for PCIe
+ legacy INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+With this change legacy INTA, INTB, INTC and INTD interrupts are reported
+separately and not mixed into one Linux virq source anymore.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Gregory CLEMENT <gregory.clement@bootlin.com>
+Tested-by: Luis Mendes <luis.p.mendes@gmail.com>
+---
+ arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++-----
+ 1 file changed, 44 insertions(+), 8 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-385.dtsi b/arch/arm/boot/dts/armada-385.dtsi
+index f0022d10c715..83392b92dae2 100644
+--- a/arch/arm/boot/dts/armada-385.dtsi
++++ b/arch/arm/boot/dts/armada-385.dtsi
+@@ -69,16 +69,25 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ /* x1 port */
+@@ -88,16 +97,25 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie2_intc 0>,
++ <0 0 0 2 &pcie2_intc 1>,
++ <0 0 0 3 &pcie2_intc 2>,
++ <0 0 0 4 &pcie2_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
++ pcie2_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ /* x1 port */
+@@ -107,16 +125,25 @@ pcie3: pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie3_intc 0>,
++ <0 0 0 2 &pcie3_intc 1>,
++ <0 0 0 3 &pcie3_intc 2>,
++ <0 0 0 4 &pcie3_intc 3>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
++ pcie3_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ /*
+@@ -129,16 +156,25 @@ pcie4: pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie4_intc 0>,
++ <0 0 0 2 &pcie4_intc 1>,
++ <0 0 0 3 &pcie4_intc 2>,
++ <0 0 0 4 &pcie4_intc 3>;
+ marvell,pcie-port = <3>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 7>;
+ status = "disabled";
++ pcie4_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+ };
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0045-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0045-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-leg.patch
new file mode 100644
index 0000000..6264dcb
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0045-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-leg.patch
@@ -0,0 +1,137 @@
+From 6538b798220816625295fb0d3a15fe44ac2445d8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 17:47:53 +0200
+Subject: [PATCH 45/90] ARM: dts: armada-39x.dtsi: Add definitions for PCIe
+ legacy INTx interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-39x.dtsi | 56 ++++++++++++++++++++++++++-----
+ 1 file changed, 48 insertions(+), 8 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi
+index e0b7c2099831..923b035a3ab3 100644
+--- a/arch/arm/boot/dts/armada-39x.dtsi
++++ b/arch/arm/boot/dts/armada-39x.dtsi
+@@ -438,16 +438,26 @@ pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
++ <0 0 0 2 &pcie1_intc 1>,
++ <0 0 0 3 &pcie1_intc 2>,
++ <0 0 0 4 &pcie1_intc 3>;
+ marvell,pcie-port = <0>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 8>;
+ status = "disabled";
++
++ pcie1_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ /* x1 port */
+@@ -457,16 +467,26 @@ pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie2_intc 0>,
++ <0 0 0 2 &pcie2_intc 1>,
++ <0 0 0 3 &pcie2_intc 2>,
++ <0 0 0 4 &pcie2_intc 3>;
+ marvell,pcie-port = <1>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 5>;
+ status = "disabled";
++
++ pcie2_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ /* x1 port */
+@@ -476,16 +496,26 @@ pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie3_intc 0>,
++ <0 0 0 2 &pcie3_intc 1>,
++ <0 0 0 3 &pcie3_intc 2>,
++ <0 0 0 4 &pcie3_intc 3>;
+ marvell,pcie-port = <2>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 6>;
+ status = "disabled";
++
++ pcie3_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ /*
+@@ -498,16 +528,26 @@ pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
++ interrupt-names = "intx";
++ interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-map-mask = <0 0 0 0>;
+- interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie4_intc 0>,
++ <0 0 0 2 &pcie4_intc 1>,
++ <0 0 0 3 &pcie4_intc 2>,
++ <0 0 0 4 &pcie4_intc 3>;
+ marvell,pcie-port = <3>;
+ marvell,pcie-lane = <0>;
+ clocks = <&gateclk 7>;
+ status = "disabled";
++
++ pcie4_intc: interrupt-controller {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+ };
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0046-PCI-Add-PCI_EXP_SLTCTL_ASPL_DISABLE-macro.patch b/pkgs/patches-linux-5.15/0046-PCI-Add-PCI_EXP_SLTCTL_ASPL_DISABLE-macro.patch
new file mode 100644
index 0000000..35af364
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0046-PCI-Add-PCI_EXP_SLTCTL_ASPL_DISABLE-macro.patch
@@ -0,0 +1,33 @@
+From 864663e14d534b51c066d7a686f43c2502bb311a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 2 Apr 2021 22:53:01 +0200
+Subject: [PATCH 46/90] PCI: Add PCI_EXP_SLTCTL_ASPL_DISABLE macro
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add macro defining Auto Slot Power Limit Disable bit in Slot Control
+Register.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Acked-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ include/uapi/linux/pci_regs.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
+index ff6ccbc6efe9..e3456b8050f5 100644
+--- a/include/uapi/linux/pci_regs.h
++++ b/include/uapi/linux/pci_regs.h
+@@ -616,6 +616,7 @@
+ #define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */
+ #define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */
+ #define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */
++#define PCI_EXP_SLTCTL_ASPL_DISABLE 0x2000 /* Auto Slot Power Limit Disable */
+ #define PCI_EXP_SLTCTL_IBPD_DISABLE 0x4000 /* In-band PD disable */
+ #define PCI_EXP_SLTSTA 26 /* Slot Status */
+ #define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0047-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch b/pkgs/patches-linux-5.15/0047-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch
new file mode 100644
index 0000000..ebe671d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0047-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch
@@ -0,0 +1,46 @@
+From 0b9d2e40cc7379a4ad00dc198f755748eb654b49 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sun, 31 Oct 2021 16:07:05 +0100
+Subject: [PATCH 47/90] dt-bindings: Add 'slot-power-limit-milliwatt' PCIe port
+ property
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This property specifies slot power limit in mW unit. It is a form-factor
+and board specific value and must be initialized by hardware.
+
+Some PCIe controllers delegate this work to software to allow hardware
+flexibility and therefore this property basically specifies what should
+host bridge program into PCIe Slot Capabilities registers.
+
+The property needs to be specified in mW unit instead of the special format
+defined by Slot Capabilities (which encodes scaling factor or different
+unit). Host drivers should convert the value from mW to needed format.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ Documentation/devicetree/bindings/pci/pci.txt | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/Documentation/devicetree/bindings/pci/pci.txt b/Documentation/devicetree/bindings/pci/pci.txt
+index 6a8f2874a24d..b0cc133ed00d 100644
+--- a/Documentation/devicetree/bindings/pci/pci.txt
++++ b/Documentation/devicetree/bindings/pci/pci.txt
+@@ -32,6 +32,12 @@ driver implementation may support the following properties:
+ root port to downstream device and host bridge drivers can do programming
+ which depends on CLKREQ signal existence. For example, programming root port
+ not to advertise ASPM L1 Sub-States support if there is no CLKREQ signal.
++- slot-power-limit-milliwatt:
++ If present, this property specifies slot power limit in milliwatts. Host
++ drivers can parse this property and use it for programming Root Port or host
++ bridge, or for composing and sending PCIe Set_Slot_Power_Limit messages
++ through the Root Port or host bridge when transitioning PCIe link from a
++ non-DL_Up Status to a DL_Up Status.
+
+ PCI-PCI Bridge properties
+ -------------------------
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0048-PCI-Add-function-for-parsing-slot-power-limit-milliw.patch b/pkgs/patches-linux-5.15/0048-PCI-Add-function-for-parsing-slot-power-limit-milliw.patch
new file mode 100644
index 0000000..4518849
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0048-PCI-Add-function-for-parsing-slot-power-limit-milliw.patch
@@ -0,0 +1,135 @@
+From 3514828cb2cf52e545fe71bb7dc2d105d8b0084e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sun, 31 Oct 2021 16:07:06 +0100
+Subject: [PATCH 48/90] PCI: Add function for parsing
+ 'slot-power-limit-milliwatt' DT property
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add function of_pci_get_slot_power_limit(), which parses the
+'slot-power-limit-milliwatt' DT property, returning the value in
+milliwatts and in format ready for the PCIe Slot Capabilities Register.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ drivers/pci/of.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/pci/pci.h | 15 ++++++++++
+ 2 files changed, 85 insertions(+)
+
+diff --git a/drivers/pci/of.c b/drivers/pci/of.c
+index d84381ce82b5..1372da653929 100644
+--- a/drivers/pci/of.c
++++ b/drivers/pci/of.c
+@@ -627,3 +627,73 @@ int of_pci_get_max_link_speed(struct device_node *node)
+ return max_link_speed;
+ }
+ EXPORT_SYMBOL_GPL(of_pci_get_max_link_speed);
++
++/**
++ * of_pci_get_slot_power_limit - Parses the "slot-power-limit-milliwatt"
++ * property.
++ *
++ * @node: device tree node with the slot power limit information
++ * @slot_power_limit_value: pointer where the value should be stored in PCIe
++ * Slot Capabilities Register format
++ * @slot_power_limit_scale: pointer where the scale should be stored in PCIe
++ * Slot Capabilities Register format
++ *
++ * Returns the slot power limit in milliwatts and if @slot_power_limit_value
++ * and @slot_power_limit_scale pointers are non-NULL, fills in the value and
++ * scale in format used by PCIe Slot Capabilities Register.
++ *
++ * If the property is not found or is invalid, returns 0.
++ */
++u32 of_pci_get_slot_power_limit(struct device_node *node,
++ u8 *slot_power_limit_value,
++ u8 *slot_power_limit_scale)
++{
++ u32 slot_power_limit_mw;
++ u8 value, scale;
++
++ if (of_property_read_u32(node, "slot-power-limit-milliwatt",
++ &slot_power_limit_mw))
++ slot_power_limit_mw = 0;
++
++ /* Calculate Slot Power Limit Value and Slot Power Limit Scale */
++ if (slot_power_limit_mw == 0) {
++ value = 0x00;
++ scale = 0;
++ } else if (slot_power_limit_mw <= 255) {
++ value = slot_power_limit_mw;
++ scale = 3;
++ } else if (slot_power_limit_mw <= 255*10) {
++ value = slot_power_limit_mw / 10;
++ scale = 2;
++ slot_power_limit_mw = slot_power_limit_mw / 10 * 10;
++ } else if (slot_power_limit_mw <= 255*100) {
++ value = slot_power_limit_mw / 100;
++ scale = 1;
++ slot_power_limit_mw = slot_power_limit_mw / 100 * 100;
++ } else if (slot_power_limit_mw <= 239*1000) {
++ value = slot_power_limit_mw / 1000;
++ scale = 0;
++ slot_power_limit_mw = slot_power_limit_mw / 1000 * 1000;
++ } else if (slot_power_limit_mw < 250*1000) {
++ value = 0xEF;
++ scale = 0;
++ slot_power_limit_mw = 239*1000;
++ } else if (slot_power_limit_mw <= 600*1000) {
++ value = 0xF0 + (slot_power_limit_mw / 1000 - 250) / 25;
++ scale = 0;
++ slot_power_limit_mw = slot_power_limit_mw / (1000*25) * (1000*25);
++ } else {
++ value = 0xFE;
++ scale = 0;
++ slot_power_limit_mw = 600*1000;
++ }
++
++ if (slot_power_limit_value)
++ *slot_power_limit_value = value;
++
++ if (slot_power_limit_scale)
++ *slot_power_limit_scale = scale;
++
++ return slot_power_limit_mw;
++}
++EXPORT_SYMBOL_GPL(of_pci_get_slot_power_limit);
+diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
+index 1cce56c2aea0..9352278141be 100644
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -665,6 +665,9 @@ struct device_node;
+ int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
+ int of_get_pci_domain_nr(struct device_node *node);
+ int of_pci_get_max_link_speed(struct device_node *node);
++u32 of_pci_get_slot_power_limit(struct device_node *node,
++ u8 *slot_power_limit_value,
++ u8 *slot_power_limit_scale);
+ void pci_set_of_node(struct pci_dev *dev);
+ void pci_release_of_node(struct pci_dev *dev);
+ void pci_set_bus_of_node(struct pci_bus *bus);
+@@ -691,6 +694,18 @@ of_pci_get_max_link_speed(struct device_node *node)
+ return -EINVAL;
+ }
+
++static inline u32
++of_pci_get_slot_power_limit(struct device_node *node,
++ u8 *slot_power_limit_value,
++ u8 *slot_power_limit_scale)
++{
++ if (slot_power_limit_value)
++ *slot_power_limit_value = 0;
++ if (slot_power_limit_scale)
++ *slot_power_limit_scale = 0;
++ return 0;
++}
++
+ static inline void pci_set_of_node(struct pci_dev *dev) { }
+ static inline void pci_release_of_node(struct pci_dev *dev) { }
+ static inline void pci_set_bus_of_node(struct pci_bus *bus) { }
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch b/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch
new file mode 100644
index 0000000..4c526f6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch
@@ -0,0 +1,210 @@
+From 5c87d5ff707236098214303289cd5724aebbd0ab Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 14:25:33 +0200
+Subject: [PATCH 49/90] PCI: mvebu: Add support for sending
+ Set_Slot_Power_Limit message
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If DT supplies the 'slot-power-limit-milliwatt' property, program
+the value in the Slot Power Limit in the Slot Capabilities register
+and program the Root Port to send a Set_Slot_Power_Limit Message
+when the Link transitions to DL_Up.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 97 ++++++++++++++++++++++++++++--
+ 1 file changed, 92 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index c9311829dfe3..3c48b15e3948 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -8,6 +8,7 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
++#include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/gpio.h>
+@@ -66,6 +67,12 @@
+ #define PCIE_STAT_BUS 0xff00
+ #define PCIE_STAT_DEV 0x1f0000
+ #define PCIE_STAT_LINK_DOWN BIT(0)
++#define PCIE_SSPL_OFF 0x1a0c
++#define PCIE_SSPL_VALUE_SHIFT 0
++#define PCIE_SSPL_VALUE_MASK GENMASK(7, 0)
++#define PCIE_SSPL_SCALE_SHIFT 8
++#define PCIE_SSPL_SCALE_MASK GENMASK(9, 8)
++#define PCIE_SSPL_ENABLE BIT(16)
+ #define PCIE_RC_RTSTA 0x1a14
+ #define PCIE_DEBUG_CTRL 0x1a60
+ #define PCIE_DEBUG_SOFT_RESET BIT(20)
+@@ -111,6 +118,8 @@ struct mvebu_pcie_port {
+ struct mvebu_pcie_window iowin;
+ u32 saved_pcie_stat;
+ struct resource regs;
++ u8 slot_power_limit_value;
++ u8 slot_power_limit_scale;
+ struct irq_domain *intx_irq_domain;
+ raw_spinlock_t irq_lock;
+ int intx_irq;
+@@ -239,7 +248,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
+
+ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ {
+- u32 ctrl, lnkcap, cmd, dev_rev, unmask;
++ u32 ctrl, lnkcap, cmd, dev_rev, unmask, sspl;
+
+ /* Setup PCIe controller to Root Complex mode. */
+ ctrl = mvebu_readl(port, PCIE_CTRL_OFF);
+@@ -292,6 +301,20 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ /* Point PCIe unit MBUS decode windows to DRAM space. */
+ mvebu_pcie_setup_wins(port);
+
++ /*
++ * Program Root Port to automatically send Set_Slot_Power_Limit
++ * PCIe Message when changing status from Dl_Down to Dl_Up and valid
++ * slot power limit was specified.
++ */
++ sspl = mvebu_readl(port, PCIE_SSPL_OFF);
++ sspl &= ~(PCIE_SSPL_VALUE_MASK | PCIE_SSPL_SCALE_MASK | PCIE_SSPL_ENABLE);
++ if (port->slot_power_limit_value) {
++ sspl |= port->slot_power_limit_value << PCIE_SSPL_VALUE_SHIFT;
++ sspl |= port->slot_power_limit_scale << PCIE_SSPL_SCALE_SHIFT;
++ sspl |= PCIE_SSPL_ENABLE;
++ }
++ mvebu_writel(port, sspl, PCIE_SSPL_OFF);
++
+ /* Mask all interrupt sources. */
+ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF);
+
+@@ -633,9 +656,24 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+ (PCI_EXP_LNKSTA_DLLLA << 16) : 0);
+ break;
+
+- case PCI_EXP_SLTCTL:
+- *value = PCI_EXP_SLTSTA_PDS << 16;
++ case PCI_EXP_SLTCTL: {
++ u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl);
++ u16 slotsta = le16_to_cpu(bridge->pcie_conf.slotsta);
++ u32 val = 0;
++ /*
++ * When slot power limit was not specified in DT then
++ * ASPL_DISABLE bit is stored only in emulated config space.
++ * Otherwise reflect status of PCIE_SSPL_ENABLE bit in HW.
++ */
++ if (!port->slot_power_limit_value)
++ val |= slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE;
++ else if (!(mvebu_readl(port, PCIE_SSPL_OFF) & PCIE_SSPL_ENABLE))
++ val |= PCI_EXP_SLTCTL_ASPL_DISABLE;
++ /* This callback is 32-bit and in high bits is slot status. */
++ val |= slotsta << 16;
++ *value = val;
+ break;
++ }
+
+ case PCI_EXP_RTSTA:
+ *value = mvebu_readl(port, PCIE_RC_RTSTA);
+@@ -779,6 +817,22 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
+ break;
+
++ case PCI_EXP_SLTCTL:
++ /*
++ * Allow to change PCIE_SSPL_ENABLE bit only when slot power
++ * limit was specified in DT and configured into HW.
++ */
++ if ((mask & PCI_EXP_SLTCTL_ASPL_DISABLE) &&
++ port->slot_power_limit_value) {
++ u32 sspl = mvebu_readl(port, PCIE_SSPL_OFF);
++ if (new & PCI_EXP_SLTCTL_ASPL_DISABLE)
++ sspl &= ~PCIE_SSPL_ENABLE;
++ else
++ sspl |= PCIE_SSPL_ENABLE;
++ mvebu_writel(port, sspl, PCIE_SSPL_OFF);
++ }
++ break;
++
+ case PCI_EXP_RTSTA:
+ /*
+ * PME Status bit in Root Status Register (PCIE_RC_RTSTA)
+@@ -873,8 +927,26 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ /*
+ * Older mvebu hardware provides PCIe Capability structure only in
+ * version 1. New hardware provides it in version 2.
++ * Enable slot support which is emulated.
+ */
+- bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver);
++ bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver | PCI_EXP_FLAGS_SLOT);
++
++ /*
++ * Set Presence Detect State bit permanently as there is no support for
++ * unplugging PCIe card from the slot. Assume that PCIe card is always
++ * connected in slot.
++ *
++ * Set physical slot number to port+1 as mvebu ports are indexed from
++ * zero and zero value is reserved for ports within the same silicon
++ * as Root Port which is not mvebu case.
++ *
++ * Also set correct slot power limit.
++ */
++ bridge->pcie_conf.slotcap = cpu_to_le32(
++ FIELD_PREP(PCI_EXP_SLTCAP_SPLV, port->slot_power_limit_value) |
++ FIELD_PREP(PCI_EXP_SLTCAP_SPLS, port->slot_power_limit_scale) |
++ FIELD_PREP(PCI_EXP_SLTCAP_PSN, port->port+1));
++ bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS);
+
+ bridge->subsystem_vendor_id = ssdev_id & 0xffff;
+ bridge->subsystem_id = ssdev_id >> 16;
+@@ -1198,6 +1270,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ {
+ struct device *dev = &pcie->pdev->dev;
+ enum of_gpio_flags flags;
++ u32 slot_power_limit;
+ int reset_gpio, ret;
+ u32 num_lanes;
+
+@@ -1298,6 +1371,15 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ port->reset_gpio = gpio_to_desc(reset_gpio);
+ }
+
++ slot_power_limit = of_pci_get_slot_power_limit(child,
++ &port->slot_power_limit_value,
++ &port->slot_power_limit_scale);
++ if (slot_power_limit)
++ dev_info(dev, "%s: Slot power limit %u.%uW\n",
++ port->name,
++ slot_power_limit / 1000,
++ (slot_power_limit / 100) % 10);
++
+ port->clk = of_clk_get_by_name(child, NULL);
+ if (IS_ERR(port->clk)) {
+ dev_err(dev, "%s: cannot get clock\n", port->name);
+@@ -1595,7 +1677,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+ {
+ struct mvebu_pcie *pcie = platform_get_drvdata(pdev);
+ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
+- u32 cmd;
++ u32 cmd, sspl;
+ int i;
+
+ /* Remove PCI bus with all devices. */
+@@ -1632,6 +1714,11 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+ /* Free config space for emulated root bridge. */
+ pci_bridge_emul_cleanup(&port->bridge);
+
++ /* Disable sending Set_Slot_Power_Limit PCIe Message. */
++ sspl = mvebu_readl(port, PCIE_SSPL_OFF);
++ sspl &= ~(PCIE_SSPL_VALUE_MASK | PCIE_SSPL_SCALE_MASK | PCIE_SSPL_ENABLE);
++ mvebu_writel(port, sspl, PCIE_SSPL_OFF);
++
+ /* Disable and clear BARs and windows. */
+ mvebu_pcie_disable_wins(port);
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0050-ARM-dts-turris-omnia-Set-PCIe-slot-power-limit-milli.patch b/pkgs/patches-linux-5.15/0050-ARM-dts-turris-omnia-Set-PCIe-slot-power-limit-milli.patch
new file mode 100644
index 0000000..f14ff4d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0050-ARM-dts-turris-omnia-Set-PCIe-slot-power-limit-milli.patch
@@ -0,0 +1,43 @@
+From f06d18dc4bdc230d247370c54a9ad9d4676ceb6d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Sep 2021 11:40:05 +0200
+Subject: [PATCH 50/90] ARM: dts: turris-omnia: Set PCIe
+ slot-power-limit-milliwatt properties
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+All 3 miniPCIe slots in Turris Omnia are designed for 10 W.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-385-turris-omnia.dts | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts
+index 5bd6a66d2c2b..f240018148f6 100644
+--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts
++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts
+@@ -71,16 +71,19 @@ pcie {
+ pcie@1,0 {
+ /* Port 0, Lane 0 */
+ status = "okay";
++ slot-power-limit-milliwatt = <10000>;
+ };
+
+ pcie@2,0 {
+ /* Port 1, Lane 0 */
+ status = "okay";
++ slot-power-limit-milliwatt = <10000>;
+ };
+
+ pcie@3,0 {
+ /* Port 2, Lane 0 */
+ status = "okay";
++ slot-power-limit-milliwatt = <10000>;
+ };
+ };
+ };
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0051-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch b/pkgs/patches-linux-5.15/0051-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch
new file mode 100644
index 0000000..0d3b160
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0051-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch
@@ -0,0 +1,164 @@
+From f0a61d9c1f4396e7f3a241b9be1378ed4e5bb947 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sun, 3 Jul 2022 12:40:13 +0200
+Subject: [PATCH 51/90] PCI: pci-bridge-emul: Set position of PCI capabilities
+ to real HW value
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+mvebu and aardvark HW have PCIe capabilities on different offset in PCI
+config space. Extend pci-bridge-emul.c code to allow setting custom driver
+custom value where PCIe capabilities starts.
+
+With this change PCIe capabilities of both drivers are reported at the same
+location as where they are reported by U-Boot - in their real HW offset.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 1 +
+ drivers/pci/controller/pci-mvebu.c | 1 +
+ drivers/pci/pci-bridge-emul.c | 48 +++++++++++++++++----------
+ drivers/pci/pci-bridge-emul.h | 2 ++
+ 4 files changed, 35 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index 3fe4d3329267..4ed1b3ed3d35 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -984,6 +984,7 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie)
+ bridge->pcie_conf.rootcap = cpu_to_le16(PCI_EXP_RTCAP_CRSVIS);
+
+ bridge->has_pcie = true;
++ bridge->pcie_start = PCIE_CORE_PCIEXP_CAP;
+ bridge->data = pcie;
+ bridge->ops = &advk_pci_bridge_emul_ops;
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 3c48b15e3948..8205b4d1f04c 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -951,6 +951,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ bridge->subsystem_vendor_id = ssdev_id & 0xffff;
+ bridge->subsystem_id = ssdev_id >> 16;
+ bridge->has_pcie = true;
++ bridge->pcie_start = PCIE_CAP_PCIEXP;
+ bridge->data = port;
+ bridge->ops = &mvebu_pci_bridge_emul_ops;
+
+diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
+index a5b662cc89d0..7a8a7c804050 100644
+--- a/drivers/pci/pci-bridge-emul.c
++++ b/drivers/pci/pci-bridge-emul.c
+@@ -22,11 +22,7 @@
+
+ #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF
+ #define PCI_CAP_SSID_SIZEOF (PCI_SSVID_DEVICE_ID + 2)
+-#define PCI_CAP_SSID_START PCI_BRIDGE_CONF_END
+-#define PCI_CAP_SSID_END (PCI_CAP_SSID_START + PCI_CAP_SSID_SIZEOF)
+ #define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2)
+-#define PCI_CAP_PCIE_START PCI_CAP_SSID_END
+-#define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF)
+
+ /**
+ * struct pci_bridge_reg_behavior - register bits behaviors
+@@ -324,7 +320,7 @@ pci_bridge_emul_read_ssid(struct pci_bridge_emul *bridge, int reg, u32 *value)
+ switch (reg) {
+ case PCI_CAP_LIST_ID:
+ *value = PCI_CAP_ID_SSVID |
+- (bridge->has_pcie ? (PCI_CAP_PCIE_START << 8) : 0);
++ ((bridge->pcie_start > bridge->ssid_start) ? (bridge->pcie_start << 8) : 0);
+ return PCI_BRIDGE_EMUL_HANDLED;
+
+ case PCI_SSVID_VENDOR_ID:
+@@ -363,18 +359,33 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
+ if (!bridge->pci_regs_behavior)
+ return -ENOMEM;
+
+- if (bridge->subsystem_vendor_id)
+- bridge->conf.capabilities_pointer = PCI_CAP_SSID_START;
+- else if (bridge->has_pcie)
+- bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START;
+- else
+- bridge->conf.capabilities_pointer = 0;
++ /* If ssid_start and pcie_start were not specified then choose the lowest possible value. */
++ if (!bridge->ssid_start && !bridge->pcie_start) {
++ if (bridge->subsystem_vendor_id)
++ bridge->ssid_start = PCI_BRIDGE_CONF_END;
++ if (bridge->has_pcie)
++ bridge->pcie_start = bridge->ssid_start + PCI_CAP_SSID_SIZEOF;
++ } else if (!bridge->ssid_start && bridge->subsystem_vendor_id) {
++ if (bridge->pcie_start - PCI_BRIDGE_CONF_END >= PCI_CAP_SSID_SIZEOF)
++ bridge->ssid_start = PCI_BRIDGE_CONF_END;
++ else
++ bridge->ssid_start = bridge->pcie_start + PCI_CAP_PCIE_SIZEOF;
++ } else if (!bridge->pcie_start && bridge->has_pcie) {
++ if (bridge->ssid_start - PCI_BRIDGE_CONF_END >= PCI_CAP_PCIE_SIZEOF)
++ bridge->pcie_start = PCI_BRIDGE_CONF_END;
++ else
++ bridge->pcie_start = bridge->ssid_start + PCI_CAP_SSID_SIZEOF;
++ }
++
++ bridge->conf.capabilities_pointer = min(bridge->ssid_start, bridge->pcie_start);
+
+ if (bridge->conf.capabilities_pointer)
+ bridge->conf.status |= cpu_to_le16(PCI_STATUS_CAP_LIST);
+
+ if (bridge->has_pcie) {
+ bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP;
++ bridge->pcie_conf.next = (bridge->ssid_start > bridge->pcie_start) ?
++ bridge->ssid_start : 0;
+ bridge->pcie_conf.cap |= cpu_to_le16(PCI_EXP_TYPE_ROOT_PORT << 4);
+ bridge->pcie_cap_regs_behavior =
+ kmemdup(pcie_cap_regs_behavior,
+@@ -457,15 +468,17 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
+ read_op = bridge->ops->read_base;
+ cfgspace = (__le32 *) &bridge->conf;
+ behavior = bridge->pci_regs_behavior;
+- } else if (reg >= PCI_CAP_SSID_START && reg < PCI_CAP_SSID_END && bridge->subsystem_vendor_id) {
++ } else if (reg >= bridge->ssid_start && reg < bridge->ssid_start + PCI_CAP_SSID_SIZEOF &&
++ bridge->subsystem_vendor_id) {
+ /* Emulated PCI Bridge Subsystem Vendor ID capability */
+- reg -= PCI_CAP_SSID_START;
++ reg -= bridge->ssid_start;
+ read_op = pci_bridge_emul_read_ssid;
+ cfgspace = NULL;
+ behavior = NULL;
+- } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) {
++ } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF &&
++ bridge->has_pcie) {
+ /* Our emulated PCIe capability */
+- reg -= PCI_CAP_PCIE_START;
++ reg -= bridge->pcie_start;
+ read_op = bridge->ops->read_pcie;
+ cfgspace = (__le32 *) &bridge->pcie_conf;
+ behavior = bridge->pcie_cap_regs_behavior;
+@@ -536,9 +549,10 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+ write_op = bridge->ops->write_base;
+ cfgspace = (__le32 *) &bridge->conf;
+ behavior = bridge->pci_regs_behavior;
+- } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) {
++ } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF &&
++ bridge->has_pcie) {
+ /* Our emulated PCIe capability */
+- reg -= PCI_CAP_PCIE_START;
++ reg -= bridge->pcie_start;
+ write_op = bridge->ops->write_pcie;
+ cfgspace = (__le32 *) &bridge->pcie_conf;
+ behavior = bridge->pcie_cap_regs_behavior;
+diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h
+index 71392b67471d..2a0e59c7f0d9 100644
+--- a/drivers/pci/pci-bridge-emul.h
++++ b/drivers/pci/pci-bridge-emul.h
+@@ -131,6 +131,8 @@ struct pci_bridge_emul {
+ struct pci_bridge_reg_behavior *pci_regs_behavior;
+ struct pci_bridge_reg_behavior *pcie_cap_regs_behavior;
+ void *data;
++ u8 pcie_start;
++ u8 ssid_start;
+ bool has_pcie;
+ u16 subsystem_vendor_id;
+ u16 subsystem_id;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0052-irqchip-armada-370-xp-Do-not-allow-mapping-IRQ-0-and.patch b/pkgs/patches-linux-5.15/0052-irqchip-armada-370-xp-Do-not-allow-mapping-IRQ-0-and.patch
new file mode 100644
index 0000000..f93f96f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0052-irqchip-armada-370-xp-Do-not-allow-mapping-IRQ-0-and.patch
@@ -0,0 +1,37 @@
+From 9e71688dc62f92af7691bc3c0c30c25f82022f6d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sun, 17 Apr 2022 22:56:55 +0200
+Subject: [PATCH 52/90] irqchip/armada-370-xp: Do not allow mapping IRQ 0 and 1
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+IRQs 0 and 1 cannot be mapped, they are handled internally by this driver
+and this driver does not call generic_handle_domain_irq() for these IRQs.
+So do not allow mapping these IRQs and correctly propagate error from the
+.irq_map callback.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Cc: stable@vger.kernel.org
+---
+ drivers/irqchip/irq-armada-370-xp.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
+index 01709c61e364..3fa6bd70684b 100644
+--- a/drivers/irqchip/irq-armada-370-xp.c
++++ b/drivers/irqchip/irq-armada-370-xp.c
+@@ -546,6 +546,10 @@ static struct irq_chip armada_370_xp_irq_chip = {
+ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
+ unsigned int virq, irq_hw_number_t hw)
+ {
++ /* IRQs 0 and 1 cannot be mapped, they are handled internally */
++ if (hw <= 1)
++ return -EINVAL;
++
+ armada_370_xp_irq_mask(irq_get_irq_data(virq));
+ if (!is_percpu_irq(hw))
+ writel(hw, per_cpu_int_base +
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0053-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch b/pkgs/patches-linux-5.15/0053-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch
new file mode 100644
index 0000000..d09446f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0053-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch
@@ -0,0 +1,99 @@
+From 149b6e20ecbc96ede412736d5c26496382d382e7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 24 May 2022 13:57:37 +0200
+Subject: [PATCH 53/90] PCI: mvebu: Use devm_request_irq() for registering
+ interrupt handler
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Same as in commit a3b69dd0ad62 ("Revert "PCI: aardvark: Rewrite IRQ code to
+chained IRQ handler"") for pci-aardvark driver, use devm_request_irq()
+instead of chained IRQ handler in pci-mvebu.c driver.
+
+This change fixes affinity support and allows to pin interrupts from
+different PCIe controllers to different CPU cores.
+
+Fixes: ec075262648f ("PCI: mvebu: Implement support for legacy INTx interrupts")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 30 +++++++++++++++++-------------
+ 1 file changed, 17 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 8205b4d1f04c..d8cba09649ba 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -1097,16 +1097,13 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port)
+ return 0;
+ }
+
+-static void mvebu_pcie_irq_handler(struct irq_desc *desc)
++static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg)
+ {
+- struct mvebu_pcie_port *port = irq_desc_get_handler_data(desc);
+- struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct mvebu_pcie_port *port = arg;
+ struct device *dev = &port->pcie->pdev->dev;
+ u32 cause, unmask, status;
+ int i;
+
+- chained_irq_enter(chip, desc);
+-
+ cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF);
+ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
+ status = cause & unmask;
+@@ -1120,7 +1117,7 @@ static void mvebu_pcie_irq_handler(struct irq_desc *desc)
+ dev_err_ratelimited(dev, "unexpected INT%c IRQ\n", (char)i+'A');
+ }
+
+- chained_irq_exit(chip, desc);
++ return status ? IRQ_HANDLED : IRQ_NONE;
+ }
+
+ static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+@@ -1580,9 +1577,20 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+ mvebu_pcie_powerdown(port);
+ continue;
+ }
+- irq_set_chained_handler_and_data(irq,
+- mvebu_pcie_irq_handler,
+- port);
++
++ ret = devm_request_irq(dev, irq, mvebu_pcie_irq_handler,
++ IRQF_SHARED | IRQF_NO_THREAD,
++ port->name, port);
++ if (ret) {
++ dev_err(dev, "%s: cannot register interrupt handler: %d\n",
++ port->name, ret);
++ irq_domain_remove(port->intx_irq_domain);
++ pci_bridge_emul_cleanup(&port->bridge);
++ devm_iounmap(dev, port->base);
++ port->base = NULL;
++ mvebu_pcie_powerdown(port);
++ continue;
++ }
+ }
+
+ /*
+@@ -1689,7 +1697,6 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = &pcie->ports[i];
+- int irq = port->intx_irq;
+
+ if (!port->base)
+ continue;
+@@ -1705,9 +1712,6 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+ /* Clear all interrupt causes. */
+ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF);
+
+- if (irq > 0)
+- irq_set_chained_handler_and_data(irq, NULL, NULL);
+-
+ /* Remove IRQ domains. */
+ if (port->intx_irq_domain)
+ irq_domain_remove(port->intx_irq_domain);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0054-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch b/pkgs/patches-linux-5.15/0054-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch
new file mode 100644
index 0000000..cd1f82f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0054-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch
@@ -0,0 +1,47 @@
+From a3502251f0a195b584efc6b29ecac0ac53ac582a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sat, 9 Jul 2022 16:12:40 +0200
+Subject: [PATCH 54/90] PCI: mvebu: Dispose INTx irqs prior to removing INTx
+ domain
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Documentation for irq_domain_remove() says that all mapping within the
+domain must be disposed prior to domain remove.
+
+Currently INTx irqs are not disposed in pci-mvebu.c device unbind callback
+which cause that kernel crashes after unloading driver and trying to read
+/sys/kernel/debug/irq/irqs/<num> or /proc/interrupts.
+
+Fixes: ec075262648f ("PCI: mvebu: Implement support for legacy INTx interrupts")
+Reported-by: Hajo Noerenberg <hajo-linux-bugzilla@noerenberg.de>
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index d8cba09649ba..0122c65b0269 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -1713,8 +1713,15 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF);
+
+ /* Remove IRQ domains. */
+- if (port->intx_irq_domain)
++ if (port->intx_irq_domain) {
++ int virq, j;
++ for (j = 0; j < PCI_NUM_INTX; j++) {
++ virq = irq_find_mapping(port->intx_irq_domain, j);
++ if (virq > 0)
++ irq_dispose_mapping(virq);
++ }
+ irq_domain_remove(port->intx_irq_domain);
++ }
+
+ /* Free config space for emulated root bridge. */
+ pci_bridge_emul_cleanup(&port->bridge);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0055-PCI-Assign-PCI-domain-by-ida_alloc.patch b/pkgs/patches-linux-5.15/0055-PCI-Assign-PCI-domain-by-ida_alloc.patch
new file mode 100644
index 0000000..b639b72
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0055-PCI-Assign-PCI-domain-by-ida_alloc.patch
@@ -0,0 +1,215 @@
+From 4194dcecf85a825287ce0d4931cf27c36ba899ce Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sat, 2 Jul 2022 21:37:51 +0200
+Subject: [PATCH 55/90] PCI: Assign PCI domain by ida_alloc()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Replace assignment of PCI domain from atomic_inc_return() to ida_alloc().
+
+Use two IDAs, one for static domain allocations (those which are defined in
+device tree) and second for dynamic allocations (all other).
+
+During removal of root bus / host bridge release also allocated domain id.
+So released id can be reused again, for example in situation when
+dynamically loading and unloading native PCI host bridge drivers.
+
+This change also allows to mix static device tree assignment and dynamic by
+kernel as all static allocations are reserved in dynamic pool.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/pci.c | 103 +++++++++++++++++++++++++------------------
+ drivers/pci/probe.c | 5 +++
+ drivers/pci/remove.c | 6 +++
+ include/linux/pci.h | 1 +
+ 4 files changed, 72 insertions(+), 43 deletions(-)
+
+diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
+index 2bfff2328cf8..99aaf6d8d92c 100644
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -6679,60 +6679,70 @@ static void pci_no_domains(void)
+ }
+
+ #ifdef CONFIG_PCI_DOMAINS_GENERIC
+-static atomic_t __domain_nr = ATOMIC_INIT(-1);
++static DEFINE_IDA(pci_domain_nr_static_ida);
++static DEFINE_IDA(pci_domain_nr_dynamic_ida);
+
+-static int pci_get_new_domain_nr(void)
++static void of_pci_reserve_static_domain_nr(void)
+ {
+- return atomic_inc_return(&__domain_nr);
++ struct device_node *np;
++ int domain_nr;
++
++ for_each_node_by_type(np, "pci") {
++ domain_nr = of_get_pci_domain_nr(np);
++ if (domain_nr < 0)
++ continue;
++ /*
++ * Permanently allocate domain_nr in dynamic_ida
++ * to prevent it from dynamic allocation.
++ */
++ ida_alloc_range(&pci_domain_nr_dynamic_ida,
++ domain_nr, domain_nr, GFP_KERNEL);
++ }
+ }
+
+ static int of_pci_bus_find_domain_nr(struct device *parent)
+ {
+- static int use_dt_domains = -1;
+- int domain = -1;
++ static bool static_domains_reserved = false;
++ int domain_nr;
+
+- if (parent)
+- domain = of_get_pci_domain_nr(parent->of_node);
++ /* On the first call scan device tree for static allocations. */
++ if (!static_domains_reserved) {
++ of_pci_reserve_static_domain_nr();
++ static_domains_reserved = true;
++ }
++
++ if (parent) {
++ /*
++ * If domain is in DT then allocate it in static IDA.
++ * This prevent duplicate static allocations in case
++ * of errors in DT.
++ */
++ domain_nr = of_get_pci_domain_nr(parent->of_node);
++ if (domain_nr >= 0)
++ return ida_alloc_range(&pci_domain_nr_static_ida,
++ domain_nr, domain_nr,
++ GFP_KERNEL);
++ }
+
+ /*
+- * Check DT domain and use_dt_domains values.
+- *
+- * If DT domain property is valid (domain >= 0) and
+- * use_dt_domains != 0, the DT assignment is valid since this means
+- * we have not previously allocated a domain number by using
+- * pci_get_new_domain_nr(); we should also update use_dt_domains to
+- * 1, to indicate that we have just assigned a domain number from
+- * DT.
+- *
+- * If DT domain property value is not valid (ie domain < 0), and we
+- * have not previously assigned a domain number from DT
+- * (use_dt_domains != 1) we should assign a domain number by
+- * using the:
+- *
+- * pci_get_new_domain_nr()
+- *
+- * API and update the use_dt_domains value to keep track of method we
+- * are using to assign domain numbers (use_dt_domains = 0).
+- *
+- * All other combinations imply we have a platform that is trying
+- * to mix domain numbers obtained from DT and pci_get_new_domain_nr(),
+- * which is a recipe for domain mishandling and it is prevented by
+- * invalidating the domain value (domain = -1) and printing a
+- * corresponding error.
++ * If domain was not specified in DT then choose free id from dynamic
++ * allocations. All domain numbers from DT are permanently in dynamic
++ * allocations to prevent assigning them to other DT nodes without
++ * static domain.
+ */
+- if (domain >= 0 && use_dt_domains) {
+- use_dt_domains = 1;
+- } else if (domain < 0 && use_dt_domains != 1) {
+- use_dt_domains = 0;
+- domain = pci_get_new_domain_nr();
+- } else {
+- if (parent)
+- pr_err("Node %pOF has ", parent->of_node);
+- pr_err("Inconsistent \"linux,pci-domain\" property in DT\n");
+- domain = -1;
+- }
++ return ida_alloc(&pci_domain_nr_dynamic_ida, GFP_KERNEL);
++}
+
+- return domain;
++static void of_pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
++{
++ if (bus->domain_nr < 0)
++ return;
++
++ /* Release domain from ida in which was it allocated. */
++ if (of_get_pci_domain_nr(parent->of_node) == bus->domain_nr)
++ ida_free(&pci_domain_nr_static_ida, bus->domain_nr);
++ else
++ ida_free(&pci_domain_nr_dynamic_ida, bus->domain_nr);
+ }
+
+ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
+@@ -6740,6 +6750,13 @@ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
+ return acpi_disabled ? of_pci_bus_find_domain_nr(parent) :
+ acpi_pci_bus_find_domain_nr(bus);
+ }
++
++void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
++{
++ if (!acpi_disabled)
++ return;
++ of_pci_bus_release_domain_nr(bus, parent);
++}
+ #endif
+
+ /**
+diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
+index d9fc02a71baa..1b6f817454e2 100644
+--- a/drivers/pci/probe.c
++++ b/drivers/pci/probe.c
+@@ -908,6 +908,8 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
+ bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
+ else
+ bus->domain_nr = bridge->domain_nr;
++ if (bus->domain_nr < 0)
++ goto free;
+ #endif
+
+ b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
+@@ -1007,6 +1009,9 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
+ device_del(&bridge->dev);
+
+ free:
++#ifdef CONFIG_PCI_DOMAINS_GENERIC
++ pci_bus_release_domain_nr(bus, parent);
++#endif
+ kfree(bus);
+ return err;
+ }
+diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
+index 4c54c75050dc..0145aef1b930 100644
+--- a/drivers/pci/remove.c
++++ b/drivers/pci/remove.c
+@@ -160,6 +160,12 @@ void pci_remove_root_bus(struct pci_bus *bus)
+ pci_remove_bus(bus);
+ host_bridge->bus = NULL;
+
++#ifdef CONFIG_PCI_DOMAINS_GENERIC
++ /* Release domain_nr if it was dynamically allocated */
++ if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
++ pci_bus_release_domain_nr(bus, host_bridge->dev.parent);
++#endif
++
+ /* remove the host bridge */
+ device_del(&host_bridge->dev);
+ }
+diff --git a/include/linux/pci.h b/include/linux/pci.h
+index 9d6e75222868..e127ef7c3930 100644
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -1707,6 +1707,7 @@ static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
+ { return 0; }
+ #endif
+ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent);
++void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent);
+ #endif
+
+ /* Some architectures require additional setup to direct VGA traffic */
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0056-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch b/pkgs/patches-linux-5.15/0056-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch
new file mode 100644
index 0000000..323e8bc
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0056-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch
@@ -0,0 +1,68 @@
+From 600952067c786972461d1ec16bfd71221066341f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 12 Aug 2022 11:09:11 +0200
+Subject: [PATCH 56/90] PCI: mvebu: Fix endianity when accessing pci emul
+ bridge members
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+PCI emul bridge members iolimitupper, iobaseupper, memlimit and membase are
+of type __le16, so correctly access these members via le16_to_cpu() macros.
+
+Fixes: 4ded69473adb ("PCI: mvebu: Propagate errors when updating PCI_IO_BASE and PCI_MEM_BASE registers")
+Reported-by: kernel test robot <lkp@intel.com>
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 0122c65b0269..c5e5bc085a9d 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -528,7 +528,7 @@ static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
+
+ /* Are the new iobase/iolimit values invalid? */
+ if (conf->iolimit < conf->iobase ||
+- conf->iolimitupper < conf->iobaseupper)
++ le16_to_cpu(conf->iolimitupper) < le16_to_cpu(conf->iobaseupper))
+ return mvebu_pcie_set_window(port, port->io_target, port->io_attr,
+ &desired, &port->iowin);
+
+@@ -540,10 +540,10 @@ static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
+ * is the CPU address.
+ */
+ desired.remap = ((conf->iobase & 0xF0) << 8) |
+- (conf->iobaseupper << 16);
++ (le16_to_cpu(conf->iobaseupper) << 16);
+ desired.base = port->pcie->io.start + desired.remap;
+ desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) |
+- (conf->iolimitupper << 16)) -
++ (le16_to_cpu(conf->iolimitupper) << 16)) -
+ desired.remap) +
+ 1;
+
+@@ -557,7 +557,7 @@ static int mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
+ struct pci_bridge_emul_conf *conf = &port->bridge.conf;
+
+ /* Are the new membase/memlimit values invalid? */
+- if (conf->memlimit < conf->membase)
++ if (le16_to_cpu(conf->memlimit) < le16_to_cpu(conf->membase))
+ return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr,
+ &desired, &port->memwin);
+
+@@ -567,8 +567,8 @@ static int mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
+ * window to setup, according to the PCI-to-PCI bridge
+ * specifications.
+ */
+- desired.base = ((conf->membase & 0xFFF0) << 16);
+- desired.size = (((conf->memlimit & 0xFFF0) << 16) | 0xFFFFF) -
++ desired.base = ((le16_to_cpu(conf->membase) & 0xFFF0) << 16);
++ desired.size = (((le16_to_cpu(conf->memlimit) & 0xFFF0) << 16) | 0xFFFFF) -
+ desired.base + 1;
+
+ return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired,
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0057-ARM-dts-dove-Fix-assigned-addresses-for-every-PCIe-R.patch b/pkgs/patches-linux-5.15/0057-ARM-dts-dove-Fix-assigned-addresses-for-every-PCIe-R.patch
new file mode 100644
index 0000000..99f1872
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0057-ARM-dts-dove-Fix-assigned-addresses-for-every-PCIe-R.patch
@@ -0,0 +1,35 @@
+From ebcb5a3c9803cacf33ff2d029325519a4e27ec66 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 17 Aug 2022 23:46:32 +0200
+Subject: [PATCH 57/90] ARM: dts: dove: Fix assigned-addresses for every PCIe
+ Root Port
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port
+(PCI-to-PCI bridge) should match BDF in address part in that DT node name
+as specified resource belongs to Marvell PCIe Root Port itself.
+
+Fixes: 74ecaa403a74 ("ARM: dove: add PCIe controllers to SoC DT")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/dove.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
+index 96ba47c061a7..70d45d2b1258 100644
+--- a/arch/arm/boot/dts/dove.dtsi
++++ b/arch/arm/boot/dts/dove.dtsi
+@@ -139,7 +139,7 @@ pcie0_intc: interrupt-controller {
+ pcie1: pcie@2 {
+ device_type = "pci";
+ status = "disabled";
+- assigned-addresses = <0x82002800 0 0x80000 0 0x2000>;
++ assigned-addresses = <0x82001000 0 0x80000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ clocks = <&gate_clk 5>;
+ marvell,pcie-port = <1>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0058-ARM-dts-armada-370-Fix-assigned-addresses-for-every-.patch b/pkgs/patches-linux-5.15/0058-ARM-dts-armada-370-Fix-assigned-addresses-for-every-.patch
new file mode 100644
index 0000000..985fc20
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0058-ARM-dts-armada-370-Fix-assigned-addresses-for-every-.patch
@@ -0,0 +1,35 @@
+From 20f5fa51c6e4dc3ee944b3a207d641ba2e6964df Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 18 Aug 2022 00:01:14 +0200
+Subject: [PATCH 58/90] ARM: dts: armada-370: Fix assigned-addresses for every
+ PCIe Root Port
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port
+(PCI-to-PCI bridge) should match BDF in address part in that DT node name
+as specified resource belongs to Marvell PCIe Root Port itself.
+
+Fixes: a09a0b7c6ff1 ("arm: mvebu: add PCIe Device Tree informations for Armada 370")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-370.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi
+index 9dc928859ad3..2013a5ccecd3 100644
+--- a/arch/arm/boot/dts/armada-370.dtsi
++++ b/arch/arm/boot/dts/armada-370.dtsi
+@@ -84,7 +84,7 @@ pcie0_intc: interrupt-controller {
+
+ pcie2: pcie@2,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82002800 0 0x80000 0 0x2000>;
++ assigned-addresses = <0x82001000 0 0x80000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0059-ARM-dts-armada-xp-Fix-assigned-addresses-for-every-P.patch b/pkgs/patches-linux-5.15/0059-ARM-dts-armada-xp-Fix-assigned-addresses-for-every-P.patch
new file mode 100644
index 0000000..018ed65
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0059-ARM-dts-armada-xp-Fix-assigned-addresses-for-every-P.patch
@@ -0,0 +1,141 @@
+From f0c729c073b7436ae8bcdd1dfb68d45787e8ae6d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 18 Aug 2022 00:01:47 +0200
+Subject: [PATCH 59/90] ARM: dts: armada-xp: Fix assigned-addresses for every
+ PCIe Root Port
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port
+(PCI-to-PCI bridge) should match BDF in address part in that DT node name
+as specified resource belongs to Marvell PCIe Root Port itself.
+
+Fixes: 9d8f44f02d4a ("arm: mvebu: add PCIe Device Tree informations for Armada XP")
+Fixes: 12b69a599745 ("ARM: mvebu: second PCIe unit of Armada XP mv78230 is only x1 capable")
+Fixes: 2163e61c92d9 ("ARM: mvebu: fix second and third PCIe unit of Armada XP mv78260")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-xp-mv78230.dtsi | 8 ++++----
+ arch/arm/boot/dts/armada-xp-mv78260.dtsi | 16 ++++++++--------
+ 2 files changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-xp-mv78230.dtsi b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
+index bf9360f41e0a..5ea9d509cd30 100644
+--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
+@@ -107,7 +107,7 @@ pcie1_intc: interrupt-controller {
+
+ pcie2: pcie@2,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
++ assigned-addresses = <0x82001000 0 0x44000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -135,7 +135,7 @@ pcie2_intc: interrupt-controller {
+
+ pcie3: pcie@3,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x48000 0 0x2000>;
++ assigned-addresses = <0x82001800 0 0x48000 0 0x2000>;
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -163,7 +163,7 @@ pcie3_intc: interrupt-controller {
+
+ pcie4: pcie@4,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x4c000 0 0x2000>;
++ assigned-addresses = <0x82002000 0 0x4c000 0 0x2000>;
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -191,7 +191,7 @@ pcie4_intc: interrupt-controller {
+
+ pcie5: pcie@5,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x80000 0 0x2000>;
++ assigned-addresses = <0x82002800 0 0x80000 0 0x2000>;
+ reg = <0x2800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+diff --git a/arch/arm/boot/dts/armada-xp-mv78260.dtsi b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
+index 0714af52e607..6c6fbb9faf5a 100644
+--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
+@@ -122,7 +122,7 @@ pcie1_intc: interrupt-controller {
+
+ pcie2: pcie@2,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
++ assigned-addresses = <0x82001000 0 0x44000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -150,7 +150,7 @@ pcie2_intc: interrupt-controller {
+
+ pcie3: pcie@3,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x48000 0 0x2000>;
++ assigned-addresses = <0x82001800 0 0x48000 0 0x2000>;
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -178,7 +178,7 @@ pcie3_intc: interrupt-controller {
+
+ pcie4: pcie@4,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x4c000 0 0x2000>;
++ assigned-addresses = <0x82002000 0 0x4c000 0 0x2000>;
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -206,7 +206,7 @@ pcie4_intc: interrupt-controller {
+
+ pcie5: pcie@5,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x80000 0 0x2000>;
++ assigned-addresses = <0x82002800 0 0x80000 0 0x2000>;
+ reg = <0x2800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -234,7 +234,7 @@ pcie5_intc: interrupt-controller {
+
+ pcie6: pcie@6,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x84000 0 0x2000>;
++ assigned-addresses = <0x82003000 0 0x84000 0 0x2000>;
+ reg = <0x3000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -262,7 +262,7 @@ pcie6_intc: interrupt-controller {
+
+ pcie7: pcie@7,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x88000 0 0x2000>;
++ assigned-addresses = <0x82003800 0 0x88000 0 0x2000>;
+ reg = <0x3800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -290,7 +290,7 @@ pcie7_intc: interrupt-controller {
+
+ pcie8: pcie@8,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x8c000 0 0x2000>;
++ assigned-addresses = <0x82004000 0 0x8c000 0 0x2000>;
+ reg = <0x4000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -318,7 +318,7 @@ pcie8_intc: interrupt-controller {
+
+ pcie9: pcie@9,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x42000 0 0x2000>;
++ assigned-addresses = <0x82004800 0 0x42000 0 0x2000>;
+ reg = <0x4800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0060-ARM-dts-armada-375-Fix-assigned-addresses-for-every-.patch b/pkgs/patches-linux-5.15/0060-ARM-dts-armada-375-Fix-assigned-addresses-for-every-.patch
new file mode 100644
index 0000000..d87ae0f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0060-ARM-dts-armada-375-Fix-assigned-addresses-for-every-.patch
@@ -0,0 +1,35 @@
+From 5664d7274ba0c37e52b0aa6c2639866dc2785baf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 18 Aug 2022 00:02:33 +0200
+Subject: [PATCH 60/90] ARM: dts: armada-375: Fix assigned-addresses for every
+ PCIe Root Port
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port
+(PCI-to-PCI bridge) should match BDF in address part in that DT node name
+as specified resource belongs to Marvell PCIe Root Port itself.
+
+Fixes: 4de59085091f ("ARM: mvebu: add Device Tree description of the Armada 375 SoC")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-375.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi
+index 929deaf312a5..c310ef26d1cc 100644
+--- a/arch/arm/boot/dts/armada-375.dtsi
++++ b/arch/arm/boot/dts/armada-375.dtsi
+@@ -592,7 +592,7 @@ pcie0_intc: interrupt-controller {
+
+ pcie1: pcie@2,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
++ assigned-addresses = <0x82001000 0 0x44000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0061-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch b/pkgs/patches-linux-5.15/0061-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch
new file mode 100644
index 0000000..5bdd431
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0061-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch
@@ -0,0 +1,76 @@
+From 31b9db5d6e0fc33b81ea8409c32294731238e5b1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 18 Aug 2022 00:03:45 +0200
+Subject: [PATCH 61/90] ARM: dts: armada-38x: Fix assigned-addresses for every
+ PCIe Root Port
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port
+(PCI-to-PCI bridge) should match BDF in address part in that DT node name
+as specified resource belongs to Marvell PCIe Root Port itself.
+
+Fixes: 0d3d96ab0059 ("ARM: mvebu: add Device Tree description of the Armada 380/385 SoCs")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-380.dtsi | 4 ++--
+ arch/arm/boot/dts/armada-385.dtsi | 6 +++---
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-380.dtsi b/arch/arm/boot/dts/armada-380.dtsi
+index ce1dddb2269b..e94f22b0e9b5 100644
+--- a/arch/arm/boot/dts/armada-380.dtsi
++++ b/arch/arm/boot/dts/armada-380.dtsi
+@@ -89,7 +89,7 @@ pcie1_intc: interrupt-controller {
+ /* x1 port */
+ pcie@2,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
++ assigned-addresses = <0x82001000 0 0x40000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -118,7 +118,7 @@ pcie2_intc: interrupt-controller {
+ /* x1 port */
+ pcie@3,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
++ assigned-addresses = <0x82001800 0 0x44000 0 0x2000>;
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+diff --git a/arch/arm/boot/dts/armada-385.dtsi b/arch/arm/boot/dts/armada-385.dtsi
+index 83392b92dae2..be8d607c59b2 100644
+--- a/arch/arm/boot/dts/armada-385.dtsi
++++ b/arch/arm/boot/dts/armada-385.dtsi
+@@ -93,7 +93,7 @@ pcie1_intc: interrupt-controller {
+ /* x1 port */
+ pcie2: pcie@2,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
++ assigned-addresses = <0x82001000 0 0x40000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -121,7 +121,7 @@ pcie2_intc: interrupt-controller {
+ /* x1 port */
+ pcie3: pcie@3,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
++ assigned-addresses = <0x82001800 0 0x44000 0 0x2000>;
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -152,7 +152,7 @@ pcie3_intc: interrupt-controller {
+ */
+ pcie4: pcie@4,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x48000 0 0x2000>;
++ assigned-addresses = <0x82002000 0 0x48000 0 0x2000>;
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0062-ARM-dts-armada-39x-Fix-assigned-addresses-for-every-.patch b/pkgs/patches-linux-5.15/0062-ARM-dts-armada-39x-Fix-assigned-addresses-for-every-.patch
new file mode 100644
index 0000000..849b2cf
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0062-ARM-dts-armada-39x-Fix-assigned-addresses-for-every-.patch
@@ -0,0 +1,53 @@
+From da049d058681865c4f31e393168e76a911e2d6df Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 18 Aug 2022 00:04:33 +0200
+Subject: [PATCH 62/90] ARM: dts: armada-39x: Fix assigned-addresses for every
+ PCIe Root Port
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port
+(PCI-to-PCI bridge) should match BDF in address part in that DT node name
+as specified resource belongs to Marvell PCIe Root Port itself.
+
+Fixes: 538da83ddbea ("ARM: mvebu: add Device Tree files for Armada 39x SoC and board")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-39x.dtsi | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi
+index 923b035a3ab3..9d1cac49c022 100644
+--- a/arch/arm/boot/dts/armada-39x.dtsi
++++ b/arch/arm/boot/dts/armada-39x.dtsi
+@@ -463,7 +463,7 @@ pcie1_intc: interrupt-controller {
+ /* x1 port */
+ pcie@2,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x40000 0 0x2000>;
++ assigned-addresses = <0x82001000 0 0x40000 0 0x2000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -492,7 +492,7 @@ pcie2_intc: interrupt-controller {
+ /* x1 port */
+ pcie@3,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>;
++ assigned-addresses = <0x82001800 0 0x44000 0 0x2000>;
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+@@ -524,7 +524,7 @@ pcie3_intc: interrupt-controller {
+ */
+ pcie@4,0 {
+ device_type = "pci";
+- assigned-addresses = <0x82000800 0 0x48000 0 0x2000>;
++ assigned-addresses = <0x82002000 0 0x48000 0 0x2000>;
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0063-irqchip-armada-370-xp-Do-not-call-ipi_resume-when-IP.patch b/pkgs/patches-linux-5.15/0063-irqchip-armada-370-xp-Do-not-call-ipi_resume-when-IP.patch
new file mode 100644
index 0000000..867e6dd
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0063-irqchip-armada-370-xp-Do-not-call-ipi_resume-when-IP.patch
@@ -0,0 +1,49 @@
+From 748cb95b78593cba8dd0abbbb7d466322c46bde2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 11 Aug 2022 10:57:05 +0200
+Subject: [PATCH 63/90] irqchip/armada-370-xp: Do not call ipi_resume() when
+ IPI is not used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When IPI is not used then ipi_resume() does nothing. IPI is used only on
+systems where mpic controller does not parent GIC IRQ (e.g. on Armada XP).
+For documentation purpose add condition when ipi_resume() should be called.
+This simplify understanding of irq-armada-370-xp.c driver when individual
+driver functions are called when they do something. No functional change.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/irqchip/irq-armada-370-xp.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
+index 3fa6bd70684b..11b6ce81d17e 100644
+--- a/drivers/irqchip/irq-armada-370-xp.c
++++ b/drivers/irqchip/irq-armada-370-xp.c
+@@ -509,7 +509,9 @@ static void armada_xp_mpic_reenable_percpu(void)
+ armada_370_xp_irq_unmask(data);
+ }
+
+- ipi_resume();
++ /* IPI is used only when we do not have parent irq */
++ if (parent_irq <= 0)
++ ipi_resume();
+ }
+
+ static int armada_xp_mpic_starting_cpu(unsigned int cpu)
+@@ -735,7 +737,9 @@ static void armada_370_xp_mpic_resume(void)
+ if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK)
+ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+
+- ipi_resume();
++ /* IPI is used only when we do not have parent irq */
++ if (parent_irq <= 0)
++ ipi_resume();
+ }
+
+ static struct syscore_ops armada_370_xp_mpic_syscore_ops = {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0064-irqchip-armada-370-xp-Fix-comment-about-unmasking-mp.patch b/pkgs/patches-linux-5.15/0064-irqchip-armada-370-xp-Fix-comment-about-unmasking-mp.patch
new file mode 100644
index 0000000..e50699b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0064-irqchip-armada-370-xp-Fix-comment-about-unmasking-mp.patch
@@ -0,0 +1,35 @@
+From e5a5d4fd63c10ff98847e9c1cfa3c6161f4b69e8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 11 Aug 2022 11:15:56 +0200
+Subject: [PATCH 64/90] irqchip/armada-370-xp: Fix comment about unmasking mpic
+ source 1
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+mpic source 1 unmasks MSI interrupts, not IPI interrupt (IPI has source 0).
+Function armada_370_xp_msi_init() is used for initializing MSI interrupts,
+so code is correct (it should enable MSI interrupts; not IPI), just comment
+was wrong.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/irqchip/irq-armada-370-xp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
+index 11b6ce81d17e..9b28e6269176 100644
+--- a/drivers/irqchip/irq-armada-370-xp.c
++++ b/drivers/irqchip/irq-armada-370-xp.c
+@@ -293,7 +293,7 @@ static int armada_370_xp_msi_init(struct device_node *node,
+ writel(reg, per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+
+- /* Unmask IPI interrupt */
++ /* Unmask MSI interrupt */
+ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+
+ return 0;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0065-irqchip-armada-370-xp-Do-not-touch-IPI-registers-on-.patch b/pkgs/patches-linux-5.15/0065-irqchip-armada-370-xp-Do-not-touch-IPI-registers-on-.patch
new file mode 100644
index 0000000..038ce8e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0065-irqchip-armada-370-xp-Do-not-touch-IPI-registers-on-.patch
@@ -0,0 +1,67 @@
+From bfe14db697196f06c0a7abe7f0d44edac04f0c11 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 11 Aug 2022 11:35:53 +0200
+Subject: [PATCH 65/90] irqchip/armada-370-xp: Do not touch IPI registers on
+ platforms without IPI
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+IPI is used only when we do not have parent irq. On platforms with parent
+irq are those IPI registers used for additional set of MSI interrupts
+(which are currently unused). So do not touch these registers when IPI is
+not used.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/irqchip/irq-armada-370-xp.c | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
+index 9b28e6269176..397bacf638a2 100644
+--- a/drivers/irqchip/irq-armada-370-xp.c
++++ b/drivers/irqchip/irq-armada-370-xp.c
+@@ -478,6 +478,10 @@ static void armada_xp_mpic_smp_cpu_init(void)
+ for (i = 0; i < nr_irqs; i++)
+ writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS);
+
++ /* IPI is not used when we do have parent irq */
++ if (parent_irq > 0)
++ return;
++
+ /* Disable all IPIs */
+ writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+
+@@ -732,7 +736,8 @@ static void armada_370_xp_mpic_resume(void)
+ /* Reconfigure doorbells for IPIs and MSIs */
+ writel(doorbell_mask_reg,
+ per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+- if (doorbell_mask_reg & IPI_DOORBELL_MASK)
++ /* IPI is used only when we do not have parent irq */
++ if (parent_irq <= 0 && (doorbell_mask_reg & IPI_DOORBELL_MASK))
+ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+ if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK)
+ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+@@ -784,13 +789,18 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
+ BUG_ON(!armada_370_xp_mpic_domain);
+ irq_domain_update_bus_token(armada_370_xp_mpic_domain, DOMAIN_BUS_WIRED);
+
++ /*
++ * parent_irq is used for distinguish between IPI and non-IPI platforms.
++ * So initialize it before calling any other driver functions.
++ */
++ parent_irq = irq_of_parse_and_map(node, 0);
++
+ /* Setup for the boot CPU */
+ armada_xp_mpic_perf_init();
+ armada_xp_mpic_smp_cpu_init();
+
+ armada_370_xp_msi_init(node, main_int_res.start);
+
+- parent_irq = irq_of_parse_and_map(node, 0);
+ if (parent_irq <= 0) {
+ irq_set_default_host(armada_370_xp_mpic_domain);
+ set_handle_irq(armada_370_xp_handle_irq);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0066-irqchip-armada-370-xp-Add-support-for-32-MSI-interru.patch b/pkgs/patches-linux-5.15/0066-irqchip-armada-370-xp-Add-support-for-32-MSI-interru.patch
new file mode 100644
index 0000000..0eefb9a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0066-irqchip-armada-370-xp-Add-support-for-32-MSI-interru.patch
@@ -0,0 +1,181 @@
+From adf4ef16f1869c4f8a35bfb95ce95e0d418f72b9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 11 Aug 2022 13:16:26 +0200
+Subject: [PATCH 66/90] irqchip/armada-370-xp: Add support for 32 MSI
+ interrupts on non-IPI platforms
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently only upper 16 MSI interrupts on non-IPI platforms are used.
+Low 16 MSI interrupts on non-IPI platforms are mapped into IPI registers.
+Implement support also for low 16 MSI interrupts which allow increase
+number of MSI interrupts on non-IPI platforms from 16 to 32.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/irqchip/irq-armada-370-xp.c | 54 +++++++++++++++++++++--------
+ 1 file changed, 40 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
+index 397bacf638a2..8abc70ed30c1 100644
+--- a/drivers/irqchip/irq-armada-370-xp.c
++++ b/drivers/irqchip/irq-armada-370-xp.c
+@@ -135,6 +135,7 @@
+
+ #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
+
++/* IPI and MSI interrupt definitions for IPI platforms */
+ #define IPI_DOORBELL_START (0)
+ #define IPI_DOORBELL_END (8)
+ #define IPI_DOORBELL_MASK 0xFF
+@@ -143,6 +144,12 @@
+ #define PCI_MSI_DOORBELL_END (32)
+ #define PCI_MSI_DOORBELL_MASK 0xFFFF0000
+
++/* MSI interrupt definitions for non-IPI platforms */
++#define PCI_MSI_FULL_DOORBELL_START (0)
++#define PCI_MSI_FULL_DOORBELL_NR (32)
++#define PCI_MSI_FULL_DOORBELL_END (32)
++#define PCI_MSI_FULL_DOORBELL_MASK (0xFFFFFFFF)
++
+ static void __iomem *per_cpu_int_base;
+ static void __iomem *main_int_base;
+ static struct irq_domain *armada_370_xp_mpic_domain;
+@@ -151,7 +158,7 @@ static int parent_irq;
+ #ifdef CONFIG_PCI_MSI
+ static struct irq_domain *armada_370_xp_msi_domain;
+ static struct irq_domain *armada_370_xp_msi_inner_domain;
+-static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
++static DECLARE_BITMAP(msi_used, PCI_MSI_FULL_DOORBELL_NR);
+ static DEFINE_MUTEX(msi_used_lock);
+ static phys_addr_t msi_doorbell_addr;
+ #endif
+@@ -209,9 +216,10 @@ static struct msi_domain_info armada_370_xp_msi_domain_info = {
+
+ static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+ {
++ u32 msi_start = (parent_irq <= 0) ? PCI_MSI_DOORBELL_START : PCI_MSI_FULL_DOORBELL_START;
+ msg->address_lo = lower_32_bits(msi_doorbell_addr);
+ msg->address_hi = upper_32_bits(msi_doorbell_addr);
+- msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START);
++ msg->data = 0xf00 | (data->hwirq + msi_start);
+ }
+
+ static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data,
+@@ -229,10 +237,11 @@ static struct irq_chip armada_370_xp_msi_bottom_irq_chip = {
+ static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *args)
+ {
++ unsigned int msi_nr = (parent_irq <= 0) ? PCI_MSI_DOORBELL_NR : PCI_MSI_FULL_DOORBELL_NR;
+ int hwirq, i;
+
+ mutex_lock(&msi_used_lock);
+- hwirq = bitmap_find_free_region(msi_used, PCI_MSI_DOORBELL_NR,
++ hwirq = bitmap_find_free_region(msi_used, msi_nr,
+ order_base_2(nr_irqs));
+ mutex_unlock(&msi_used_lock);
+
+@@ -267,13 +276,15 @@ static const struct irq_domain_ops armada_370_xp_msi_domain_ops = {
+ static int armada_370_xp_msi_init(struct device_node *node,
+ phys_addr_t main_int_phys_base)
+ {
++ unsigned int msi_nr = (parent_irq <= 0) ? PCI_MSI_DOORBELL_NR : PCI_MSI_FULL_DOORBELL_NR;
++ u32 msi_mask = (parent_irq <= 0) ? PCI_MSI_DOORBELL_MASK: PCI_MSI_FULL_DOORBELL_MASK;
+ u32 reg;
+
+ msi_doorbell_addr = main_int_phys_base +
+ ARMADA_370_XP_SW_TRIG_INT_OFFS;
+
+ armada_370_xp_msi_inner_domain =
+- irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
++ irq_domain_add_linear(NULL, msi_nr,
+ &armada_370_xp_msi_domain_ops, NULL);
+ if (!armada_370_xp_msi_inner_domain)
+ return -ENOMEM;
+@@ -288,7 +299,7 @@ static int armada_370_xp_msi_init(struct device_node *node,
+ }
+
+ reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
+- | PCI_MSI_DOORBELL_MASK;
++ | msi_mask;
+
+ writel(reg, per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+@@ -296,6 +307,10 @@ static int armada_370_xp_msi_init(struct device_node *node,
+ /* Unmask MSI interrupt */
+ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+
++ /* Unmask low 16 MSI irqs on non-IPI platforms */
++ if (parent_irq > 0)
++ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
++
+ return 0;
+ }
+ #else
+@@ -586,23 +601,25 @@ static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
+ #ifdef CONFIG_PCI_MSI
+ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
+ {
++ u32 msi_start = (parent_irq <= 0) ? PCI_MSI_DOORBELL_START : PCI_MSI_FULL_DOORBELL_START;
++ u32 msi_end = (parent_irq <= 0) ? PCI_MSI_DOORBELL_END : PCI_MSI_FULL_DOORBELL_END;
++ u32 msi_mask = (parent_irq <= 0) ? PCI_MSI_DOORBELL_MASK: PCI_MSI_FULL_DOORBELL_MASK;
+ u32 msimask, msinr;
+
+ msimask = readl_relaxed(per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
+- & PCI_MSI_DOORBELL_MASK;
++ & msi_mask;
+
+ writel(~msimask, per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
+
+- for (msinr = PCI_MSI_DOORBELL_START;
+- msinr < PCI_MSI_DOORBELL_END; msinr++) {
++ for (msinr = msi_start; msinr < msi_end; msinr++) {
+ unsigned int irq;
+
+ if (!(msimask & BIT(msinr)))
+ continue;
+
+- irq = msinr - PCI_MSI_DOORBELL_START;
++ irq = msinr - msi_start;
+
+ if (is_chained)
+ generic_handle_domain_irq(armada_370_xp_msi_inner_domain,
+@@ -636,7 +653,7 @@ static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc)
+ if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid)))
+ continue;
+
+- if (irqn == 1) {
++ if (irqn == 0 || irqn == 1) {
+ armada_370_xp_handle_msi_irq(NULL, true);
+ continue;
+ }
+@@ -737,10 +754,19 @@ static void armada_370_xp_mpic_resume(void)
+ writel(doorbell_mask_reg,
+ per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+ /* IPI is used only when we do not have parent irq */
+- if (parent_irq <= 0 && (doorbell_mask_reg & IPI_DOORBELL_MASK))
+- writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+- if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK)
+- writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
++ if (parent_irq <= 0) {
++ /* On IPI platforms is source 0 used for IPI and source 1 for MSI */
++ if (doorbell_mask_reg & IPI_DOORBELL_MASK)
++ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
++ if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK)
++ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
++ } else {
++ /* On non-IPI platforms is source 0 used for MSI 0-15 and 1 for MSI 16-31 */
++ if (doorbell_mask_reg & GENMASK(15, 0))
++ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
++ if (doorbell_mask_reg & GENMASK(31, 16))
++ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
++ }
+
+ /* IPI is used only when we do not have parent irq */
+ if (parent_irq <= 0)
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0067-dt-bindings-PCI-mvebu-Update-information-about-error.patch b/pkgs/patches-linux-5.15/0067-dt-bindings-PCI-mvebu-Update-information-about-error.patch
new file mode 100644
index 0000000..c0e08dd
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0067-dt-bindings-PCI-mvebu-Update-information-about-error.patch
@@ -0,0 +1,32 @@
+From 71d68bf335708fe14724bd520d72afcd71ee56dd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 2 Nov 2021 11:06:18 +0100
+Subject: [PATCH 67/90] dt-bindings: PCI: mvebu: Update information about error
+ interrupt
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+mvebu error interrupt is triggered by any non-intx event, which is mainly
+some pcie error.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ Documentation/devicetree/bindings/pci/mvebu-pci.txt | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt
+index 6d022a9d36ee..8f0bca42113f 100644
+--- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt
++++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt
+@@ -83,6 +83,7 @@ and the following optional properties:
+ specified will default to 100ms, as required by the PCIe specification.
+ - interrupt-names: list of interrupt names, supported are:
+ - "intx" - interrupt line triggered by one of the legacy interrupt
++ - "error" - interrupt line triggered by any other event (non-intx)
+ - interrupts or interrupts-extended: List of the interrupt sources which
+ corresponding to the "interrupt-names". If non-empty then also additional
+ 'interrupt-controller' subnode must be defined.
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0068-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch b/pkgs/patches-linux-5.15/0068-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch
new file mode 100644
index 0000000..a26cd8a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0068-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch
@@ -0,0 +1,437 @@
+From 580e9959e0110a244ffec06c4007921737955748 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 14:40:17 +0200
+Subject: [PATCH 68/90] PCI: mvebu: Implement support for interrupts on
+ emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This adds support for PME and ERR interrupts reported by emulated bridge
+(for PME and AER kernel drivers) via new Root Port irq chip as these
+interrupts from PCIe Root Ports are handled by mvebu hardware completely
+separately from INTx and MSI interrupts send by real PCIe devices.
+
+With this change, kernel PME and AER drivers start working as they can
+acquire required interrupt lines (provided by mvebu rp virtual irq chip).
+
+Note that for this support, device tree files has to be properly adjusted
+to provide "interrupts" or "interrupts-extended" property with error
+interrupt source and "interrupt-names" property with "error" string.
+
+If device tree files do not provide these properties then driver would work
+as before and would not provide interrupts on emulated bridge, like before.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 256 ++++++++++++++++++++++++++---
+ 1 file changed, 237 insertions(+), 19 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index c5e5bc085a9d..319cd2b98545 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -56,8 +56,16 @@
+ #define PCIE_CONF_DATA_OFF 0x18fc
+ #define PCIE_INT_CAUSE_OFF 0x1900
+ #define PCIE_INT_UNMASK_OFF 0x1910
++#define PCIE_INT_DET_COR BIT(8)
++#define PCIE_INT_DET_NONFATAL BIT(9)
++#define PCIE_INT_DET_FATAL BIT(10)
++#define PCIE_INT_ERR_FATAL BIT(16)
++#define PCIE_INT_ERR_NONFATAL BIT(17)
++#define PCIE_INT_ERR_COR BIT(18)
+ #define PCIE_INT_INTX(i) BIT(24+i)
+ #define PCIE_INT_PM_PME BIT(28)
++#define PCIE_INT_DET_MASK (PCIE_INT_DET_COR | PCIE_INT_DET_NONFATAL | PCIE_INT_DET_FATAL)
++#define PCIE_INT_ERR_MASK (PCIE_INT_ERR_FATAL | PCIE_INT_ERR_NONFATAL | PCIE_INT_ERR_COR)
+ #define PCIE_INT_ALL_MASK GENMASK(31, 0)
+ #define PCIE_CTRL_OFF 0x1a00
+ #define PCIE_CTRL_X1_MODE 0x0001
+@@ -120,9 +128,12 @@ struct mvebu_pcie_port {
+ struct resource regs;
+ u8 slot_power_limit_value;
+ u8 slot_power_limit_scale;
++ struct irq_domain *rp_irq_domain;
+ struct irq_domain *intx_irq_domain;
+ raw_spinlock_t irq_lock;
++ int error_irq;
+ int intx_irq;
++ bool pme_pending;
+ };
+
+ static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg)
+@@ -321,9 +332,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ /* Clear all interrupt causes. */
+ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF);
+
+- /* Check if "intx" interrupt was specified in DT. */
+- if (port->intx_irq > 0)
+- return;
++ /*
++ * Unmask all error interrupts which are internally generated.
++ * They cannot be disabled by SERR# Enable bit in PCI Command register,
++ * see Figure 6-3: Pseudo Logic Diagram for Error Message Controls in
++ * PCIe base specification.
++ * Internally generated mvebu interrupts are reported via mvebu summary
++ * interrupt which requires "error" interrupt to be specified in DT.
++ */
++ if (port->error_irq > 0) {
++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ unmask |= PCIE_INT_DET_MASK;
++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
++ }
+
+ /*
+ * Fallback code when "intx" interrupt was not specified in DT:
+@@ -335,10 +356,12 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ * performance penalty as every PCIe interrupt handler needs to be
+ * called when some interrupt is triggered.
+ */
+- unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
+- unmask |= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) |
+- PCIE_INT_INTX(2) | PCIE_INT_INTX(3);
+- mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
++ if (port->intx_irq <= 0) {
++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ unmask |= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) |
++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3);
++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
++ }
+ }
+
+ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
+@@ -603,11 +626,16 @@ mvebu_pci_bridge_emul_base_conf_read(struct pci_bridge_emul *bridge,
+ case PCI_INTERRUPT_LINE: {
+ /*
+ * From the whole 32bit register we support reading from HW only
+- * one bit: PCI_BRIDGE_CTL_BUS_RESET.
++ * two bits: PCI_BRIDGE_CTL_BUS_RESET and PCI_BRIDGE_CTL_SERR.
+ * Other bits are retrieved only from emulated config buffer.
+ */
+ __le32 *cfgspace = (__le32 *)&bridge->conf;
+ u32 val = le32_to_cpu(cfgspace[PCI_INTERRUPT_LINE / 4]);
++ if ((mvebu_readl(port, PCIE_INT_UNMASK_OFF) &
++ PCIE_INT_ERR_MASK) == PCIE_INT_ERR_MASK)
++ val |= PCI_BRIDGE_CTL_SERR << 16;
++ else
++ val &= ~(PCI_BRIDGE_CTL_SERR << 16);
+ if (mvebu_readl(port, PCIE_CTRL_OFF) & PCIE_CTRL_MASTER_HOT_RESET)
+ val |= PCI_BRIDGE_CTL_BUS_RESET << 16;
+ else
+@@ -675,6 +703,11 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+ break;
+ }
+
++ case PCI_EXP_RTCTL:
++ *value = (mvebu_readl(port, PCIE_INT_UNMASK_OFF) &
++ PCIE_INT_PM_PME) ? PCI_EXP_RTCTL_PMEIE : 0;
++ break;
++
+ case PCI_EXP_RTSTA:
+ *value = mvebu_readl(port, PCIE_RC_RTSTA);
+ break;
+@@ -780,6 +813,14 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
+ break;
+
+ case PCI_INTERRUPT_LINE:
++ if ((mask & (PCI_BRIDGE_CTL_SERR << 16)) && port->error_irq > 0) {
++ u32 unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ if (new & (PCI_BRIDGE_CTL_SERR << 16))
++ unmask |= PCIE_INT_ERR_MASK;
++ else
++ unmask &= ~PCIE_INT_ERR_MASK;
++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
++ }
+ if (mask & (PCI_BRIDGE_CTL_BUS_RESET << 16)) {
+ u32 ctrl = mvebu_readl(port, PCIE_CTRL_OFF);
+ if (new & (PCI_BRIDGE_CTL_BUS_RESET << 16))
+@@ -838,10 +879,25 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ * PME Status bit in Root Status Register (PCIE_RC_RTSTA)
+ * is read-only and can be cleared only by writing 0b to the
+ * Interrupt Cause RW0C register (PCIE_INT_CAUSE_OFF). So
+- * clear PME via Interrupt Cause.
++ * clear PME via Interrupt Cause and also set port->pme_pending
++ * variable to false value to start processing PME interrupts
++ * in interrupt handler again.
+ */
+- if (new & PCI_EXP_RTSTA_PME)
++ if (new & PCI_EXP_RTSTA_PME) {
+ mvebu_writel(port, ~PCIE_INT_PM_PME, PCIE_INT_CAUSE_OFF);
++ port->pme_pending = false;
++ }
++ break;
++
++ case PCI_EXP_RTCTL:
++ if ((mask & PCI_EXP_RTCTL_PMEIE) && port->error_irq > 0) {
++ u32 unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ if (new & PCI_EXP_RTCTL_PMEIE)
++ unmask |= PCIE_INT_PM_PME;
++ else
++ unmask &= ~PCIE_INT_PM_PME;
++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
++ }
+ break;
+
+ case PCI_EXP_DEVCTL2:
+@@ -924,6 +980,14 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ bridge_flags |= PCI_BRIDGE_EMUL_NO_IO_FORWARD;
+ }
+
++ /*
++ * Interrupts on emulated bridge are supported only when "error"
++ * interrupt was specified in DT. Without it emulated bridge cannot
++ * emulate interrupts.
++ */
++ if (port->error_irq > 0)
++ bridge->conf.intpin = PCI_INTERRUPT_INTA;
++
+ /*
+ * Older mvebu hardware provides PCIe Capability structure only in
+ * version 1. New hardware provides it in version 2.
+@@ -1072,6 +1136,26 @@ static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ };
+
++static struct irq_chip rp_irq_chip = {
++ .name = "mvebu-rp",
++};
++
++static int mvebu_pcie_rp_irq_map(struct irq_domain *h,
++ unsigned int virq, irq_hw_number_t hwirq)
++{
++ struct mvebu_pcie_port *port = h->host_data;
++
++ irq_set_chip_and_handler(virq, &rp_irq_chip, handle_simple_irq);
++ irq_set_chip_data(virq, port);
++
++ return 0;
++}
++
++static const struct irq_domain_ops mvebu_pcie_rp_irq_domain_ops = {
++ .map = mvebu_pcie_rp_irq_map,
++ .xlate = irq_domain_xlate_onecell,
++};
++
+ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port)
+ {
+ struct device *dev = &port->pcie->pdev->dev;
+@@ -1094,10 +1178,72 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port)
+ return -ENOMEM;
+ }
+
++ /*
++ * When "error" interrupt was not specified in DT then there is no
++ * support for interrupts on emulated root bridge. So skip following
++ * initialization.
++ */
++ if (port->error_irq <= 0)
++ return 0;
++
++ port->rp_irq_domain = irq_domain_add_linear(NULL, 1,
++ &mvebu_pcie_rp_irq_domain_ops,
++ port);
++ if (!port->rp_irq_domain) {
++ irq_domain_remove(port->intx_irq_domain);
++ dev_err(dev, "Failed to add Root Port IRQ domain for %s\n", port->name);
++ return -ENOMEM;
++ }
++
+ return 0;
+ }
+
+-static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg)
++static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg)
++{
++ struct mvebu_pcie_port *port = arg;
++ struct device *dev = &port->pcie->pdev->dev;
++ u32 cause, unmask, status;
++
++ cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF);
++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ status = cause & unmask;
++
++ /* "error" interrupt handler does not process INTX interrupts */
++ status &= ~(PCIE_INT_INTX(0) | PCIE_INT_INTX(1) |
++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3));
++
++ /* Process PME interrupt */
++ if ((status & PCIE_INT_PM_PME) && !port->pme_pending) {
++ /*
++ * Do not clear PME interrupt bit in Cause Register as it
++ * invalidates also content of Root Status Register. Instead
++ * set port->pme_pending variable to true to indicate that
++ * next time PME interrupt should be ignored until variable
++ * is back to the false value.
++ */
++ port->pme_pending = true;
++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL)
++ dev_err_ratelimited(dev, "unhandled PME IRQ\n");
++ }
++
++ /* Process ERR interrupt */
++ if (status & PCIE_INT_ERR_MASK) {
++ mvebu_writel(port, ~PCIE_INT_ERR_MASK, PCIE_INT_CAUSE_OFF);
++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL)
++ dev_err_ratelimited(dev, "unhandled ERR IRQ\n");
++ }
++
++ /* Process local ERR interrupt */
++ if (status & PCIE_INT_DET_MASK) {
++ mvebu_writel(port, ~PCIE_INT_DET_MASK, PCIE_INT_CAUSE_OFF);
++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL)
++ dev_err_ratelimited(dev, "unhandled ERR IRQ\n");
++ }
++
++ return status ? IRQ_HANDLED : IRQ_NONE;
++}
++
++static irqreturn_t mvebu_pcie_intx_irq_handler(int irq, void *arg)
+ {
+ struct mvebu_pcie_port *port = arg;
+ struct device *dev = &port->pcie->pdev->dev;
+@@ -1108,6 +1254,10 @@ static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg)
+ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
+ status = cause & unmask;
+
++ /* "intx" interrupt handler process only INTX interrupts */
++ status &= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) |
++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3);
++
+ /* Process legacy INTx interrupts */
+ for (i = 0; i < PCI_NUM_INTX; i++) {
+ if (!(status & PCIE_INT_INTX(i)))
+@@ -1122,9 +1272,29 @@ static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg)
+
+ static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+ {
+- /* Interrupt support on mvebu emulated bridges is not implemented yet */
+- if (dev->bus->number == 0)
+- return 0; /* Proper return code 0 == NO_IRQ */
++ struct mvebu_pcie_port *port;
++ struct mvebu_pcie *pcie;
++
++ if (dev->bus->number == 0) {
++ /*
++ * Each emulated root bridge for every mvebu port has its own
++ * Root Port irq chip and irq domain. Argument pin is the INTx
++ * pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) and hwirq for function
++ * irq_create_mapping() is indexed from zero.
++ */
++ pcie = dev->bus->sysdata;
++ port = mvebu_pcie_find_port(pcie, dev->bus, PCI_DEVFN(slot, 0));
++ if (!port)
++ return 0; /* Proper return code 0 == NO_IRQ */
++ /*
++ * port->rp_irq_domain is available only when "error" interrupt
++ * was specified in DT. When is not available then interrupts
++ * for emulated root bridge are not provided.
++ */
++ if (port->error_irq <= 0)
++ return 0; /* Proper return code 0 == NO_IRQ */
++ return irq_create_mapping(port->rp_irq_domain, pin - 1);
++ }
+
+ return of_irq_parse_and_map_pci(dev, slot, pin);
+ }
+@@ -1333,6 +1503,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ port->name, child);
+ }
+
++ /*
++ * Old DT bindings do not contain "error" interrupt
++ * so do not fail probing driver when interrupt does not exist.
++ */
++ port->error_irq = of_irq_get_byname(child, "error");
++ if (port->error_irq == -EPROBE_DEFER) {
++ ret = port->error_irq;
++ goto err;
++ }
++ if (port->error_irq <= 0) {
++ dev_warn(dev, "%s: interrupts on Root Port are unsupported, "
++ "%pOF does not contain error interrupt\n",
++ port->name, child);
++ }
++
+ reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags);
+ if (reset_gpio == -EPROBE_DEFER) {
+ ret = reset_gpio;
+@@ -1538,7 +1723,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+
+ for (i = 0; i < pcie->nports; i++) {
+ struct mvebu_pcie_port *port = &pcie->ports[i];
+- int irq = port->intx_irq;
+
+ child = port->dn;
+ if (!child)
+@@ -1566,7 +1750,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+ continue;
+ }
+
+- if (irq > 0) {
++ if (port->error_irq > 0 || port->intx_irq > 0) {
+ ret = mvebu_pcie_init_irq_domain(port);
+ if (ret) {
+ dev_err(dev, "%s: cannot init irq domain\n",
+@@ -1577,14 +1761,42 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+ mvebu_pcie_powerdown(port);
+ continue;
+ }
++ }
++
++ if (port->error_irq > 0) {
++ ret = devm_request_irq(dev, port->error_irq,
++ mvebu_pcie_error_irq_handler,
++ IRQF_SHARED | IRQF_NO_THREAD,
++ port->name, port);
++ if (ret) {
++ dev_err(dev, "%s: cannot register error interrupt handler: %d\n",
++ port->name, ret);
++ if (port->intx_irq_domain)
++ irq_domain_remove(port->intx_irq_domain);
++ if (port->rp_irq_domain)
++ irq_domain_remove(port->rp_irq_domain);
++ pci_bridge_emul_cleanup(&port->bridge);
++ devm_iounmap(dev, port->base);
++ port->base = NULL;
++ mvebu_pcie_powerdown(port);
++ continue;
++ }
++ }
+
+- ret = devm_request_irq(dev, irq, mvebu_pcie_irq_handler,
++ if (port->intx_irq > 0) {
++ ret = devm_request_irq(dev, port->intx_irq,
++ mvebu_pcie_intx_irq_handler,
+ IRQF_SHARED | IRQF_NO_THREAD,
+ port->name, port);
+ if (ret) {
+- dev_err(dev, "%s: cannot register interrupt handler: %d\n",
++ dev_err(dev, "%s: cannot register intx interrupt handler: %d\n",
+ port->name, ret);
+- irq_domain_remove(port->intx_irq_domain);
++ if (port->error_irq > 0)
++ devm_free_irq(dev, port->error_irq, port);
++ if (port->intx_irq_domain)
++ irq_domain_remove(port->intx_irq_domain);
++ if (port->rp_irq_domain)
++ irq_domain_remove(port->rp_irq_domain);
+ pci_bridge_emul_cleanup(&port->bridge);
+ devm_iounmap(dev, port->base);
+ port->base = NULL;
+@@ -1722,6 +1934,12 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+ }
+ irq_domain_remove(port->intx_irq_domain);
+ }
++ if (port->rp_irq_domain) {
++ int virq = irq_find_mapping(port->rp_irq_domain, 0);
++ if (virq > 0)
++ irq_dispose_mapping(virq);
++ irq_domain_remove(port->rp_irq_domain);
++ }
+
+ /* Free config space for emulated root bridge. */
+ pci_bridge_emul_cleanup(&port->bridge);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0069-ARM-dts-kirkwood-Add-definitions-for-PCIe-error-inte.patch b/pkgs/patches-linux-5.15/0069-ARM-dts-kirkwood-Add-definitions-for-PCIe-error-inte.patch
new file mode 100644
index 0000000..e1b97f9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0069-ARM-dts-kirkwood-Add-definitions-for-PCIe-error-inte.patch
@@ -0,0 +1,94 @@
+From 1e7448b4d20311d861dd8e7e9cfa60fea643b001 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 20:15:56 +0200
+Subject: [PATCH 69/90] ARM: dts: kirkwood: Add definitions for PCIe error
+ interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+First PCIe controller on Kirkwood SoC reports error interrupt via IRQ 44
+and second PCIe controller via IRQ 45.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/kirkwood-6192.dtsi | 4 ++--
+ arch/arm/boot/dts/kirkwood-6281.dtsi | 4 ++--
+ arch/arm/boot/dts/kirkwood-6282.dtsi | 8 ++++----
+ arch/arm/boot/dts/kirkwood-98dx4122.dtsi | 4 ++--
+ 4 files changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/arch/arm/boot/dts/kirkwood-6192.dtsi b/arch/arm/boot/dts/kirkwood-6192.dtsi
+index 07f4f7f98c0c..705c0d7effed 100644
+--- a/arch/arm/boot/dts/kirkwood-6192.dtsi
++++ b/arch/arm/boot/dts/kirkwood-6192.dtsi
+@@ -26,8 +26,8 @@ pcie0: pcie@1,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-names = "intx";
+- interrupts = <9>;
++ interrupt-names = "intx", "error";
++ interrupts = <9>, <44>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie_intc 0>,
+ <0 0 0 2 &pcie_intc 1>,
+diff --git a/arch/arm/boot/dts/kirkwood-6281.dtsi b/arch/arm/boot/dts/kirkwood-6281.dtsi
+index d08a9a5ecc26..8e311165fd13 100644
+--- a/arch/arm/boot/dts/kirkwood-6281.dtsi
++++ b/arch/arm/boot/dts/kirkwood-6281.dtsi
+@@ -26,8 +26,8 @@ pcie0: pcie@1,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-names = "intx";
+- interrupts = <9>;
++ interrupt-names = "intx", "error";
++ interrupts = <9>, <44>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie_intc 0>,
+ <0 0 0 2 &pcie_intc 1>,
+diff --git a/arch/arm/boot/dts/kirkwood-6282.dtsi b/arch/arm/boot/dts/kirkwood-6282.dtsi
+index 2eea5b304f47..e33723160ce7 100644
+--- a/arch/arm/boot/dts/kirkwood-6282.dtsi
++++ b/arch/arm/boot/dts/kirkwood-6282.dtsi
+@@ -30,8 +30,8 @@ pcie0: pcie@1,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-names = "intx";
+- interrupts = <9>;
++ interrupt-names = "intx", "error";
++ interrupts = <9>, <44>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie0_intc 0>,
+ <0 0 0 2 &pcie0_intc 1>,
+@@ -58,8 +58,8 @@ pcie1: pcie@2,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-names = "intx";
+- interrupts = <10>;
++ interrupt-names = "intx", "error";
++ interrupts = <10>, <45>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
+ <0 0 0 2 &pcie1_intc 1>,
+diff --git a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi
+index 070bc13242b8..c3469a2fc58a 100644
+--- a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi
++++ b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi
+@@ -26,8 +26,8 @@ pcie0: pcie@1,0 {
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+ bus-range = <0x00 0xff>;
+- interrupt-names = "intx";
+- interrupts = <9>;
++ interrupt-names = "intx", "error";
++ interrupts = <9>, <44>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie_intc 0>,
+ <0 0 0 2 &pcie_intc 1>,
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0070-ARM-dts-dove-Add-definitions-for-PCIe-error-interrup.patch b/pkgs/patches-linux-5.15/0070-ARM-dts-dove-Add-definitions-for-PCIe-error-interrup.patch
new file mode 100644
index 0000000..194d64e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0070-ARM-dts-dove-Add-definitions-for-PCIe-error-interrup.patch
@@ -0,0 +1,46 @@
+From db1cf8c55bbea9735a91e550bcea4b2d9abf1d39 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 20:18:11 +0200
+Subject: [PATCH 70/90] ARM: dts: dove: Add definitions for PCIe error
+ interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+First PCIe controller on Dove SoC reports error interrupt via IRQ 15
+and second PCIe controller via IRQ 17.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/dove.dtsi | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi
+index 70d45d2b1258..9aee3cfd3e98 100644
+--- a/arch/arm/boot/dts/dove.dtsi
++++ b/arch/arm/boot/dts/dove.dtsi
+@@ -122,8 +122,8 @@ pcie0: pcie@1 {
+ bus-range = <0x00 0xff>;
+
+ #interrupt-cells = <1>;
+- interrupt-names = "intx";
+- interrupts = <16>;
++ interrupt-names = "intx", "error";
++ interrupts = <16>, <15>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie0_intc 0>,
+ <0 0 0 2 &pcie0_intc 1>,
+@@ -151,8 +151,8 @@ pcie1: pcie@2 {
+ bus-range = <0x00 0xff>;
+
+ #interrupt-cells = <1>;
+- interrupt-names = "intx";
+- interrupts = <18>;
++ interrupt-names = "intx", "error";
++ interrupts = <18>, <17>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie1_intc 0>,
+ <0 0 0 2 &pcie1_intc 1>,
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0071-dt-bindings-irqchip-armada-370-xp-Update-information.patch b/pkgs/patches-linux-5.15/0071-dt-bindings-irqchip-armada-370-xp-Update-information.patch
new file mode 100644
index 0000000..2819708
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0071-dt-bindings-irqchip-armada-370-xp-Update-information.patch
@@ -0,0 +1,42 @@
+From 7324692109764dbd416a308e796ef1d463d07100 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 6 May 2022 14:22:28 +0200
+Subject: [PATCH 71/90] dt-bindings: irqchip: armada-370-xp: Update information
+ about MPIC SoC Error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ .../interrupt-controller/marvell,armada-370-xp-mpic.txt | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt
+index 5fc03134a999..8cddbc16ddbd 100644
+--- a/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt
++++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt
+@@ -24,6 +24,11 @@ Optional properties:
+ connected as a slave to the Cortex-A9 GIC. The provided interrupt
+ indicate to which GIC interrupt the MPIC output is connected.
+
++Optional subnodes:
++
++- interrupt-controller@20 with interrupt-controller property for
++ MPIC SoC Error IRQ controller
++
+ Example:
+
+ mpic: interrupt-controller@d0020000 {
+@@ -35,4 +40,8 @@ Example:
+ msi-controller;
+ reg = <0xd0020a00 0x1d0>,
+ <0xd0021070 0x58>;
++ soc_err: interrupt-controller@20 {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0072-irqchip-armada-370-xp-Implement-SoC-Error-interrupts.patch b/pkgs/patches-linux-5.15/0072-irqchip-armada-370-xp-Implement-SoC-Error-interrupts.patch
new file mode 100644
index 0000000..5bd6786
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0072-irqchip-armada-370-xp-Implement-SoC-Error-interrupts.patch
@@ -0,0 +1,348 @@
+From e4f14222579ceded47baafcbe10fc78f080e538b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 18 Apr 2022 00:04:32 +0200
+Subject: [PATCH 72/90] irqchip/armada-370-xp: Implement SoC Error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+MPIC IRQ 4 is used as SoC Error Summary interrupt and provides access to
+another hierarchy of SoC Error interrupts. Implement a new IRQ chip and
+domain for accessing this IRQ hierarchy.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/irqchip/irq-armada-370-xp.c | 212 +++++++++++++++++++++++++++-
+ 1 file changed, 209 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
+index 8abc70ed30c1..2df9e21e1559 100644
+--- a/drivers/irqchip/irq-armada-370-xp.c
++++ b/drivers/irqchip/irq-armada-370-xp.c
+@@ -117,6 +117,8 @@
+ /* Registers relative to main_int_base */
+ #define ARMADA_370_XP_INT_CONTROL (0x00)
+ #define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x04)
++#define ARMADA_370_XP_INT_SOC_ERR_0_CAUSE_OFFS (0x20)
++#define ARMADA_370_XP_INT_SOC_ERR_1_CAUSE_OFFS (0x24)
+ #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30)
+ #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34)
+ #define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4)
+@@ -130,6 +132,8 @@
+ #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
+ #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
+ #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
++#define ARMADA_370_XP_INT_SOC_ERR_0_MASK_OFF (0x50)
++#define ARMADA_370_XP_INT_SOC_ERR_1_MASK_OFF (0x54)
+ #define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54)
+ #define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu)
+
+@@ -153,6 +157,8 @@
+ static void __iomem *per_cpu_int_base;
+ static void __iomem *main_int_base;
+ static struct irq_domain *armada_370_xp_mpic_domain;
++static struct irq_domain *armada_370_xp_soc_err_domain;
++static unsigned int soc_err_irq_num_regs;
+ static u32 doorbell_mask_reg;
+ static int parent_irq;
+ #ifdef CONFIG_PCI_MSI
+@@ -163,6 +169,8 @@ static DEFINE_MUTEX(msi_used_lock);
+ static phys_addr_t msi_doorbell_addr;
+ #endif
+
++static void armada_370_xp_soc_err_irq_unmask(struct irq_data *d);
++
+ static inline bool is_percpu_irq(irq_hw_number_t irq)
+ {
+ if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS)
+@@ -528,6 +536,27 @@ static void armada_xp_mpic_reenable_percpu(void)
+ armada_370_xp_irq_unmask(data);
+ }
+
++ /* Re-enable per-CPU SoC Error interrupts that were enabled before suspend */
++ for (irq = 0; irq < soc_err_irq_num_regs * 32; irq++) {
++ struct irq_data *data;
++ int virq;
++
++ virq = irq_linear_revmap(armada_370_xp_soc_err_domain, irq);
++ if (virq == 0)
++ continue;
++
++ data = irq_get_irq_data(virq);
++
++ if (!irq_percpu_is_enabled(virq))
++ continue;
++
++ armada_370_xp_soc_err_irq_unmask(data);
++ }
++
++ /* Unmask summary SoC Error Interrupt */
++ if (soc_err_irq_num_regs > 0)
++ writel(4, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
++
+ /* IPI is used only when we do not have parent irq */
+ if (parent_irq <= 0)
+ ipi_resume();
+@@ -567,8 +596,8 @@ static struct irq_chip armada_370_xp_irq_chip = {
+ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
+ unsigned int virq, irq_hw_number_t hw)
+ {
+- /* IRQs 0 and 1 cannot be mapped, they are handled internally */
+- if (hw <= 1)
++ /* IRQs 0, 1 and 4 cannot be mapped, they are handled internally */
++ if (hw <= 1 || hw == 4)
+ return -EINVAL;
+
+ armada_370_xp_irq_mask(irq_get_irq_data(virq));
+@@ -598,6 +627,98 @@ static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ };
+
++static DEFINE_RAW_SPINLOCK(armada_370_xp_soc_err_lock);
++
++static void armada_370_xp_soc_err_irq_mask(struct irq_data *d)
++{
++ irq_hw_number_t hwirq = irqd_to_hwirq(d);
++ u32 reg, mask;
++
++ reg = hwirq >= 32 ? ARMADA_370_XP_INT_SOC_ERR_1_MASK_OFF
++ : ARMADA_370_XP_INT_SOC_ERR_0_MASK_OFF;
++
++ raw_spin_lock(&armada_370_xp_soc_err_lock);
++ mask = readl(per_cpu_int_base + reg);
++ mask &= ~BIT(hwirq % 32);
++ writel(mask, per_cpu_int_base + reg);
++ raw_spin_unlock(&armada_370_xp_soc_err_lock);
++}
++
++static void armada_370_xp_soc_err_irq_unmask(struct irq_data *d)
++{
++ irq_hw_number_t hwirq = irqd_to_hwirq(d);
++ u32 reg, mask;
++
++ reg = hwirq >= 32 ? ARMADA_370_XP_INT_SOC_ERR_1_MASK_OFF
++ : ARMADA_370_XP_INT_SOC_ERR_0_MASK_OFF;
++
++ raw_spin_lock(&armada_370_xp_soc_err_lock);
++ mask = readl(per_cpu_int_base + reg);
++ mask |= BIT(hwirq % 32);
++ writel(mask, per_cpu_int_base + reg);
++ raw_spin_unlock(&armada_370_xp_soc_err_lock);
++}
++
++static int armada_370_xp_soc_err_irq_mask_on_cpu(void *par)
++{
++ struct irq_data *d = par;
++ armada_370_xp_soc_err_irq_mask(d);
++ return 0;
++}
++
++static int armada_370_xp_soc_err_irq_unmask_on_cpu(void *par)
++{
++ struct irq_data *d = par;
++ armada_370_xp_soc_err_irq_unmask(d);
++ return 0;
++}
++
++static int armada_xp_soc_err_irq_set_affinity(struct irq_data *d,
++ const struct cpumask *mask,
++ bool force)
++{
++ unsigned int cpu;
++
++ cpus_read_lock();
++
++ /* First disable IRQ on all cores */
++ for_each_online_cpu(cpu)
++ smp_call_on_cpu(cpu, armada_370_xp_soc_err_irq_mask_on_cpu, d, true);
++
++ /* Select a single core from the affinity mask which is online */
++ cpu = cpumask_any_and(mask, cpu_online_mask);
++ smp_call_on_cpu(cpu, armada_370_xp_soc_err_irq_unmask_on_cpu, d, true);
++
++ cpus_read_unlock();
++
++ irq_data_update_effective_affinity(d, cpumask_of(cpu));
++
++ return IRQ_SET_MASK_OK;
++}
++
++static struct irq_chip armada_370_xp_soc_err_irq_chip = {
++ .name = "MPIC SOC",
++ .irq_mask = armada_370_xp_soc_err_irq_mask,
++ .irq_unmask = armada_370_xp_soc_err_irq_unmask,
++ .irq_set_affinity = armada_xp_soc_err_irq_set_affinity,
++};
++
++static int armada_370_xp_soc_err_irq_map(struct irq_domain *h,
++ unsigned int virq, irq_hw_number_t hw)
++{
++ armada_370_xp_soc_err_irq_mask(irq_get_irq_data(virq));
++ irq_set_status_flags(virq, IRQ_LEVEL);
++ irq_set_chip_and_handler(virq, &armada_370_xp_soc_err_irq_chip,
++ handle_level_irq);
++ irq_set_probe(virq);
++ return 0;
++}
++
++static const struct irq_domain_ops armada_370_xp_soc_err_irq_ops = {
++ .map = armada_370_xp_soc_err_irq_map,
++ .xlate = irq_domain_xlate_onecell,
++};
++
+ #ifdef CONFIG_PCI_MSI
+ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
+ {
+@@ -633,6 +754,32 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
+ static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {}
+ #endif
+
++static void armada_370_xp_handle_soc_err_irq(void)
++{
++ unsigned long status, bit;
++ u32 mask, cause;
++
++ if (soc_err_irq_num_regs < 1)
++ return;
++
++ mask = readl(per_cpu_int_base + ARMADA_370_XP_INT_SOC_ERR_0_MASK_OFF);
++ cause = readl(main_int_base + ARMADA_370_XP_INT_SOC_ERR_0_CAUSE_OFFS);
++ status = cause & mask;
++
++ for_each_set_bit(bit, &status, 32)
++ generic_handle_domain_irq(armada_370_xp_soc_err_domain, bit);
++
++ if (soc_err_irq_num_regs < 2)
++ return;
++
++ mask = readl(per_cpu_int_base + ARMADA_370_XP_INT_SOC_ERR_1_MASK_OFF);
++ cause = readl(main_int_base + ARMADA_370_XP_INT_SOC_ERR_1_CAUSE_OFFS);
++ status = cause & mask;
++
++ for_each_set_bit(bit, &status, 32)
++ generic_handle_domain_irq(armada_370_xp_soc_err_domain, bit + 32);
++}
++
+ static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc)
+ {
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+@@ -658,6 +805,11 @@ static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc)
+ continue;
+ }
+
++ if (irqn == 4) {
++ armada_370_xp_handle_soc_err_irq();
++ continue;
++ }
++
+ generic_handle_domain_irq(armada_370_xp_mpic_domain, irqn);
+ }
+
+@@ -677,7 +829,7 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
+ if (irqnr > 1022)
+ break;
+
+- if (irqnr > 1) {
++ if (irqnr > 1 && irqnr != 4) {
+ handle_domain_irq(armada_370_xp_mpic_domain,
+ irqnr, regs);
+ continue;
+@@ -687,6 +839,10 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
+ if (irqnr == 1)
+ armada_370_xp_handle_msi_irq(regs, false);
+
++ /* SoC Error handling */
++ if (irqnr == 4)
++ armada_370_xp_handle_soc_err_irq();
++
+ #ifdef CONFIG_SMP
+ /* IPI Handling */
+ if (irqnr == 0) {
+@@ -750,6 +906,26 @@ static void armada_370_xp_mpic_resume(void)
+ }
+ }
+
++ /* Re-enable per-CPU SoC Error interrupts */
++ for (irq = 0; irq < soc_err_irq_num_regs * 32; irq++) {
++ struct irq_data *data;
++ int virq;
++
++ virq = irq_linear_revmap(armada_370_xp_soc_err_domain, irq);
++ if (virq == 0)
++ continue;
++
++ data = irq_get_irq_data(virq);
++
++ /*
++ * Re-enable on the current CPU,
++ * armada_xp_mpic_reenable_percpu() will take
++ * care of secondary CPUs when they come up.
++ */
++ if (irq_percpu_is_enabled(virq))
++ armada_370_xp_soc_err_irq_unmask(data);
++ }
++
+ /* Reconfigure doorbells for IPIs and MSIs */
+ writel(doorbell_mask_reg,
+ per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+@@ -768,6 +944,10 @@ static void armada_370_xp_mpic_resume(void)
+ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+ }
+
++ /* Unmask summary SoC Error Interrupt */
++ if (soc_err_irq_num_regs > 0)
++ writel(4, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
++
+ /* IPI is used only when we do not have parent irq */
+ if (parent_irq <= 0)
+ ipi_resume();
+@@ -782,6 +962,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
+ struct device_node *parent)
+ {
+ struct resource main_int_res, per_cpu_int_res;
++ struct device_node *soc_err_node;
+ int nr_irqs, i;
+ u32 control;
+
+@@ -815,6 +996,27 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
+ BUG_ON(!armada_370_xp_mpic_domain);
+ irq_domain_update_bus_token(armada_370_xp_mpic_domain, DOMAIN_BUS_WIRED);
+
++ soc_err_node = of_get_next_child(node, NULL);
++ if (!soc_err_node) {
++ pr_warn("Missing SoC Error Interrupt Controller node\n");
++ pr_warn("Extended interrupts are not supported\n");
++ } else {
++ pr_info("Registering MPIC SoC Error Interrupt Controller\n");
++ /*
++ * Armada 370 and XP have only 32 SoC Error IRQs in one register
++ * and other Armada platforms have 64 IRQs in two registers.
++ */
++ soc_err_irq_num_regs =
++ of_machine_is_compatible("marvell,armada-370-xp") ? 1 : 2;
++ armada_370_xp_soc_err_domain =
++ irq_domain_add_hierarchy(armada_370_xp_mpic_domain, 0,
++ soc_err_irq_num_regs * 32,
++ soc_err_node,
++ &armada_370_xp_soc_err_irq_ops,
++ NULL);
++ BUG_ON(!armada_370_xp_soc_err_domain);
++ }
++
+ /*
+ * parent_irq is used for distinguish between IPI and non-IPI platforms.
+ * So initialize it before calling any other driver functions.
+@@ -827,6 +1029,10 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
+
+ armada_370_xp_msi_init(node, main_int_res.start);
+
++ /* Unmask summary SoC Error Interrupt */
++ if (soc_err_irq_num_regs > 0)
++ writel(4, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
++
+ if (parent_irq <= 0) {
+ irq_set_default_host(armada_370_xp_mpic_domain);
+ set_handle_irq(armada_370_xp_handle_irq);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0073-ARM-dts-armada-370-xp.dtsi-Add-node-for-MPIC-SoC-Err.patch b/pkgs/patches-linux-5.15/0073-ARM-dts-armada-370-xp.dtsi-Add-node-for-MPIC-SoC-Err.patch
new file mode 100644
index 0000000..6dec138
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0073-ARM-dts-armada-370-xp.dtsi-Add-node-for-MPIC-SoC-Err.patch
@@ -0,0 +1,33 @@
+From e43fb41649eb974e62b64debc234cba8aed4f978 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 16:25:39 +0200
+Subject: [PATCH 73/90] ARM: dts: armada-370-xp.dtsi: Add node for MPIC SoC
+ Error IRQ controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-370-xp.dtsi | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi
+index 0b8c2a64b36f..7aedacff2c00 100644
+--- a/arch/arm/boot/dts/armada-370-xp.dtsi
++++ b/arch/arm/boot/dts/armada-370-xp.dtsi
+@@ -171,6 +171,11 @@ mpic: interrupt-controller@20a00 {
+ #size-cells = <1>;
+ interrupt-controller;
+ msi-controller;
++
++ soc_err: interrupt-controller@20 {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ coherencyfab: coherency-fabric@20200 {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0074-ARM-dts-armada-375.dtsi-Add-node-for-MPIC-SoC-Error-.patch b/pkgs/patches-linux-5.15/0074-ARM-dts-armada-375.dtsi-Add-node-for-MPIC-SoC-Error-.patch
new file mode 100644
index 0000000..410109c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0074-ARM-dts-armada-375.dtsi-Add-node-for-MPIC-SoC-Error-.patch
@@ -0,0 +1,33 @@
+From 2a2f2b633f0ecce7a56322c17a8ed91db71977eb Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 16:43:14 +0200
+Subject: [PATCH 74/90] ARM: dts: armada-375.dtsi: Add node for MPIC SoC Error
+ IRQ controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-375.dtsi | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi
+index c310ef26d1cc..82f0a59d112f 100644
+--- a/arch/arm/boot/dts/armada-375.dtsi
++++ b/arch/arm/boot/dts/armada-375.dtsi
+@@ -376,6 +376,11 @@ mpic: interrupt-controller@20a00 {
+ interrupt-controller;
+ msi-controller;
+ interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>;
++
++ soc_err: interrupt-controller@20 {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ timer1: timer@20300 {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0075-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch b/pkgs/patches-linux-5.15/0075-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch
new file mode 100644
index 0000000..67d1064
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0075-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch
@@ -0,0 +1,35 @@
+From 29284b9f12db226af974191bdf2164bc3155c2c3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 18 Apr 2022 00:39:52 +0200
+Subject: [PATCH 75/90] ARM: dts: armada-38x.dtsi: Add node for MPIC SoC Error
+ IRQ controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It is child of the MPIC IRQ controller.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-38x.dtsi | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi
+index df3c8d1d8f64..099f167b65aa 100644
+--- a/arch/arm/boot/dts/armada-38x.dtsi
++++ b/arch/arm/boot/dts/armada-38x.dtsi
+@@ -398,6 +398,11 @@ mpic: interrupt-controller@20a00 {
+ interrupt-controller;
+ msi-controller;
+ interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>;
++
++ soc_err: interrupt-controller@20 {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ timer: timer@20300 {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0076-ARM-dts-armada-39x.dtsi-Add-node-for-MPIC-SoC-Error-.patch b/pkgs/patches-linux-5.15/0076-ARM-dts-armada-39x.dtsi-Add-node-for-MPIC-SoC-Error-.patch
new file mode 100644
index 0000000..bf59a95
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0076-ARM-dts-armada-39x.dtsi-Add-node-for-MPIC-SoC-Error-.patch
@@ -0,0 +1,33 @@
+From add0bae184216783b6f6bc67c6d4b48fec781ad5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 17:49:36 +0200
+Subject: [PATCH 76/90] ARM: dts: armada-39x.dtsi: Add node for MPIC SoC Error
+ IRQ controller
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-39x.dtsi | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi
+index 9d1cac49c022..f21231a1f244 100644
+--- a/arch/arm/boot/dts/armada-39x.dtsi
++++ b/arch/arm/boot/dts/armada-39x.dtsi
+@@ -272,6 +272,11 @@ mpic: interrupt-controller@20a00 {
+ interrupt-controller;
+ msi-controller;
+ interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>;
++
++ soc_err: interrupt-controller@20 {
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
+ };
+
+ timer@20300 {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0077-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0077-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-err.patch
new file mode 100644
index 0000000..2093cfd
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0077-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-err.patch
@@ -0,0 +1,43 @@
+From 9740547f8b4a338e7d67760db774ef49033ed033 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 16:31:57 +0200
+Subject: [PATCH 77/90] ARM: dts: armada-370.dtsi: Add definitions for PCIe
+ error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-370.dtsi | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi
+index 2013a5ccecd3..9daece786a53 100644
+--- a/arch/arm/boot/dts/armada-370.dtsi
++++ b/arch/arm/boot/dts/armada-370.dtsi
+@@ -60,8 +60,8 @@ pcie0: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 58>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 58>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+@@ -88,8 +88,8 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 62>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 62>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0078-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0078-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch
new file mode 100644
index 0000000..e8ced75
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0078-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch
@@ -0,0 +1,79 @@
+From d7af6212b6962f75c52dd61516ab92eb448a83bb Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 19:33:45 +0200
+Subject: [PATCH 78/90] ARM: dts: armada-xp-mv78230.dtsi: Add definitions for
+ PCIe error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+PCIe controllers on Marvell Port 0 share MPIC SoC Error IRQ 4 and PCIe
+controller on Marvell Port 1 uses MPIC SoC Error IRQ 5.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-xp-mv78230.dtsi | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-xp-mv78230.dtsi b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
+index 5ea9d509cd30..b8d169c4feec 100644
+--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
+@@ -83,8 +83,8 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 58>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 58>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+@@ -111,8 +111,8 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 59>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 59>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+@@ -139,8 +139,8 @@ pcie3: pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 60>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 60>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+@@ -167,8 +167,8 @@ pcie4: pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 61>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 61>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+@@ -195,8 +195,8 @@ pcie5: pcie@5,0 {
+ reg = <0x2800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 62>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 62>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0
+ 0x81000000 0 0 0x81000000 0x5 0 1 0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0079-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0079-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch
new file mode 100644
index 0000000..de93813
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0079-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch
@@ -0,0 +1,124 @@
+From 9197b016f20750c4df3e33b48c9252fbb5972425 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 20:02:26 +0200
+Subject: [PATCH 79/90] ARM: dts: armada-xp-mv78260.dtsi: Add definitions for
+ PCIe error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+PCIe controllers on Marvell Port 0 share MPIC SoC Error IRQ 4, PCIe
+controllers on Marvell Port 1 share MPIC SoC Error IRQ 5 and PCIe
+controller on Marvell Port 2 uses MPIC SoC Error IRQ 15.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-xp-mv78260.dtsi | 36 ++++++++++++------------
+ 1 file changed, 18 insertions(+), 18 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-xp-mv78260.dtsi b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
+index 6c6fbb9faf5a..febd9d98a44e 100644
+--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
+@@ -98,8 +98,8 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 58>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 58>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+@@ -126,8 +126,8 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 59>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 59>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+@@ -154,8 +154,8 @@ pcie3: pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 60>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 60>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+@@ -182,8 +182,8 @@ pcie4: pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 61>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 61>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+@@ -210,8 +210,8 @@ pcie5: pcie@5,0 {
+ reg = <0x2800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 62>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 62>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0
+ 0x81000000 0 0 0x81000000 0x5 0 1 0>;
+@@ -238,8 +238,8 @@ pcie6: pcie@6,0 {
+ reg = <0x3000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 63>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 63>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x6 0 1 0
+ 0x81000000 0 0 0x81000000 0x6 0 1 0>;
+@@ -266,8 +266,8 @@ pcie7: pcie@7,0 {
+ reg = <0x3800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 64>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 64>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x7 0 1 0
+ 0x81000000 0 0 0x81000000 0x7 0 1 0>;
+@@ -294,8 +294,8 @@ pcie8: pcie@8,0 {
+ reg = <0x4000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 65>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 65>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x8 0 1 0
+ 0x81000000 0 0 0x81000000 0x8 0 1 0>;
+@@ -322,8 +322,8 @@ pcie9: pcie@9,0 {
+ reg = <0x4800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 99>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 99>, <&soc_err 15>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x9 0 1 0
+ 0x81000000 0 0 0x81000000 0x9 0 1 0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0080-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0080-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch
new file mode 100644
index 0000000..cdea44f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0080-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch
@@ -0,0 +1,136 @@
+From 20d6c3c5af016a0a2c176e4bed82a7fede362462 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 20:05:35 +0200
+Subject: [PATCH 80/90] ARM: dts: armada-xp-mv78460.dtsi: Add definitions for
+ PCIe error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+PCIe controllers on Marvell Port 0 share MPIC SoC Error IRQ 4, PCIe
+controllers on Marvell Port 1 share MPIC SoC Error IRQ 5, PCIe
+controller on Marvell Port 2 uses MPIC SoC Error IRQ 15 and PCIe
+controller on Marvell Port 3 uses MPIC SoC Error IRQ 16.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-xp-mv78460.dtsi | 40 ++++++++++++------------
+ 1 file changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-xp-mv78460.dtsi b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
+index 16185edf9aa5..3b8adbc89a06 100644
+--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
+@@ -119,8 +119,8 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 58>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 58>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+@@ -147,8 +147,8 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 59>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 59>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+@@ -175,8 +175,8 @@ pcie3: pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 60>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 60>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+@@ -203,8 +203,8 @@ pcie4: pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 61>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 61>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+@@ -231,8 +231,8 @@ pcie5: pcie@5,0 {
+ reg = <0x2800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 62>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 62>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0
+ 0x81000000 0 0 0x81000000 0x5 0 1 0>;
+@@ -259,8 +259,8 @@ pcie6: pcie@6,0 {
+ reg = <0x3000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 63>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 63>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x6 0 1 0
+ 0x81000000 0 0 0x81000000 0x6 0 1 0>;
+@@ -287,8 +287,8 @@ pcie7: pcie@7,0 {
+ reg = <0x3800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 64>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 64>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x7 0 1 0
+ 0x81000000 0 0 0x81000000 0x7 0 1 0>;
+@@ -315,8 +315,8 @@ pcie8: pcie@8,0 {
+ reg = <0x4000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 65>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 65>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x8 0 1 0
+ 0x81000000 0 0 0x81000000 0x8 0 1 0>;
+@@ -343,8 +343,8 @@ pcie9: pcie@9,0 {
+ reg = <0x4800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 99>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 99>, <&soc_err 15>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x9 0 1 0
+ 0x81000000 0 0 0x81000000 0x9 0 1 0>;
+@@ -371,8 +371,8 @@ pcie10: pcie@a,0 {
+ reg = <0x5000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 103>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 103>, <&soc_err 16>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0xa 0 1 0
+ 0x81000000 0 0 0x81000000 0xa 0 1 0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0081-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch b/pkgs/patches-linux-5.15/0081-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch
new file mode 100644
index 0000000..814d003
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0081-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch
@@ -0,0 +1,32 @@
+From 776df9728a4b2d3ae935487d815fbf8570b86701 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 17:30:55 +0200
+Subject: [PATCH 81/90] ARM: dts: armada-xp-98dx3236.dtsi: Add definitions for
+ PCIe error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-xp-98dx3236.dtsi | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-xp-98dx3236.dtsi b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi
+index b21ffb819b1d..0d021f3b86be 100644
+--- a/arch/arm/boot/dts/armada-xp-98dx3236.dtsi
++++ b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi
+@@ -76,8 +76,8 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&mpic 58>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&mpic 58>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0082-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0082-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-err.patch
new file mode 100644
index 0000000..93d506c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0082-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-err.patch
@@ -0,0 +1,43 @@
+From 3fca9286a8603517109f21d743bac3bf657b39b0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 16:45:29 +0200
+Subject: [PATCH 82/90] ARM: dts: armada-375.dtsi: Add definitions for PCIe
+ error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-375.dtsi | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi
+index 82f0a59d112f..71b01a089c81 100644
+--- a/arch/arm/boot/dts/armada-375.dtsi
++++ b/arch/arm/boot/dts/armada-375.dtsi
+@@ -573,8 +573,8 @@ pcie0: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>, <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+@@ -601,8 +601,8 @@ pcie1: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0083-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0083-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-err.patch
new file mode 100644
index 0000000..1c5ddb2
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0083-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-err.patch
@@ -0,0 +1,57 @@
+From 8bbc60f9c76f529c929def04a9feb39bf05dc355 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 17:41:39 +0200
+Subject: [PATCH 83/90] ARM: dts: armada-380.dtsi: Add definitions for PCIe
+ error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-380.dtsi | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-380.dtsi b/arch/arm/boot/dts/armada-380.dtsi
+index e94f22b0e9b5..970ac6820db9 100644
+--- a/arch/arm/boot/dts/armada-380.dtsi
++++ b/arch/arm/boot/dts/armada-380.dtsi
+@@ -64,8 +64,9 @@ pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+@@ -93,8 +94,9 @@ pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+@@ -122,8 +124,9 @@ pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 15>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch
new file mode 100644
index 0000000..b4aaf8b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch
@@ -0,0 +1,71 @@
+From bb12d452fcc9386758e8d144c3090eddb0ef3fcd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 18 Apr 2022 00:40:05 +0200
+Subject: [PATCH 84/90] ARM: dts: armada-385.dtsi: Add definitions for PCIe
+ error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+PCIe error interrupt is reported by MPIC SoC Error IRQ controller.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-385.dtsi | 20 ++++++++++++--------
+ 1 file changed, 12 insertions(+), 8 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-385.dtsi b/arch/arm/boot/dts/armada-385.dtsi
+index be8d607c59b2..d603de5aa574 100644
+--- a/arch/arm/boot/dts/armada-385.dtsi
++++ b/arch/arm/boot/dts/armada-385.dtsi
+@@ -69,8 +69,9 @@ pcie1: pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+@@ -97,8 +98,9 @@ pcie2: pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+@@ -125,8 +127,9 @@ pcie3: pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 15>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+@@ -156,8 +159,9 @@ pcie4: pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 16>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0085-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0085-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-err.patch
new file mode 100644
index 0000000..1b0be45
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0085-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-err.patch
@@ -0,0 +1,69 @@
+From 3eb30a9715bc5754c09341c20d296d968d7885a0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Mon, 27 Jun 2022 17:50:54 +0200
+Subject: [PATCH 85/90] ARM: dts: armada-39x.dtsi: Add definitions for PCIe
+ error interrupts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ arch/arm/boot/dts/armada-39x.dtsi | 20 ++++++++++++--------
+ 1 file changed, 12 insertions(+), 8 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi
+index f21231a1f244..f58bd456e5ad 100644
+--- a/arch/arm/boot/dts/armada-39x.dtsi
++++ b/arch/arm/boot/dts/armada-39x.dtsi
+@@ -443,8 +443,9 @@ pcie@1,0 {
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 4>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0
+ 0x81000000 0 0 0x81000000 0x1 0 1 0>;
+@@ -472,8 +473,9 @@ pcie@2,0 {
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 5>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0
+ 0x81000000 0 0 0x81000000 0x2 0 1 0>;
+@@ -501,8 +503,9 @@ pcie@3,0 {
+ reg = <0x1800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 15>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0
+ 0x81000000 0 0 0x81000000 0x3 0 1 0>;
+@@ -533,8 +536,9 @@ pcie@4,0 {
+ reg = <0x2000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+- interrupt-names = "intx";
+- interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "intx", "error";
++ interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>,
++ <&soc_err 16>;
+ #interrupt-cells = <1>;
+ ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0
+ 0x81000000 0 0 0x81000000 0x4 0 1 0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0086-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch b/pkgs/patches-linux-5.15/0086-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch
new file mode 100644
index 0000000..51b8d55
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0086-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch
@@ -0,0 +1,139 @@
+From ff967b37402c672f475854be60f6b47a69732a59 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 31 Mar 2021 15:12:50 +0200
+Subject: [PATCH 86/90] PCI: pciehp: Enable DLLSC interrupt only if supported
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Don't enable Data Link Layer State Changed interrupt if it isn't
+supported.
+
+Data Link Layer Link Active Reporting Capable bit in Link Capabilities
+register indicates if Data Link Layer State Changed Enable is supported.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/hotplug/pciehp_hpc.c | 32 ++++++++++++++++++++++++--------
+ drivers/pci/hotplug/pnv_php.c | 13 +++++++++----
+ 2 files changed, 33 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
+index 60098a701e83..5b0d48435b36 100644
+--- a/drivers/pci/hotplug/pciehp_hpc.c
++++ b/drivers/pci/hotplug/pciehp_hpc.c
+@@ -788,6 +788,7 @@ static int pciehp_poll(void *data)
+ static void pcie_enable_notification(struct controller *ctrl)
+ {
+ u16 cmd, mask;
++ u32 link_cap;
+
+ /*
+ * TBD: Power fault detected software notification support.
+@@ -800,12 +801,17 @@ static void pcie_enable_notification(struct controller *ctrl)
+ * next power fault detected interrupt was notified again.
+ */
+
++ pcie_capability_read_dword(ctrl_dev(ctrl), PCI_EXP_LNKCAP, &link_cap);
++
+ /*
+- * Always enable link events: thus link-up and link-down shall
+- * always be treated as hotplug and unplug respectively. Enable
+- * presence detect only if Attention Button is not present.
+- */
+- cmd = PCI_EXP_SLTCTL_DLLSCE;
++ * Enable link events if their support is indicated in Link Capability
++ * register: thus link-up and link-down shall always be treated as
++ * hotplug and unplug respectively. Enable presence detect only if
++ * Attention Button is not present.
++ */
++ cmd = 0;
++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
++ cmd |= PCI_EXP_SLTCTL_DLLSCE;
+ if (ATTN_BUTTN(ctrl))
+ cmd |= PCI_EXP_SLTCTL_ABPE;
+ else
+@@ -845,8 +851,13 @@ void pcie_clear_hotplug_events(struct controller *ctrl)
+ void pcie_enable_interrupt(struct controller *ctrl)
+ {
+ u16 mask;
++ u32 link_cap;
+
+- mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
++ pcie_capability_read_dword(ctrl_dev(ctrl), PCI_EXP_LNKCAP, &link_cap);
++
++ mask = PCI_EXP_SLTCTL_HPIE;
++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
++ mask |= PCI_EXP_SLTCTL_DLLSCE;
+ pcie_write_cmd(ctrl, mask, mask);
+ }
+
+@@ -904,19 +915,24 @@ int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, bool probe)
+ struct controller *ctrl = to_ctrl(hotplug_slot);
+ struct pci_dev *pdev = ctrl_dev(ctrl);
+ u16 stat_mask = 0, ctrl_mask = 0;
++ u32 link_cap;
+ int rc;
+
+ if (probe)
+ return 0;
+
++ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
++
+ down_write_nested(&ctrl->reset_lock, ctrl->depth);
+
+ if (!ATTN_BUTTN(ctrl)) {
+ ctrl_mask |= PCI_EXP_SLTCTL_PDCE;
+ stat_mask |= PCI_EXP_SLTSTA_PDC;
+ }
+- ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE;
+- stat_mask |= PCI_EXP_SLTSTA_DLLSC;
++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) {
++ ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE;
++ stat_mask |= PCI_EXP_SLTSTA_DLLSC;
++ }
+
+ pcie_write_cmd(ctrl, 0, ctrl_mask);
+ ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
+index f4c2e6e01be0..41b86fae41c8 100644
+--- a/drivers/pci/hotplug/pnv_php.c
++++ b/drivers/pci/hotplug/pnv_php.c
+@@ -839,6 +839,7 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
+ {
+ struct pci_dev *pdev = php_slot->pdev;
+ u32 broken_pdc = 0;
++ u32 link_cap;
+ u16 sts, ctrl;
+ int ret;
+
+@@ -873,17 +874,21 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
+ return;
+ }
+
++ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
++
+ /* Enable the interrupts */
+ pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl);
+ if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) {
+ ctrl &= ~PCI_EXP_SLTCTL_PDCE;
+- ctrl |= (PCI_EXP_SLTCTL_HPIE |
+- PCI_EXP_SLTCTL_DLLSCE);
++ ctrl |= PCI_EXP_SLTCTL_HPIE;
+ } else {
+ ctrl |= (PCI_EXP_SLTCTL_HPIE |
+- PCI_EXP_SLTCTL_PDCE |
+- PCI_EXP_SLTCTL_DLLSCE);
++ PCI_EXP_SLTCTL_PDCE);
+ }
++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
++ ctrl |= PCI_EXP_SLTCTL_DLLSCE;
++ else
++ ctrl &= ~PCI_EXP_SLTCTL_DLLSCE;
+ pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl);
+
+ /* The interrupt is initialized successfully when @irq is valid */
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0087-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch b/pkgs/patches-linux-5.15/0087-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch
new file mode 100644
index 0000000..af62890
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0087-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch
@@ -0,0 +1,38 @@
+From 589599f1ebbfa109bbcad2758af525b48c01b495 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 31 Mar 2021 15:14:29 +0200
+Subject: [PATCH 87/90] PCI: pciehp: Enable Command Completed Interrupt only if
+ supported
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The No Command Completed Support bit in the Slot Capabilities register
+indicates whether Command Completed Interrupt Enable is unsupported.
+
+Enable this interrupt only in the case it is supported.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/hotplug/pciehp_hpc.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
+index 5b0d48435b36..701031d809f5 100644
+--- a/drivers/pci/hotplug/pciehp_hpc.c
++++ b/drivers/pci/hotplug/pciehp_hpc.c
+@@ -817,7 +817,9 @@ static void pcie_enable_notification(struct controller *ctrl)
+ else
+ cmd |= PCI_EXP_SLTCTL_PDCE;
+ if (!pciehp_poll_mode)
+- cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE;
++ cmd |= PCI_EXP_SLTCTL_HPIE;
++ if (!pciehp_poll_mode && !NO_CMD_CMPL(ctrl))
++ cmd |= PCI_EXP_SLTCTL_CCIE;
+
+ mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
+ PCI_EXP_SLTCTL_PFDE |
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0088-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch b/pkgs/patches-linux-5.15/0088-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch
new file mode 100644
index 0000000..ad01551
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0088-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch
@@ -0,0 +1,297 @@
+From 38251bffc7552d62689e5fd6831f0aae1d842438 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 14:53:11 +0200
+Subject: [PATCH 88/90] PCI: mvebu: Add support for PCI_EXP_SLTSTA_DLLSC via
+ hot plug interrupt
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If link up/down state is changed in mvebu_pcie_link_up() then trigger
+hot plug interrupt with DLLSC state change.
+
+Also triggers hot plug interrupt when mvebu triggers Link Failure interrupt
+which indicates that link was changed from active state or when mvebu
+triggers TxReq No Link interrupt which indicates that link is down while
+trying to transmit PCIe transaction.
+
+And this hot plug interrupt also when explicit Link Disable or PCIe Host
+Reset is issued as mvebu does not trigger Link Failure when dropping to
+Detect via Hot Reset or Link Disable.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/Kconfig | 3 +
+ drivers/pci/controller/pci-mvebu.c | 147 ++++++++++++++++++++++++++++-
+ 2 files changed, 149 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
+index 46fa9bbff177..c217ea3bc703 100644
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -10,6 +10,9 @@ config PCI_MVEBU
+ depends on ARM
+ depends on OF
+ select PCI_BRIDGE_EMUL
++ select PCIEPORTBUS
++ select HOTPLUG_PCI
++ select HOTPLUG_PCI_PCIE
+ help
+ Add support for Marvell EBU PCIe controller. This PCIe controller
+ is used on 32-bit Marvell ARM SoCs: Dove, Kirkwood, Armada 370,
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 319cd2b98545..2792b02278b9 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -56,12 +56,14 @@
+ #define PCIE_CONF_DATA_OFF 0x18fc
+ #define PCIE_INT_CAUSE_OFF 0x1900
+ #define PCIE_INT_UNMASK_OFF 0x1910
++#define PCIE_INT_TXREQ_NOLINK BIT(0)
+ #define PCIE_INT_DET_COR BIT(8)
+ #define PCIE_INT_DET_NONFATAL BIT(9)
+ #define PCIE_INT_DET_FATAL BIT(10)
+ #define PCIE_INT_ERR_FATAL BIT(16)
+ #define PCIE_INT_ERR_NONFATAL BIT(17)
+ #define PCIE_INT_ERR_COR BIT(18)
++#define PCIE_INT_LINK_FAIL BIT(23)
+ #define PCIE_INT_INTX(i) BIT(24+i)
+ #define PCIE_INT_PM_PME BIT(28)
+ #define PCIE_INT_DET_MASK (PCIE_INT_DET_COR | PCIE_INT_DET_NONFATAL | PCIE_INT_DET_FATAL)
+@@ -134,6 +136,8 @@ struct mvebu_pcie_port {
+ int error_irq;
+ int intx_irq;
+ bool pme_pending;
++ struct timer_list link_irq_timer;
++ bool link_was_up;
+ };
+
+ static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg)
+@@ -153,7 +157,26 @@ static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port)
+
+ static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port)
+ {
+- return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN);
++ bool link_is_up;
++ u16 slotsta;
++
++ link_is_up = !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN);
++
++ if (link_is_up != port->link_was_up) {
++ port->link_was_up = link_is_up;
++ /*
++ * Link IRQ timer/handler is available only when "error"
++ * interrupt was specified in DT.
++ */
++ if (port->error_irq > 0) {
++ slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta);
++ port->bridge.pcie_conf.slotsta =
++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC);
++ mod_timer(&port->link_irq_timer, jiffies + 1);
++ }
++ }
++
++ return link_is_up;
+ }
+
+ static u8 mvebu_pcie_get_local_bus_nr(struct mvebu_pcie_port *port)
+@@ -346,6 +369,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
+ }
+
++ /*
++ * Unmask No Link and Link Failure interrupts to process Link Down
++ * events. These events are reported as Data Link Layer State Changed
++ * notification via Hot Plug Interrupt. Other parts of Link change
++ * events are available only when "error" interrupt was specified in DT.
++ * So enable these interrupts under same conditions.
++ */
++ if (port->error_irq > 0) {
++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
++ unmask |= PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL;
++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF);
++ }
++
+ /*
+ * Fallback code when "intx" interrupt was not specified in DT:
+ * Unmask all legacy INTx interrupts as driver does not provide a way
+@@ -697,6 +733,14 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+ val |= slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE;
+ else if (!(mvebu_readl(port, PCIE_SSPL_OFF) & PCIE_SSPL_ENABLE))
+ val |= PCI_EXP_SLTCTL_ASPL_DISABLE;
++ /*
++ * HPIE and DLLSCE bits are stored only in emulated config
++ * space buffer and are supported only when or "error" interrupt
++ * was specified in DT.
++ */
++ if (port->error_irq > 0)
++ val |= slotctl & (PCI_EXP_SLTCTL_HPIE |
++ PCI_EXP_SLTCTL_DLLSCE);
+ /* This callback is 32-bit and in high bits is slot status. */
+ val |= slotsta << 16;
+ *value = val;
+@@ -828,6 +872,25 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
+ else
+ ctrl &= ~PCIE_CTRL_MASTER_HOT_RESET;
+ mvebu_writel(port, ctrl, PCIE_CTRL_OFF);
++ /*
++ * When dropping to Detect via Hot Reset, Disable Link
++ * or Loopback states, the Link Failure interrupt is not
++ * asserted. So when setting Secondary Bus Reset / Hot
++ * Reset bit, call link IRQ timer/handler manually.
++ */
++ if ((ctrl & PCIE_CTRL_MASTER_HOT_RESET) && port->link_was_up) {
++ port->link_was_up = false;
++ /*
++ * Link IRQ timer/handler is available only when
++ * "error" interrupt was specified in DT.
++ */
++ if (port->error_irq > 0) {
++ u16 slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta);
++ port->bridge.pcie_conf.slotsta =
++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC);
++ mod_timer(&port->link_irq_timer, jiffies + 1);
++ }
++ }
+ }
+ break;
+
+@@ -856,6 +919,25 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
+
+ mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
++ /*
++ * When dropping to Detect via Hot Reset, Disable Link
++ * or Loopback states, the Link Failure interrupt is not
++ * asserted. So when setting Link Disable bit, call link
++ * IRQ timer/handler manually.
++ */
++ if ((new & PCI_EXP_LNKCTL_LD) && port->link_was_up) {
++ port->link_was_up = false;
++ /*
++ * Link IRQ timer/handler is available only when
++ * "error" interrupt was specified in DT.
++ */
++ if (port->error_irq > 0) {
++ u16 slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta);
++ port->bridge.pcie_conf.slotsta =
++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC);
++ mod_timer(&port->link_irq_timer, jiffies + 1);
++ }
++ }
+ break;
+
+ case PCI_EXP_SLTCTL:
+@@ -996,6 +1078,15 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver | PCI_EXP_FLAGS_SLOT);
+
+ /*
++ * When "error" interrupt was specified in DT then driver is able to
++ * deliver Data Link Layer State Change interrupt. So in this case mark
++ * bridge as Hot Plug Capable as this is the way how to enable
++ * delivering of Data Link Layer State Change interrupts.
++ *
++ * No Command Completed Support is set because bridge does not support
++ * Command Completed Interrupt. Every command is executed immediately
++ * without any delay.
++ *
+ * Set Presence Detect State bit permanently as there is no support for
+ * unplugging PCIe card from the slot. Assume that PCIe card is always
+ * connected in slot.
+@@ -1007,6 +1098,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ * Also set correct slot power limit.
+ */
+ bridge->pcie_conf.slotcap = cpu_to_le32(
++ PCI_EXP_SLTCAP_NCCS |
++ (port->error_irq > 0 ? PCI_EXP_SLTCAP_HPC : 0) |
+ FIELD_PREP(PCI_EXP_SLTCAP_SPLV, port->slot_power_limit_value) |
+ FIELD_PREP(PCI_EXP_SLTCAP_SPLS, port->slot_power_limit_scale) |
+ FIELD_PREP(PCI_EXP_SLTCAP_PSN, port->port+1));
+@@ -1198,11 +1291,29 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port)
+ return 0;
+ }
+
++static void mvebu_pcie_link_irq_handler(struct timer_list *timer)
++{
++ struct mvebu_pcie_port *port = from_timer(port, timer, link_irq_timer);
++ struct device *dev = &port->pcie->pdev->dev;
++ u16 slotctl;
++
++ dev_info(dev, "%s: link %s\n", port->name, port->link_was_up ? "up" : "down");
++
++ slotctl = le16_to_cpu(port->bridge.pcie_conf.slotctl);
++ if (!(slotctl & PCI_EXP_SLTCTL_DLLSCE) ||
++ !(slotctl & PCI_EXP_SLTCTL_HPIE))
++ return;
++
++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL)
++ dev_err_ratelimited(dev, "unhandled HP IRQ\n");
++}
++
+ static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg)
+ {
+ struct mvebu_pcie_port *port = arg;
+ struct device *dev = &port->pcie->pdev->dev;
+ u32 cause, unmask, status;
++ u16 slotsta;
+
+ cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF);
+ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF);
+@@ -1240,6 +1351,25 @@ static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg)
+ dev_err_ratelimited(dev, "unhandled ERR IRQ\n");
+ }
+
++ /* Process No Link and Link Failure interrupts as HP IRQ */
++ if (status & (PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL)) {
++ mvebu_writel(port,
++ ~(PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL),
++ PCIE_INT_CAUSE_OFF);
++ if (port->link_was_up) {
++ port->link_was_up = false;
++ slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta);
++ port->bridge.pcie_conf.slotsta =
++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC);
++ /*
++ * Deactivate timer and call mvebu_pcie_link_irq_handler()
++ * function directly as we are in the interrupt context.
++ */
++ del_timer_sync(&port->link_irq_timer);
++ mvebu_pcie_link_irq_handler(&port->link_irq_timer);
++ }
++ }
++
+ return status ? IRQ_HANDLED : IRQ_NONE;
+ }
+
+@@ -1805,6 +1935,18 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
+ }
+ }
+
++ /*
++ * Function mvebu_pcie_link_irq_handler() calls function
++ * generic_handle_irq() and it expects local IRQs to be disabled
++ * as normally generic_handle_irq() is called from the interrupt
++ * context. So use TIMER_IRQSAFE flag for this link_irq_timer.
++ * Available only if "or "error" interrupt was specified.
++ */
++ if (port->error_irq > 0)
++ timer_setup(&port->link_irq_timer,
++ mvebu_pcie_link_irq_handler,
++ TIMER_IRQSAFE);
++
+ /*
+ * PCIe topology exported by mvebu hw is quite complicated. In
+ * reality has something like N fully independent host bridges
+@@ -1941,6 +2083,9 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+ irq_domain_remove(port->rp_irq_domain);
+ }
+
++ if (port->error_irq > 0)
++ del_timer_sync(&port->link_irq_timer);
++
+ /* Free config space for emulated root bridge. */
+ pci_bridge_emul_cleanup(&port->bridge);
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0089-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch b/pkgs/patches-linux-5.15/0089-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch
new file mode 100644
index 0000000..a7dbad5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0089-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch
@@ -0,0 +1,47 @@
+From fbe986f71d0b5c280cbf08a3ce11d121739f9c8f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 14:54:29 +0200
+Subject: [PATCH 89/90] PCI: mvebu: use BIT() and GENMASK() macros instead of
+ hardcoded hex values
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 2792b02278b9..87f1c2aa5010 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -44,7 +44,7 @@
+ #define PCIE_WIN5_BASE_OFF 0x1884
+ #define PCIE_WIN5_REMAP_OFF 0x188c
+ #define PCIE_CONF_ADDR_OFF 0x18f8
+-#define PCIE_CONF_ADDR_EN 0x80000000
++#define PCIE_CONF_ADDR_EN BIT(31)
+ #define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & 0xfc))
+ #define PCIE_CONF_BUS(b) (((b) & 0xff) << 16)
+ #define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11)
+@@ -70,13 +70,13 @@
+ #define PCIE_INT_ERR_MASK (PCIE_INT_ERR_FATAL | PCIE_INT_ERR_NONFATAL | PCIE_INT_ERR_COR)
+ #define PCIE_INT_ALL_MASK GENMASK(31, 0)
+ #define PCIE_CTRL_OFF 0x1a00
+-#define PCIE_CTRL_X1_MODE 0x0001
++#define PCIE_CTRL_X1_MODE BIT(0)
+ #define PCIE_CTRL_RC_MODE BIT(1)
+ #define PCIE_CTRL_MASTER_HOT_RESET BIT(24)
+ #define PCIE_STAT_OFF 0x1a04
+-#define PCIE_STAT_BUS 0xff00
+-#define PCIE_STAT_DEV 0x1f0000
+ #define PCIE_STAT_LINK_DOWN BIT(0)
++#define PCIE_STAT_BUS GENMASK(15, 8)
++#define PCIE_STAT_DEV GENMASK(20, 16)
+ #define PCIE_SSPL_OFF 0x1a0c
+ #define PCIE_SSPL_VALUE_SHIFT 0
+ #define PCIE_SSPL_VALUE_MASK GENMASK(7, 0)
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0090-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch b/pkgs/patches-linux-5.15/0090-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch
new file mode 100644
index 0000000..8679fdf
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0090-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch
@@ -0,0 +1,167 @@
+From 905b0eda8ec724de75d825b1d3625c27beb0bc6d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 14:55:03 +0200
+Subject: [PATCH 90/90] PCI: mvebu: For consistency add _OFF suffix to all
+ registers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 40 +++++++++++++++---------------
+ 1 file changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index 87f1c2aa5010..b04b9bbe9217 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -34,7 +34,7 @@
+ #define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
+ #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
+ #define PCIE_SSDEV_ID_OFF 0x002c
+-#define PCIE_CAP_PCIEXP 0x0060
++#define PCIE_CAP_PCIEXP_OFF 0x0060
+ #define PCIE_CAP_PCIERR_OFF 0x0100
+ #define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
+ #define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
+@@ -83,8 +83,8 @@
+ #define PCIE_SSPL_SCALE_SHIFT 8
+ #define PCIE_SSPL_SCALE_MASK GENMASK(9, 8)
+ #define PCIE_SSPL_ENABLE BIT(16)
+-#define PCIE_RC_RTSTA 0x1a14
+-#define PCIE_DEBUG_CTRL 0x1a60
++#define PCIE_RC_RTSTA_OFF 0x1a14
++#define PCIE_DEBUG_CTRL_OFF 0x1a60
+ #define PCIE_DEBUG_SOFT_RESET BIT(20)
+
+ struct mvebu_pcie_port;
+@@ -296,10 +296,10 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ * be set to number of SerDes PCIe lanes (1 or 4). If this register is
+ * not set correctly then link with endpoint card is not established.
+ */
+- lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP);
++ lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCAP);
+ lnkcap &= ~PCI_EXP_LNKCAP_MLW;
+ lnkcap |= (port->is_x4 ? 4 : 1) << 4;
+- mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP);
++ mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCAP);
+
+ /* Disable Root Bridge I/O space, memory space and bus mastering. */
+ cmd = mvebu_readl(port, PCIE_CMD_OFF);
+@@ -695,11 +695,11 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+
+ switch (reg) {
+ case PCI_EXP_DEVCAP:
+- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCAP);
+ break;
+
+ case PCI_EXP_DEVCTL:
+- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL);
++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL);
+ break;
+
+ case PCI_EXP_LNKCAP:
+@@ -709,13 +709,13 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+ * Additionally enable Data Link Layer Link Active Reporting
+ * Capable bit as DL_Active indication is provided too.
+ */
+- *value = (mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) &
++ *value = (mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCAP) &
+ ~PCI_EXP_LNKCAP_CLKPM) | PCI_EXP_LNKCAP_DLLLARC;
+ break;
+
+ case PCI_EXP_LNKCTL:
+ /* DL_Active indication is provided via PCIE_STAT_OFF */
+- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL) |
++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL) |
+ (mvebu_pcie_link_up(port) ?
+ (PCI_EXP_LNKSTA_DLLLA << 16) : 0);
+ break;
+@@ -753,19 +753,19 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+ break;
+
+ case PCI_EXP_RTSTA:
+- *value = mvebu_readl(port, PCIE_RC_RTSTA);
++ *value = mvebu_readl(port, PCIE_RC_RTSTA_OFF);
+ break;
+
+ case PCI_EXP_DEVCAP2:
+- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP2);
++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCAP2);
+ break;
+
+ case PCI_EXP_DEVCTL2:
+- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL2);
++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL2);
+ break;
+
+ case PCI_EXP_LNKCTL2:
+- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL2);
++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL2);
+ break;
+
+ default:
+@@ -907,7 +907,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+
+ switch (reg) {
+ case PCI_EXP_DEVCTL:
+- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL);
++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL);
+ break;
+
+ case PCI_EXP_LNKCTL:
+@@ -918,7 +918,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ */
+ new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
+
+- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL);
+ /*
+ * When dropping to Detect via Hot Reset, Disable Link
+ * or Loopback states, the Link Failure interrupt is not
+@@ -958,7 +958,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+
+ case PCI_EXP_RTSTA:
+ /*
+- * PME Status bit in Root Status Register (PCIE_RC_RTSTA)
++ * PME Status bit in Root Status Register (PCIE_RC_RTSTA_OFF)
+ * is read-only and can be cleared only by writing 0b to the
+ * Interrupt Cause RW0C register (PCIE_INT_CAUSE_OFF). So
+ * clear PME via Interrupt Cause and also set port->pme_pending
+@@ -983,11 +983,11 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ break;
+
+ case PCI_EXP_DEVCTL2:
+- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL2);
++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL2);
+ break;
+
+ case PCI_EXP_LNKCTL2:
+- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL2);
++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL2);
+ break;
+
+ default:
+@@ -1047,7 +1047,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ u32 dev_id = mvebu_readl(port, PCIE_DEV_ID_OFF);
+ u32 dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF);
+ u32 ssdev_id = mvebu_readl(port, PCIE_SSDEV_ID_OFF);
+- u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP);
++ u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF);
+ u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS);
+
+ bridge->conf.vendor = cpu_to_le16(dev_id & 0xffff);
+@@ -1108,7 +1108,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ bridge->subsystem_vendor_id = ssdev_id & 0xffff;
+ bridge->subsystem_id = ssdev_id >> 16;
+ bridge->has_pcie = true;
+- bridge->pcie_start = PCIE_CAP_PCIEXP;
++ bridge->pcie_start = PCIE_CAP_PCIEXP_OFF;
+ bridge->data = port;
+ bridge->ops = &mvebu_pci_bridge_emul_ops;
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0091-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch b/pkgs/patches-linux-5.15/0091-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch
new file mode 100644
index 0000000..b058048
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0091-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch
@@ -0,0 +1,45 @@
+From a6e8a5fd098c65dd8608d21d64df48f5cc7c1957 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 19 Oct 2021 13:54:19 +0200
+Subject: [PATCH 1/9] PCI: aardvark: Add support for PCI Bridge Subsystem
+ Vendor ID on emulated bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Register with Subsystem Device/Vendor ID is at offset 0x2c. Export is via
+emulated bridge.
+
+After this change Subsystem ID is visible in lspci output at line:
+
+ Capabilities: [40] Subsystem
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index 4ed1b3ed3d35..8f01bf82a754 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -32,6 +32,7 @@
+ #define PCIE_CORE_DEV_ID_REG 0x0
+ #define PCIE_CORE_CMD_STATUS_REG 0x4
+ #define PCIE_CORE_DEV_REV_REG 0x8
++#define PCIE_CORE_SSDEV_ID_REG 0x2c
+ #define PCIE_CORE_PCIEXP_CAP 0xc0
+ #define PCIE_CORE_ERR_CAPCTL_REG 0x118
+ #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5)
+@@ -983,6 +984,8 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie)
+ /* Indicates supports for Completion Retry Status */
+ bridge->pcie_conf.rootcap = cpu_to_le16(PCI_EXP_RTCAP_CRSVIS);
+
++ bridge->subsystem_vendor_id = advk_readl(pcie, PCIE_CORE_SSDEV_ID_REG) & 0xffff;
++ bridge->subsystem_id = advk_readl(pcie, PCIE_CORE_SSDEV_ID_REG) >> 16;
+ bridge->has_pcie = true;
+ bridge->pcie_start = PCIE_CORE_PCIEXP_CAP;
+ bridge->data = pcie;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0092-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch b/pkgs/patches-linux-5.15/0092-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch
new file mode 100644
index 0000000..bc21fc1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0092-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch
@@ -0,0 +1,44 @@
+From dbd2881ab44968823962a14f6de0ed73f916e7b5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sat, 9 Jul 2022 16:55:54 +0200
+Subject: [PATCH 2/9] PCI: aardvark: Dispose INTx irqs prior to removing INTx
+ domain
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Documentation for irq_domain_remove() says that all mapping within the
+domain must be disposed prior to domain remove.
+
+Currently INTx irqs are not disposed in pci-aardvark.c device unbind callback
+which cause that kernel crashes after unloading driver and trying to read
+/sys/kernel/debug/irq/irqs/<num> or /proc/interrupts.
+
+Fixes: 526a76991b7b ("PCI: aardvark: Implement driver 'remove' function and allow to build it as module")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index 8f01bf82a754..5dda72ce4d7e 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1439,6 +1439,14 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie)
+
+ static void advk_pcie_remove_irq_domain(struct advk_pcie *pcie)
+ {
++ int virq, i;
++
++ for (i = 0; i < PCI_NUM_INTX; i++) {
++ virq = irq_find_mapping(pcie->irq_domain, i);
++ if (virq > 0)
++ irq_dispose_mapping(virq);
++ }
++
+ irq_domain_remove(pcie->irq_domain);
+ }
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0093-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch b/pkgs/patches-linux-5.15/0093-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch
new file mode 100644
index 0000000..008ef8d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0093-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch
@@ -0,0 +1,139 @@
+From b7f4aea2c3b7f25333b8cf086dd7af7e836a1b6a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Tue, 24 May 2022 15:28:26 +0200
+Subject: [PATCH 3/9] PCI: aardvark: Add support for AER registers on emulated
+ bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Aardvark controller supports Advanced Error Reporting configuration
+registers.
+
+Export these registers on the emulated root bridge via new .read_ext() and
+.write_ext() methods.
+
+Note that in the Advanced Error Reporting Capability header the offset to
+the next Extended Capability header is set, but it is not documented in
+Armada 3700 Functional Specification. Since this change adds support only
+for Advanced Error Reporting, explicitly clear PCI_EXT_CAP_NEXT bits in AER
+capability header.
+
+Now the pcieport driver correctly detects AER support and allows PCIe AER
+driver to start receiving ERR interrupts. Kernel log now says:
+
+ pcieport 0000:00:00.0: AER: enabled with IRQ 52
+
+Link: https://lore.kernel.org/r/20220524132827.8837-2-kabel@kernel.org
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 79 +++++++++++++++++++++++++++
+ 1 file changed, 79 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index 5dda72ce4d7e..73c37164fd44 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -34,6 +34,7 @@
+ #define PCIE_CORE_DEV_REV_REG 0x8
+ #define PCIE_CORE_SSDEV_ID_REG 0x2c
+ #define PCIE_CORE_PCIEXP_CAP 0xc0
++#define PCIE_CORE_PCIERR_CAP 0x100
+ #define PCIE_CORE_ERR_CAPCTL_REG 0x118
+ #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5)
+ #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6)
+@@ -945,11 +946,89 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ }
+ }
+
++static pci_bridge_emul_read_status_t
++advk_pci_bridge_emul_ext_conf_read(struct pci_bridge_emul *bridge,
++ int reg, u32 *value)
++{
++ struct advk_pcie *pcie = bridge->data;
++
++ switch (reg) {
++ case 0:
++ *value = advk_readl(pcie, PCIE_CORE_PCIERR_CAP + reg);
++
++ /*
++ * PCI_EXT_CAP_NEXT bits are set to offset 0x150, but Armada
++ * 3700 Functional Specification does not document registers
++ * at those addresses.
++ *
++ * Thus we clear PCI_EXT_CAP_NEXT bits to make Advanced Error
++ * Reporting Capability header the last Extended Capability.
++ * If we obtain documentation for those registers in the
++ * future, this can be changed.
++ */
++ *value &= 0x000fffff;
++ return PCI_BRIDGE_EMUL_HANDLED;
++
++ case PCI_ERR_UNCOR_STATUS:
++ case PCI_ERR_UNCOR_MASK:
++ case PCI_ERR_UNCOR_SEVER:
++ case PCI_ERR_COR_STATUS:
++ case PCI_ERR_COR_MASK:
++ case PCI_ERR_CAP:
++ case PCI_ERR_HEADER_LOG + 0:
++ case PCI_ERR_HEADER_LOG + 4:
++ case PCI_ERR_HEADER_LOG + 8:
++ case PCI_ERR_HEADER_LOG + 12:
++ case PCI_ERR_ROOT_COMMAND:
++ case PCI_ERR_ROOT_STATUS:
++ case PCI_ERR_ROOT_ERR_SRC:
++ *value = advk_readl(pcie, PCIE_CORE_PCIERR_CAP + reg);
++ return PCI_BRIDGE_EMUL_HANDLED;
++
++ default:
++ return PCI_BRIDGE_EMUL_NOT_HANDLED;
++ }
++}
++
++static void
++advk_pci_bridge_emul_ext_conf_write(struct pci_bridge_emul *bridge,
++ int reg, u32 old, u32 new, u32 mask)
++{
++ struct advk_pcie *pcie = bridge->data;
++
++ switch (reg) {
++ /* These are W1C registers, so clear other bits */
++ case PCI_ERR_UNCOR_STATUS:
++ case PCI_ERR_COR_STATUS:
++ case PCI_ERR_ROOT_STATUS:
++ new &= mask;
++ fallthrough;
++
++ case PCI_ERR_UNCOR_MASK:
++ case PCI_ERR_UNCOR_SEVER:
++ case PCI_ERR_COR_MASK:
++ case PCI_ERR_CAP:
++ case PCI_ERR_HEADER_LOG + 0:
++ case PCI_ERR_HEADER_LOG + 4:
++ case PCI_ERR_HEADER_LOG + 8:
++ case PCI_ERR_HEADER_LOG + 12:
++ case PCI_ERR_ROOT_COMMAND:
++ case PCI_ERR_ROOT_ERR_SRC:
++ advk_writel(pcie, new, PCIE_CORE_PCIERR_CAP + reg);
++ break;
++
++ default:
++ break;
++ }
++}
++
+ static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
+ .read_base = advk_pci_bridge_emul_base_conf_read,
+ .write_base = advk_pci_bridge_emul_base_conf_write,
+ .read_pcie = advk_pci_bridge_emul_pcie_conf_read,
+ .write_pcie = advk_pci_bridge_emul_pcie_conf_write,
++ .read_ext = advk_pci_bridge_emul_ext_conf_read,
++ .write_ext = advk_pci_bridge_emul_ext_conf_write,
+ };
+
+ /*
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0095-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch b/pkgs/patches-linux-5.15/0095-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch
new file mode 100644
index 0000000..6cd3904
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0095-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch
@@ -0,0 +1,41 @@
+From 2749ad1d3c854bf6926d2582cc2a7f727d0d53b3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 24 Aug 2022 15:59:49 +0200
+Subject: [PATCH 5/9] PCI: aardvark: Dispose bridge irq prior to removing
+ bridge domain
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Documentation for irq_domain_remove() says that all mapping within the
+domain must be disposed prior to domain remove.
+
+Currently bridge irq is not disposed in pci-aardvark.c device unbind callback
+which cause that kernel crashes after unloading driver and trying to read
+/sys/kernel/debug/irq/irqs/<num> or /proc/interrupts.
+
+Fixes: 815bc3136867 ("PCI: aardvark: Use separate INTA interrupt for emulated root bridge")
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index f2be2ba51217..db97dc0a8ad7 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1579,6 +1579,11 @@ static int advk_pcie_init_rp_irq_domain(struct advk_pcie *pcie)
+
+ static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie)
+ {
++ int virq;
++
++ virq = irq_find_mapping(pcie->rp_irq_domain, 0);
++ if (virq > 0)
++ irq_dispose_mapping(virq);
+ irq_domain_remove(pcie->rp_irq_domain);
+ }
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0096-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch b/pkgs/patches-linux-5.15/0096-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch
new file mode 100644
index 0000000..7d1b9b0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0096-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch
@@ -0,0 +1,268 @@
+From f36491f0f18ae6b9018ade51cbfea4fbd14ae2a6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 31 Aug 2022 15:55:46 +0200
+Subject: [PATCH 6/9] PCI: aardvark: Add support for DLLSC and hotplug
+ interrupt
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add support for Data Link Layer State Change in the emulated slot
+registers and hotplug interrupt via the emulated root bridge.
+
+This is mainly useful for when an error causes link down event. With
+this change, drivers can try recovery.
+
+Link down state change can be implemented because Aardvark supports Link
+Down event interrupt. Use it for signaling that Data Link Layer Link is
+not active anymore via Hot-Plug Interrupt on emulated root bridge.
+
+Link up interrupt is not available on Aardvark, but we check for whether
+link is up in the advk_pcie_link_up() function. By triggering Hot-Plug
+Interrupt from this function we achieve Link up event, so long as the
+function is called (which it is after probe and when rescanning).
+Although it is not ideal, it is better than nothing.
+
+Since advk_pcie_link_up() is not called from interrupt handler, we
+cannot call generic_handle_domain_irq() from it directly. Instead create
+a TIMER_IRQSAFE timer and trigger it from advk_pcie_link_up().
+
+(We haven't been able to find any documentation for a Link Up interrupt
+ on Aardvark, but it is possible there is one, in some undocumented
+ register. If we manage to find this information, this can be
+ rewritten.)
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/Kconfig | 3 +
+ drivers/pci/controller/pci-aardvark.c | 101 ++++++++++++++++++++++++--
+ 2 files changed, 99 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
+index c217ea3bc703..1a3e123570ed 100644
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -24,6 +24,9 @@ config PCI_AARDVARK
+ depends on OF
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCI_BRIDGE_EMUL
++ select PCIEPORTBUS
++ select HOTPLUG_PCI
++ select HOTPLUG_PCI_PCIE
+ help
+ Add support for Aardvark 64bit PCIe Host Controller. This
+ controller is part of the South Bridge of the Marvel Armada
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index db97dc0a8ad7..8aa05777da88 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -25,6 +25,7 @@
+ #include <linux/of_address.h>
+ #include <linux/of_gpio.h>
+ #include <linux/of_pci.h>
++#include <linux/timer.h>
+
+ #include "../pci.h"
+ #include "../pci-bridge-emul.h"
+@@ -101,6 +102,7 @@
+ #define PCIE_MSG_PM_PME_MASK BIT(7)
+ #define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44)
+ #define PCIE_ISR0_MSI_INT_PENDING BIT(24)
++#define PCIE_ISR0_LINK_DOWN BIT(1)
+ #define PCIE_ISR0_CORR_ERR BIT(11)
+ #define PCIE_ISR0_NFAT_ERR BIT(12)
+ #define PCIE_ISR0_FAT_ERR BIT(13)
+@@ -285,6 +287,8 @@ struct advk_pcie {
+ DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
+ struct mutex msi_used_lock;
+ int link_gen;
++ bool link_was_up;
++ struct timer_list link_irq_timer;
+ struct pci_bridge_emul bridge;
+ struct gpio_desc *reset_gpio;
+ struct phy *phy;
+@@ -314,7 +318,24 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie)
+ {
+ /* check if LTSSM is in normal operation - some L* state */
+ u8 ltssm_state = advk_pcie_ltssm_state(pcie);
+- return ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED;
++ bool link_is_up;
++ u16 slotsta;
++
++ link_is_up = ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED;
++
++ if (link_is_up && !pcie->link_was_up) {
++ dev_info(&pcie->pdev->dev, "link up\n");
++
++ pcie->link_was_up = true;
++
++ slotsta = le16_to_cpu(pcie->bridge.pcie_conf.slotsta);
++ slotsta |= PCI_EXP_SLTSTA_DLLSC;
++ pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta);
++
++ mod_timer(&pcie->link_irq_timer, jiffies + 1);
++ }
++
++ return link_is_up;
+ }
+
+ static inline bool advk_pcie_link_active(struct advk_pcie *pcie)
+@@ -443,8 +464,6 @@ static void advk_pcie_train_link(struct advk_pcie *pcie)
+ ret = advk_pcie_wait_for_link(pcie);
+ if (ret < 0)
+ dev_err(dev, "link never came up\n");
+- else
+- dev_info(dev, "link up\n");
+ }
+
+ /*
+@@ -593,6 +612,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
+ reg &= ~PCIE_ISR0_MSI_INT_PENDING;
+ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
+
++ /* Unmask Link Down interrupt */
++ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
++ reg &= ~PCIE_ISR0_LINK_DOWN;
++ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
++
+ /* Unmask PME interrupt for processing of PME requester */
+ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+ reg &= ~PCIE_MSG_PM_PME_MASK;
+@@ -919,6 +943,14 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ advk_pcie_wait_for_retrain(pcie);
+ break;
+
++ case PCI_EXP_SLTCTL: {
++ u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl);
++ /* Only emulation of HPIE and DLLSCE bits is provided */
++ slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
++ bridge->pcie_conf.slotctl = cpu_to_le16(slotctl);
++ break;
++ }
++
+ case PCI_EXP_RTCTL: {
+ u16 rootctl = le16_to_cpu(bridge->pcie_conf.rootctl);
+ /* Only emulation of PMEIE and CRSSVE bits is provided */
+@@ -1036,6 +1068,7 @@ static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
+ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie)
+ {
+ struct pci_bridge_emul *bridge = &pcie->bridge;
++ u32 slotcap;
+
+ bridge->conf.vendor =
+ cpu_to_le16(advk_readl(pcie, PCIE_CORE_DEV_ID_REG) & 0xffff);
+@@ -1062,6 +1095,13 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie)
+ bridge->pcie_conf.cap = cpu_to_le16(2 | PCI_EXP_FLAGS_SLOT);
+
+ /*
++ * Mark bridge as Hot Plug Capable since this is the way how to enable
++ * delivering of Data Link Layer State Change interrupts.
++ *
++ * Set No Command Completed Support because bridge does not support
++ * Command Completed Interrupt. Every command is executed immediately
++ * without any delay.
++ *
+ * Set Presence Detect State bit permanently since there is no support
+ * for unplugging the card nor detecting whether it is plugged. (If a
+ * platform exists in the future that supports it, via a GPIO for
+@@ -1071,8 +1111,9 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie)
+ * value is reserved for ports within the same silicon as Root Port
+ * which is not our case.
+ */
+- bridge->pcie_conf.slotcap = cpu_to_le32(FIELD_PREP(PCI_EXP_SLTCAP_PSN,
+- 1));
++ slotcap = PCI_EXP_SLTCAP_NCCS | PCI_EXP_SLTCAP_HPC |
++ FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1);
++ bridge->pcie_conf.slotcap = cpu_to_le32(slotcap);
+ bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS);
+
+ /* Indicates supports for Completion Retry Status */
+@@ -1587,6 +1628,24 @@ static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie)
+ irq_domain_remove(pcie->rp_irq_domain);
+ }
+
++static void advk_pcie_link_irq_handler(struct timer_list *timer)
++{
++ struct advk_pcie *pcie = from_timer(pcie, timer, link_irq_timer);
++ u16 slotctl;
++
++ slotctl = le16_to_cpu(pcie->bridge.pcie_conf.slotctl);
++ if (!(slotctl & PCI_EXP_SLTCTL_DLLSCE) ||
++ !(slotctl & PCI_EXP_SLTCTL_HPIE))
++ return;
++
++ /*
++ * Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ, so use PCIe
++ * interrupt 0
++ */
++ if (generic_handle_domain_irq(pcie->rp_irq_domain, 0) == -EINVAL)
++ dev_err_ratelimited(&pcie->pdev->dev, "unhandled HP IRQ\n");
++}
++
+ static void advk_pcie_handle_pme(struct advk_pcie *pcie)
+ {
+ u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16;
+@@ -1638,6 +1697,7 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie)
+ {
+ u32 isr0_val, isr0_mask, isr0_status;
+ u32 isr1_val, isr1_mask, isr1_status;
++ u16 slotsta;
+ int i;
+
+ isr0_val = advk_readl(pcie, PCIE_ISR0_REG);
+@@ -1664,6 +1724,26 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie)
+ dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n");
+ }
+
++ /* Process Link Down interrupt as HP IRQ */
++ if (isr0_status & PCIE_ISR0_LINK_DOWN) {
++ advk_writel(pcie, PCIE_ISR0_LINK_DOWN, PCIE_ISR0_REG);
++
++ dev_info(&pcie->pdev->dev, "link down\n");
++
++ pcie->link_was_up = false;
++
++ slotsta = le16_to_cpu(pcie->bridge.pcie_conf.slotsta);
++ slotsta |= PCI_EXP_SLTSTA_DLLSC;
++ pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta);
++
++ /*
++ * Deactivate timer and call advk_pcie_link_irq_handler()
++ * function directly as we are in the interrupt context.
++ */
++ del_timer_sync(&pcie->link_irq_timer);
++ advk_pcie_link_irq_handler(&pcie->link_irq_timer);
++ }
++
+ /* Process MSI interrupts */
+ if (isr0_status & PCIE_ISR0_MSI_INT_PENDING)
+ advk_pcie_handle_msi(pcie);
+@@ -1902,6 +1982,14 @@ static int advk_pcie_probe(struct platform_device *pdev)
+ if (ret)
+ return ret;
+
++ /*
++ * generic_handle_domain_irq() expects local IRQs to be disabled since
++ * normally it is called from interrupt context, so use TIMER_IRQSAFE
++ * flag for this link_irq_timer.
++ */
++ timer_setup(&pcie->link_irq_timer, advk_pcie_link_irq_handler,
++ TIMER_IRQSAFE);
++
+ advk_pcie_setup_hw(pcie);
+
+ ret = advk_sw_pci_bridge_init(pcie);
+@@ -1990,6 +2078,9 @@ static int advk_pcie_remove(struct platform_device *pdev)
+ advk_pcie_remove_msi_irq_domain(pcie);
+ advk_pcie_remove_irq_domain(pcie);
+
++ /* Deactivate link event timer */
++ del_timer_sync(&pcie->link_irq_timer);
++
+ /* Free config space for emulated root bridge */
+ pci_bridge_emul_cleanup(&pcie->bridge);
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0097-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch b/pkgs/patches-linux-5.15/0097-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch
new file mode 100644
index 0000000..fc39f95
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0097-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch
@@ -0,0 +1,148 @@
+From c60b0ae4a0a225d24993a7d8e8396209a745a7a0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Wed, 31 Aug 2022 15:57:01 +0200
+Subject: [PATCH 7/9] PCI: aardvark: Send Set_Slot_Power_Limit message
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Emulate Slot PowerLimit Scale and Value bits in the Slot Capabilities
+register of the emulated bridge and if slot power limit value is
+defined, send that Set_Slot_Power_Limit message via Message Generation
+Control Register in Link Up handler on link up event.
+
+Slot power limit value is read from device-tree property
+'slot-power-limit-milliwatt'. If this property is not specified, we
+treat it as "Slot Capabilities register has not yet been initialized".
+
+According to PCIe Base specification 3.0, when transitioning from a
+non-DL_Up Status to a DL_Up Status, the Port must initiate the
+transmission of a Set_Slot_Power_Limit Message to the other component
+on the Link to convey the value programmed in the Slot Power Limit
+Scale and Value fields of the Slot Capabilities register. This
+transmission is optional if the Slot Capabilities register has not
+yet been initialized.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 51 ++++++++++++++++++++++++---
+ 1 file changed, 47 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index 8aa05777da88..cf493d62b889 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -214,6 +214,11 @@ enum {
+ };
+
+ #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44)
++#define PME_MSG_GEN_CTRL (LMI_BASE_ADDR + 0x220)
++#define SEND_SET_SLOT_POWER_LIMIT BIT(13)
++#define SEND_PME_TURN_OFF BIT(14)
++#define SLOT_POWER_LIMIT_DATA_SHIFT 16
++#define SLOT_POWER_LIMIT_DATA_MASK GENMASK(25, 16)
+
+ /* PCIe core controller registers */
+ #define CTRL_CORE_BASE_ADDR 0x18000
+@@ -286,6 +291,8 @@ struct advk_pcie {
+ raw_spinlock_t msi_irq_lock;
+ DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
+ struct mutex msi_used_lock;
++ u8 slot_power_limit_value;
++ u8 slot_power_limit_scale;
+ int link_gen;
+ bool link_was_up;
+ struct timer_list link_irq_timer;
+@@ -318,8 +325,9 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie)
+ {
+ /* check if LTSSM is in normal operation - some L* state */
+ u8 ltssm_state = advk_pcie_ltssm_state(pcie);
++ u16 slotsta, slotctl;
++ u32 slotpwr, val;
+ bool link_is_up;
+- u16 slotsta;
+
+ link_is_up = ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED;
+
+@@ -333,6 +341,27 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie)
+ pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta);
+
+ mod_timer(&pcie->link_irq_timer, jiffies + 1);
++
++ /*
++ * According to PCIe Base specification 3.0, when transitioning
++ * from a non-DL_Up Status to a DL_Up Status, the Port must
++ * initiate the transmission of a Set_Slot_Power_Limit Message
++ * to the other component on the Link to convey the value
++ * programmed in the Slot Power Limit Scale and Value fields of
++ * the Slot Capabilities register. This transmission is optional
++ * if the Slot Capabilities register has not yet been
++ * initialized.
++ */
++ slotctl = le16_to_cpu(pcie->bridge.pcie_conf.slotctl);
++ slotpwr = FIELD_GET(PCI_EXP_SLTCAP_SPLV | PCI_EXP_SLTCAP_SPLS,
++ le32_to_cpu(pcie->bridge.pcie_conf.slotcap));
++ if (!(slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE) && slotpwr) {
++ val = advk_readl(pcie, PME_MSG_GEN_CTRL);
++ val &= ~SLOT_POWER_LIMIT_DATA_MASK;
++ val |= slotpwr << SLOT_POWER_LIMIT_DATA_SHIFT;
++ val |= SEND_SET_SLOT_POWER_LIMIT;
++ advk_writel(pcie, val, PME_MSG_GEN_CTRL);
++ }
+ }
+
+ return link_is_up;
+@@ -945,8 +974,9 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+
+ case PCI_EXP_SLTCTL: {
+ u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl);
+- /* Only emulation of HPIE and DLLSCE bits is provided */
+- slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE;
++ /* Only emulation of HPIE, DLLSCE and ASPLD bits is provided */
++ slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE |
++ PCI_EXP_SLTCTL_ASPL_DISABLE;
+ bridge->pcie_conf.slotctl = cpu_to_le16(slotctl);
+ break;
+ }
+@@ -1110,9 +1140,13 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie)
+ * Set physical slot number to 1 since there is only one port and zero
+ * value is reserved for ports within the same silicon as Root Port
+ * which is not our case.
++ *
++ * Set slot power limit.
+ */
+ slotcap = PCI_EXP_SLTCAP_NCCS | PCI_EXP_SLTCAP_HPC |
+- FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1);
++ FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1) |
++ FIELD_PREP(PCI_EXP_SLTCAP_SPLV, pcie->slot_power_limit_value) |
++ FIELD_PREP(PCI_EXP_SLTCAP_SPLS, pcie->slot_power_limit_scale);
+ bridge->pcie_conf.slotcap = cpu_to_le32(slotcap);
+ bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS);
+
+@@ -1858,6 +1892,7 @@ static int advk_pcie_probe(struct platform_device *pdev)
+ struct advk_pcie *pcie;
+ struct pci_host_bridge *bridge;
+ struct resource_entry *entry;
++ u32 slot_power_limit;
+ int ret, irq;
+
+ bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie));
+@@ -1978,6 +2013,14 @@ static int advk_pcie_probe(struct platform_device *pdev)
+ else
+ pcie->link_gen = ret;
+
++ slot_power_limit = of_pci_get_slot_power_limit(dev->of_node,
++ &pcie->slot_power_limit_value,
++ &pcie->slot_power_limit_scale);
++ if (slot_power_limit)
++ dev_info(dev, "Slot Power Limit: %u.%uW\n",
++ slot_power_limit / 1000,
++ (slot_power_limit / 100) % 10);
++
+ ret = advk_pcie_setup_phy(pcie);
+ if (ret)
+ return ret;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0098-PCI-aardvark-Add-clock-support.patch b/pkgs/patches-linux-5.15/0098-PCI-aardvark-Add-clock-support.patch
new file mode 100644
index 0000000..48d129d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0098-PCI-aardvark-Add-clock-support.patch
@@ -0,0 +1,93 @@
+From 336a4404b02b564930725715659ca583b8a58a6b Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Wed, 31 Aug 2022 15:59:39 +0200
+Subject: [PATCH 8/9] PCI: aardvark: Add clock support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The IP relies on a gated clock. When we will add S2RAM support, this
+clock will need to be resumed before any PCIe registers are
+accessed. Add support for this clock.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 32 +++++++++++++++++++++++++++
+ 1 file changed, 32 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index cf493d62b889..17aed65aab91 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -9,6 +9,7 @@
+ */
+
+ #include <linux/bitfield.h>
++#include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/interrupt.h>
+@@ -298,6 +299,7 @@ struct advk_pcie {
+ struct timer_list link_irq_timer;
+ struct pci_bridge_emul bridge;
+ struct gpio_desc *reset_gpio;
++ struct clk *clk;
+ struct phy *phy;
+ };
+
+@@ -1828,6 +1830,29 @@ static int advk_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+ return of_irq_parse_and_map_pci(dev, slot, pin);
+ }
+
++static int advk_pcie_setup_clk(struct advk_pcie *pcie)
++{
++ struct device *dev = &pcie->pdev->dev;
++ int ret;
++
++ pcie->clk = devm_clk_get(dev, NULL);
++ if (IS_ERR(pcie->clk) && (PTR_ERR(pcie->clk) == -EPROBE_DEFER))
++ return PTR_ERR(pcie->clk);
++
++ /* Old bindings miss the clock handle */
++ if (IS_ERR(pcie->clk)) {
++ dev_warn(dev, "Clock unavailable (%ld)\n", PTR_ERR(pcie->clk));
++ pcie->clk = NULL;
++ return 0;
++ }
++
++ ret = clk_prepare_enable(pcie->clk);
++ if (ret)
++ dev_err(dev, "Clock initialization failed (%d)\n", ret);
++
++ return ret;
++}
++
+ static void advk_pcie_disable_phy(struct advk_pcie *pcie)
+ {
+ phy_power_off(pcie->phy);
+@@ -2021,6 +2046,10 @@ static int advk_pcie_probe(struct platform_device *pdev)
+ slot_power_limit / 1000,
+ (slot_power_limit / 100) % 10);
+
++ ret = advk_pcie_setup_clk(pcie);
++ if (ret)
++ return ret;
++
+ ret = advk_pcie_setup_phy(pcie);
+ if (ret)
+ return ret;
+@@ -2143,6 +2172,9 @@ static int advk_pcie_remove(struct platform_device *pdev)
+ /* Disable phy */
+ advk_pcie_disable_phy(pcie);
+
++ /* Disable clock */
++ clk_disable_unprepare(pcie->clk);
++
+ return 0;
+ }
+
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0099-PCI-aardvark-Add-suspend-to-RAM-support.patch b/pkgs/patches-linux-5.15/0099-PCI-aardvark-Add-suspend-to-RAM-support.patch
new file mode 100644
index 0000000..c51dd8b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0099-PCI-aardvark-Add-suspend-to-RAM-support.patch
@@ -0,0 +1,74 @@
+From 4cfecad11f7a6cebb77a37b7bbd42d7f910cce80 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Wed, 31 Aug 2022 16:07:27 +0200
+Subject: [PATCH 9/9] PCI: aardvark: Add suspend to RAM support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add suspend and resume callbacks. We need to use the NOIRQ variants to
+ensure the controller's IRQ handlers are not run during suspend() /
+resume(), which could cause races.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 34 +++++++++++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index 17aed65aab91..f3984bdf1d96 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1911,6 +1911,39 @@ static int advk_pcie_setup_phy(struct advk_pcie *pcie)
+ return ret;
+ }
+
++static int advk_pcie_suspend(struct device *dev)
++{
++ struct advk_pcie *pcie = dev_get_drvdata(dev);
++
++ advk_pcie_disable_phy(pcie);
++
++ clk_disable_unprepare(pcie->clk);
++
++ return 0;
++}
++
++static int advk_pcie_resume(struct device *dev)
++{
++ struct advk_pcie *pcie = dev_get_drvdata(dev);
++ int ret;
++
++ ret = clk_prepare_enable(pcie->clk);
++ if (ret)
++ return ret;
++
++ ret = advk_pcie_enable_phy(pcie);
++ if (ret)
++ return ret;
++
++ advk_pcie_setup_hw(pcie);
++
++ return 0;
++}
++
++static const struct dev_pm_ops advk_pcie_dev_pm_ops = {
++ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(advk_pcie_suspend, advk_pcie_resume)
++};
++
+ static int advk_pcie_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -2188,6 +2221,7 @@ static struct platform_driver advk_pcie_driver = {
+ .driver = {
+ .name = "advk-pcie",
+ .of_match_table = advk_pcie_of_match_table,
++ .pm = &advk_pcie_dev_pm_ops,
+ },
+ .probe = advk_pcie_probe,
+ .remove = advk_pcie_remove,
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0100-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch b/pkgs/patches-linux-5.15/0100-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch
new file mode 100644
index 0000000..f161bff
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0100-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch
@@ -0,0 +1,51 @@
+From f589f5a4a08608cf0fc5184b82e1404250632530 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 1 Sep 2022 11:32:28 +0200
+Subject: [PATCH 1/2] PCI: aardvark: Replace custom PCIE_CORE_ERR_CAPCTL_*
+ macros by linux/pci_regs.h macros
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Kernel already has these macros defined under different names.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 13 +++----------
+ 1 file changed, 3 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index defaf74935a3..a5b1ebfb9520 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -38,11 +38,6 @@
+ #define PCIE_CORE_SSDEV_ID_REG 0x2c
+ #define PCIE_CORE_PCIEXP_CAP 0xc0
+ #define PCIE_CORE_PCIERR_CAP 0x100
+-#define PCIE_CORE_ERR_CAPCTL_REG 0x118
+-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5)
+-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6)
+-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7)
+-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8)
+ /* PIO registers base address and register offsets */
+ #define PIO_BASE_ADDR 0x4000
+ #define PIO_CTRL (PIO_BASE_ADDR + 0x0)
+@@ -592,11 +587,9 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
+ advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG);
+
+ /* Set Advanced Error Capabilities and Control PF0 register */
+- reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX |
+- PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN |
+- PCIE_CORE_ERR_CAPCTL_ECRC_CHCK |
+- PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV;
+- advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG);
++ reg = PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_GENE |
++ PCI_ERR_CAP_ECRC_CHKC | PCI_ERR_CAP_ECRC_CHKE;
++ advk_writel(pcie, reg, PCIE_CORE_PCIERR_CAP + PCI_ERR_CAP);
+
+ /* Set PCIe Device Control register */
+ reg = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + PCI_EXP_DEVCTL);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/0101-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch b/pkgs/patches-linux-5.15/0101-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch
new file mode 100644
index 0000000..edce284
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0101-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch
@@ -0,0 +1,39 @@
+From 055c623c268ee0031d05c1346f9f4c24fe3f0846 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Thu, 18 Aug 2022 15:51:38 +0200
+Subject: [PATCH 2/2] PCI: aardvark: Don't write read-only bits explicitly in
+ PCI_ERR_CAP register
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The bits PCI_ERR_CAP_ECRC_GENC and PCI_ERR_CAP_ECRC_CHKC are read only,
+reporting the capability of ECRC. Don't write them explicitly, instead
+read the register (where they are set), and add the bits that enable
+these features.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index a5b1ebfb9520..325c22092e16 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -586,9 +586,9 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
+ reg &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG);
+
+- /* Set Advanced Error Capabilities and Control PF0 register */
+- reg = PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_GENE |
+- PCI_ERR_CAP_ECRC_CHKC | PCI_ERR_CAP_ECRC_CHKE;
++ /* Enable generation and checking of ECRC on Root Bridge */
++ reg = advk_readl(pcie, PCIE_CORE_PCIERR_CAP + PCI_ERR_CAP);
++ reg |= PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE;
+ advk_writel(pcie, reg, PCIE_CORE_PCIERR_CAP + PCI_ERR_CAP);
+
+ /* Set PCIe Device Control register */
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/011-kbuild-export-SUBARCH.patch b/pkgs/patches-linux-5.15/011-kbuild-export-SUBARCH.patch
new file mode 100644
index 0000000..0aedad4
--- /dev/null
+++ b/pkgs/patches-linux-5.15/011-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
+@@ -523,7 +523,7 @@ KBUILD_LDFLAGS_MODULE :=
+ KBUILD_LDFLAGS :=
+ CLANG_FLAGS :=
+
+-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
++export ARCH SRCARCH SUBARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
+ export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL
+ export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
+ export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD
diff --git a/pkgs/patches-linux-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch b/pkgs/patches-linux-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch
new file mode 100644
index 0000000..75f6372
--- /dev/null
+++ b/pkgs/patches-linux-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch
@@ -0,0 +1,28 @@
+From d8d1a9a77863a8c7031ae82a1d461aa78eb72a7b Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Mon, 11 Oct 2021 14:12:43 -0500
+Subject: [PATCH] checks: Drop interrupt provider '#address-cells' check
+
+'#address-cells' is only needed when parsing 'interrupt-map' properties, so
+remove it from the common interrupt-provider test.
+
+Cc: Andre Przywara <andre.przywara@arm.com>
+Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
+Signed-off-by: Rob Herring <robh@kernel.org>
+Message-Id: <20211011191245.1009682-3-robh@kernel.org>
+Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
+---
+--- a/scripts/dtc/checks.c
++++ b/scripts/dtc/checks.c
+@@ -1569,11 +1569,6 @@ static void check_interrupt_provider(str
+ if (!prop)
+ FAIL(c, dti, node,
+ "Missing #interrupt-cells in interrupt provider");
+-
+- prop = get_property(node, "#address-cells");
+- if (!prop)
+- FAIL(c, dti, node,
+- "Missing #address-cells in interrupt provider");
+ }
+ WARNING(interrupt_provider, check_interrupt_provider, NULL);
+
diff --git a/pkgs/patches-linux-5.15/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch b/pkgs/patches-linux-5.15/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch
new file mode 100644
index 0000000..82feb74
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch b/pkgs/patches-linux-5.15/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch
new file mode 100644
index 0000000..3a4d573
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch b/pkgs/patches-linux-5.15/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch
new file mode 100644
index 0000000..7980659
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch b/pkgs/patches-linux-5.15/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch
new file mode 100644
index 0000000..38b46c0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch b/pkgs/patches-linux-5.15/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch
new file mode 100644
index 0000000..63553eb
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch b/pkgs/patches-linux-5.15/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch
new file mode 100644
index 0000000..41b3d81
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -3430,6 +3430,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/pkgs/patches-linux-5.15/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch b/pkgs/patches-linux-5.15/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch
new file mode 100644
index 0000000..e25c336
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch b/pkgs/patches-linux-5.15/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch
new file mode 100644
index 0000000..e4c0833
--- /dev/null
+++ b/pkgs/patches-linux-5.15/081-net-next-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/pkgs/patches-linux-5.15/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch b/pkgs/patches-linux-5.15/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch
new file mode 100644
index 0000000..135b691
--- /dev/null
+++ b/pkgs/patches-linux-5.15/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch
@@ -0,0 +1,43 @@
+From 81c0004a6433ff90fa6129418802c3c367e453c2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 4 Jul 2022 13:36:21 +0200
+Subject: [PATCH 1/5] ARM: dts: turris-omnia: configure LED[0] pin function to
+ link/activity
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The marvell PHY driver changes the LED[0] pin function to "On - 1000
+Mbps Link, Off - Else".
+
+Turris Omnia expects that the function is "On - Link, Blink - Activity,
+Off - No link".
+
+Use the `marvell,reg-init` DT property to change the function.
+
+In the future, once netdev trigger will support HW offloading, we will
+be able to have this configured via the combination of PHY driver and
+leds-turris-omnia driver.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ arch/arm/boot/dts/armada-385-turris-omnia.dts | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts
+index 5bd6a66d2c2b..1c65de55a17b 100644
+--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts
++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts
+@@ -390,7 +390,8 @@ &mdio {
+ phy1: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+- marvell,reg-init = <3 18 0 0x4985>;
++ marvell,reg-init = <3 18 0 0x4985>,
++ <3 16 0xfff0 0x0001>;
+
+ /* irq is connected to &pcawan pin 7 */
+ };
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/100-aardvark-workaround-PCIe.patch b/pkgs/patches-linux-5.15/100-aardvark-workaround-PCIe.patch
new file mode 100644
index 0000000..39a7934
--- /dev/null
+++ b/pkgs/patches-linux-5.15/100-aardvark-workaround-PCIe.patch
@@ -0,0 +1,81 @@
+From b004eff0818f657f66a6e1dc63aca0ffd9b1fb2f Mon Sep 17 00:00:00 2001
+From: Pali Rohár <pali@kernel.org>
+Date: Wed, 31 Aug 2022 19:44:08 +0200
+Subject: [PATCH] PCI: aardvark: Implement workaround for PCIe Completion
+ Timeout
+
+Marvell Armada 3700 Functional Errata, Guidelines, and Restrictions
+document describes in erratum 3.12 PCIe Completion Timeout (Ref #: 251),
+that PCIe IP does not support a strong-ordered model for inbound posted vs.
+outbound completion.
+
+As a workaround for this erratum, DIS_ORD_CHK flag in Debug Mux Control
+register must be set. It disables the ordering check in the core between
+Completions and Posted requests received from the link.
+
+Marvell also suggests to do full memory barrier at the beginning of
+aardvark summary interrupt handler before calling interrupt handlers of
+endpoint drivers in order to minimize the risk for the race condition
+documented in the Erratum between the DMA done status reading and the
+completion of writing to the host memory.
+
+More details about this issue and suggested workarounds are in discussion:
+https://lore.kernel.org/linux-pci/BN9PR18MB425154FE5019DCAF2028A1D5DB8D9@BN9PR18MB4251.namprd18.prod.outlook.com/t/#u
+
+It was reported that enabling this workaround fixes instability issues and
+"Unhandled fault" errors when using 60 GHz WiFi 802.11ad card with Qualcomm
+QCA6335 chip under significant load which were caused by interrupt status
+stuck in the outbound CMPLT queue traced back to this erratum.
+
+This workaround fixes also kernel panic triggered after some minutes of
+usage 5 GHz WiFi 802.11ax card with Mediatek MT7915 chip:
+
+ Internal error: synchronous external abort: 96000210 [#1] SMP
+ Kernel panic - not syncing: Fatal exception in interrupt
+
+Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver")
+Cc: stable@vger.kernel.org
+---
+ drivers/pci/controller/pci-aardvark.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index f3984bdf1d96..defaf74935a3 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -215,6 +215,8 @@ enum {
+ };
+
+ #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44)
++#define DEBUG_MUX_CTRL_REG (LMI_BASE_ADDR + 0x208)
++#define DIS_ORD_CHK BIT(30)
+ #define PME_MSG_GEN_CTRL (LMI_BASE_ADDR + 0x220)
+ #define SEND_SET_SLOT_POWER_LIMIT BIT(13)
+ #define SEND_PME_TURN_OFF BIT(14)
+@@ -611,6 +613,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
+ PCIE_CORE_CTRL2_TD_ENABLE;
+ advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
+
++ /* Disable ordering checks, workaround for erratum 3.12 "PCIe completion timeout" */
++ reg = advk_readl(pcie, DEBUG_MUX_CTRL_REG);
++ reg |= DIS_ORD_CHK;
++ advk_writel(pcie, reg, DEBUG_MUX_CTRL_REG);
++
+ /* Set lane X1 */
+ reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
+ reg &= ~LANE_CNT_MSK;
+@@ -1803,6 +1810,9 @@ static irqreturn_t advk_pcie_irq_handler(int irq, void *arg)
+ struct advk_pcie *pcie = arg;
+ u32 status;
+
++ /* Full memory barrier (ARM dsb sy), workaround for erratum 3.12 "PCIe completion timeout" */
++ mb();
++
+ status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG);
+ if (!(status & PCIE_IRQ_CORE_INT))
+ return IRQ_NONE;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch b/pkgs/patches-linux-5.15/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch
new file mode 100644
index 0000000..22f52c1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch b/pkgs/patches-linux-5.15/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch
new file mode 100644
index 0000000..7de3cbb
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch b/pkgs/patches-linux-5.15/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch
new file mode 100644
index 0000000..626a766
--- /dev/null
+++ b/pkgs/patches-linux-5.15/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch
@@ -0,0 +1,53 @@
+From fed7cef5e4f2df8c6a79bebf5da1fdd3783ff6f3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 4 Jul 2022 13:36:22 +0200
+Subject: [PATCH] ARM: dts: turris-omnia: enable LED controller node
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The LED controller node is disabled because the leds-turris-omnia driver
+does not support setting the LED blinking to be controlled by the MCU.
+
+The patches for that have now been sent [1], so let's enable the node.
+
+[1] https://lore.kernel.org/linux-leds/20220704105955.15474-1-kabel@kernel.org/T/
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ arch/arm/boot/dts/armada-385-turris-omnia.dts | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts
+index 1c65de55a17b..1e7d6e63c58d 100644
+--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts
++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts
+@@ -188,15 +188,13 @@ led-controller@2b {
+ reg = <0x2b>;
+ #address-cells = <1>;
+ #size-cells = <0>;
++ status = "okay";
+
+ /*
+ * LEDs are controlled by MCU (STM32F0) at
+ * address 0x2b.
+ *
+- * The driver does not support HW control mode
+- * for the LEDs yet. Disable the LEDs for now.
+- *
+- * Also LED functions are not stable yet:
++ * LED functions are not stable yet:
+ * - there are 3 LEDs connected via MCU to PCIe
+ * ports. One of these ports supports mSATA.
+ * There is no mSATA nor PCIe function.
+@@ -207,7 +205,6 @@ led-controller@2b {
+ * B. Again there is no such function defined.
+ * For now we use LED_FUNCTION_INDICATOR
+ */
+- status = "disabled";
+
+ multi-led@0 {
+ reg = <0x0>;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch b/pkgs/patches-linux-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch
new file mode 100644
index 0000000..eefbe4b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch
@@ -0,0 +1,26 @@
+From d3c5b26768dbe990c4e1bd79e420c11ce7491d51 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 11:36:00 +0200
+Subject: [PATCH] swab: use stddefs.h instead of compiler.h
+
+Fix an issue with kernel headers that broke perf.
+
+---
+ include/uapi/linux/swab.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/uapi/linux/swab.h b/include/uapi/linux/swab.h
+index 7272f85d6d6a..3736f2fe1541 100644
+--- a/include/uapi/linux/swab.h
++++ b/include/uapi/linux/swab.h
+@@ -3,7 +3,7 @@
+ #define _UAPI_LINUX_SWAB_H
+
+ #include <linux/types.h>
+-#include <linux/compiler.h>
++#include <linux/stddef.h>
+ #include <asm/bitsperlong.h>
+ #include <asm/swab.h>
+
+--
+
diff --git a/pkgs/patches-linux-5.15/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch b/pkgs/patches-linux-5.15/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch
new file mode 100644
index 0000000..95a9656
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch b/pkgs/patches-linux-5.15/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch
new file mode 100644
index 0000000..05520a8
--- /dev/null
+++ b/pkgs/patches-linux-5.15/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch
@@ -0,0 +1,125 @@
+From 80e643510cb14f116f687e992210c0008a09d869 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 4 Jul 2022 12:59:53 +0200
+Subject: [PATCH] leds: turris-omnia: support HW controlled mode via
+ private trigger
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add support for enabling MCU controlled mode of the Turris Omnia LEDs
+via a LED private trigger called "omnia-mcu".
+
+When in MCU controlled mode, the user can still set LED color, but the
+blinking is done by MCU, which does different things for various LEDs:
+- WAN LED is blinked according to the LED[0] pin of the WAN PHY
+- LAN LEDs are blinked according to the LED[0] output of corresponding
+ port of the LAN switch
+- PCIe LEDs are blinked according to the logical OR of the MiniPCIe port
+ LED pins
+
+For a long time I wanted to actually do this differently: I wanted to
+make the netdev trigger to transparently offload the blinking to the HW
+if user set compatible settings for the netdev trigger.
+There was some work on this, and hopefully we will be able to complete
+it sometime, but since there are various complications, it will probably
+not be soon.
+
+In the meantime let's support HW controlled mode via this private LED
+trigger. If, in the future, we manage to complete the netdev trigger
+offloading, we can still keep this private trigger for backwards
+compatiblity, if needed.
+
+We also set "omnia-mcu" to cdev->default_trigger, so that the MCU keeps
+control until the user first wants to take over it. If a different
+default trigger is specified in device-tree via the
+`linux,default-trigger` property, LED class will overwrite
+cdev->default_trigger, and so the DT property will be respected.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/leds/Kconfig | 1 +
+ drivers/leds/leds-turris-omnia.c | 41 ++++++++++++++++++++++++++++++++
+ 2 files changed, 42 insertions(+)
+
+diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
+index ed800f5da7d8..52f010b8f58e 100644
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -163,6 +163,7 @@ config LEDS_TURRIS_OMNIA
+ depends on I2C
+ depends on MACH_ARMADA_38X || COMPILE_TEST
+ depends on OF
++ select LEDS_TRIGGERS
+ help
+ This option enables basic support for the LEDs found on the front
+ side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs on the
+diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c
+index 1adfed1c0619..c2dfb22d3065 100644
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -41,6 +41,39 @@ struct omnia_leds {
+ struct omnia_led leds[];
+ };
+
++static struct led_hw_trigger_type omnia_hw_trigger_type;
++
++static int omnia_hwtrig_activate(struct led_classdev *cdev)
++{
++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
++ struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev));
++
++ /* put the LED into MCU controlled mode */
++ return i2c_smbus_write_byte_data(leds->client, CMD_LED_MODE,
++ CMD_LED_MODE_LED(led->reg));
++}
++
++static void omnia_hwtrig_deactivate(struct led_classdev *cdev)
++{
++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
++ struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev));
++ int ret;
++
++ /* put the LED into software mode */
++ ret = i2c_smbus_write_byte_data(leds->client, CMD_LED_MODE,
++ CMD_LED_MODE_LED(led->reg) |
++ CMD_LED_MODE_USER);
++ if (ret < 0)
++ dev_err(cdev->dev, "Cannot put to software mode: %i\n", ret);
++}
++
++static struct led_trigger omnia_hw_trigger = {
++ .name = "omnia-mcu",
++ .activate = omnia_hwtrig_activate,
++ .deactivate = omnia_hwtrig_deactivate,
++ .trigger_type = &omnia_hw_trigger_type,
++};
++
+ static int omnia_led_brightness_set_blocking(struct led_classdev *cdev,
+ enum led_brightness brightness)
+ {
+@@ -112,6 +145,8 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led,
+ cdev = &led->mc_cdev.led_cdev;
+ cdev->max_brightness = 255;
+ cdev->brightness_set_blocking = omnia_led_brightness_set_blocking;
++ cdev->trigger_type = &omnia_hw_trigger_type;
++ cdev->default_trigger = omnia_hw_trigger.name;
+
+ /* put the LED into software mode */
+ ret = i2c_smbus_write_byte_data(client, CMD_LED_MODE,
+@@ -228,6 +263,12 @@ static int omnia_leds_probe(struct i2c_client *client,
+
+ mutex_init(&leds->lock);
+
++ ret = devm_led_trigger_register(dev, &omnia_hw_trigger);
++ if (ret < 0) {
++ dev_err(dev, "Cannot register private LED trigger: %d\n", ret);
++ return ret;
++ }
++
+ led = &leds->leds[0];
+ for_each_available_child_of_node(np, child) {
+ ret = omnia_led_register(client, led, child);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch b/pkgs/patches-linux-5.15/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch
new file mode 100644
index 0000000..88b85ac
--- /dev/null
+++ b/pkgs/patches-linux-5.15/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch
@@ -0,0 +1,38 @@
+From bda176cceb735b9b46c1900658b6486c34e13ae6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 4 Jul 2022 12:59:54 +0200
+Subject: [PATCH] leds: turris-omnia: initialize multi-intensity to full
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The default color of each LED before driver probe (255, 255, 255).
+Initialize multi_intensity to this value, so that it corresponds to the
+reality.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/leds/leds-turris-omnia.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c
+index c2dfb22d3065..fae155bd119c 100644
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -131,10 +131,13 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led,
+ }
+
+ led->subled_info[0].color_index = LED_COLOR_ID_RED;
++ led->subled_info[0].intensity = 255;
+ led->subled_info[0].channel = 0;
+ led->subled_info[1].color_index = LED_COLOR_ID_GREEN;
++ led->subled_info[1].intensity = 255;
+ led->subled_info[1].channel = 1;
+ led->subled_info[2].color_index = LED_COLOR_ID_BLUE;
++ led->subled_info[2].intensity = 255;
+ led->subled_info[2].channel = 2;
+
+ led->mc_cdev.subled_info = led->subled_info;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch b/pkgs/patches-linux-5.15/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch
new file mode 100644
index 0000000..b99c0dc
--- /dev/null
+++ b/pkgs/patches-linux-5.15/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch
@@ -0,0 +1,36 @@
+From 349cbe949b9622cc05b148ecfa6268cbbae35b45 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 4 Jul 2022 12:59:55 +0200
+Subject: [PATCH] leds: turris-omnia: change max brightness from 255 to 1
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Using binary brightness makes more sense for this controller, because
+internally in the MCU it works that way: the LED has a color, and a
+state whether it is ON or OFF.
+
+The resulting brightness computation with led_mc_calc_color_components()
+will now always result in either (0, 0, 0) or the multi_intensity value.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/leds/leds-turris-omnia.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c
+index fae155bd119c..f53bdc3f4cea 100644
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -146,7 +146,7 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led,
+ init_data.fwnode = &np->fwnode;
+
+ cdev = &led->mc_cdev.led_cdev;
+- cdev->max_brightness = 255;
++ cdev->max_brightness = 1;
+ cdev->brightness_set_blocking = omnia_led_brightness_set_blocking;
+ cdev->trigger_type = &omnia_hw_trigger_type;
+ cdev->default_trigger = omnia_hw_trigger.name;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/110-net-sfp-move-quirk-handling-into-sfp.c.patch b/pkgs/patches-linux-5.15/110-net-sfp-move-quirk-handling-into-sfp.c.patch
new file mode 100644
index 0000000..fa74804
--- /dev/null
+++ b/pkgs/patches-linux-5.15/110-net-sfp-move-quirk-handling-into-sfp.c.patch
@@ -0,0 +1,299 @@
+From cb3a4e33a0415f84736734e77fb2d6c1a5fab1d7 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 26 Aug 2022 08:43:30 +0100
+Subject: [PATCH 1/4] 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>
+---
+ 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(-)
+
+diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
+index 4369d6249e7b..267182d32bd5 100644
+--- 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);
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+index 028a5df5c538..7be9eb47c40f 100644
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -252,6 +252,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;
+@@ -308,6 +310,93 @@ static const struct of_device_id sfp_of_match[] = {
+ };
+ 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)
+@@ -1952,6 +2041,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
+ else
+ sfp->tx_fault_ignore = false;
+
++ sfp->quirk = sfp_lookup_quirk(&id);
++
+ return 0;
+ }
+
+@@ -2063,7 +2154,8 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event)
+ 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;
+diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h
+index 27226535c72b..03f1d47fe6ca 100644
+--- 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, struct phy_device *phydev);
+ 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);
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/111-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch b/pkgs/patches-linux-5.15/111-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch
new file mode 100644
index 0000000..56716ca
--- /dev/null
+++ b/pkgs/patches-linux-5.15/111-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch
@@ -0,0 +1,75 @@
+From d189313ed65a21a425889664abc5e7c4646a00ba Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 26 Aug 2022 08:43:35 +0100
+Subject: [PATCH 2/4] 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>
+---
+ drivers/net/phy/sfp.c | 14 +++++++++-----
+ drivers/net/phy/sfp.h | 1 +
+ 2 files changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+index 7be9eb47c40f..900b10887c3f 100644
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -310,6 +310,11 @@ static const struct of_device_id sfp_of_match[] = {
+ };
+ 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)
+ {
+@@ -340,6 +345,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
+@@ -2029,11 +2035,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
+ if (ret < 0)
+ return ret;
+
+- 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))
+@@ -2042,6 +2044,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
+ sfp->tx_fault_ignore = false;
+
+ sfp->quirk = sfp_lookup_quirk(&id);
++ if (sfp->quirk && sfp->quirk->fixup)
++ sfp->quirk->fixup(sfp);
+
+ return 0;
+ }
+diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h
+index 03f1d47fe6ca..7ad06deae76c 100644
+--- 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 {
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/112-net-sfp-move-Huawei-MA5671A-fixup.patch b/pkgs/patches-linux-5.15/112-net-sfp-move-Huawei-MA5671A-fixup.patch
new file mode 100644
index 0000000..416f3a3
--- /dev/null
+++ b/pkgs/patches-linux-5.15/112-net-sfp-move-Huawei-MA5671A-fixup.patch
@@ -0,0 +1,52 @@
+From a9e957e4da9f28797663b162e0bdd0147ace7853 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 26 Aug 2022 08:43:40 +0100
+Subject: [PATCH 3/4] 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>
+---
+ drivers/net/phy/sfp.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+index 900b10887c3f..db922d27ee79 100644
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -315,6 +315,11 @@ static void sfp_fixup_long_startup(struct sfp *sfp)
+ 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)
+ {
+@@ -352,6 +357,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
+@@ -2037,11 +2043,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
+
+ 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)
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/113-net-sfp-add-support-for-HALNy-GPON-SFP.patch b/pkgs/patches-linux-5.15/113-net-sfp-add-support-for-HALNy-GPON-SFP.patch
new file mode 100644
index 0000000..e66bcb7
--- /dev/null
+++ b/pkgs/patches-linux-5.15/113-net-sfp-add-support-for-HALNy-GPON-SFP.patch
@@ -0,0 +1,93 @@
+From 9490075c53cec3925039d2b1c5afc4507e772660 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 26 Aug 2022 08:43:45 +0100
+Subject: [PATCH 4/4] 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 possibly uses TX_FAULT as an inverted
+host-link status signal. As we can't be certain about the modules
+use of TX_FAULT, we ignore it.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/sfp-bus.c | 2 +-
+ drivers/net/phy/sfp.c | 29 ++++++++++++++++++++++++++---
+ 2 files changed, 27 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
+index 267182d32bd5..4566348a6d05 100644
+--- 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 *bus, const struct sfp_eeprom_id *id,
+ phylink_set(modes, 2500baseX_Full);
+ }
+
+- if (bus->sfp_quirk)
++ if (bus->sfp_quirk && bus->sfp_quirk->modes)
+ bus->sfp_quirk->modes(id, modes);
+
+ bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS);
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+index db922d27ee79..2ebc577bfc56 100644
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -320,6 +320,23 @@ static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
+ sfp->tx_fault_ignore = true;
+ }
+
++static void sfp_fixup_inverted_los(struct sfp *sfp)
++{
++ const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
++ const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
++
++ sfp->id.ext.options &= ~los_normal;
++ sfp->id.ext.options |= los_inverted;
++}
++
++static void sfp_fixup_hanly_gsfp(struct sfp *sfp)
++{
++ /* LOS is inverted */
++ sfp_fixup_inverted_los(sfp);
++ /* TX fault might be inverted, but we don't know for certain. */
++ sfp_fixup_ignore_tx_fault(sfp);
++}
++
+ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
+ unsigned long *modes)
+ {
+@@ -351,6 +368,10 @@ static const struct sfp_quirk sfp_quirks[] = {
+ .part = "3FE46541AA",
+ .modes = sfp_quirk_2500basex,
+ .fixup = sfp_fixup_long_startup,
++ }, {
++ .vendor = "HANLy",
++ .part = "HL-GSFP",
++ .fixup = sfp_fixup_hanly_gsfp,
+ }, {
+ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
+ // NRZ in their EEPROM
+@@ -368,16 +389,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;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/114-fixup-net-sfp-add-support-for-HALNy-GPON-SFP.patch b/pkgs/patches-linux-5.15/114-fixup-net-sfp-add-support-for-HALNy-GPON-SFP.patch
new file mode 100644
index 0000000..99ca247
--- /dev/null
+++ b/pkgs/patches-linux-5.15/114-fixup-net-sfp-add-support-for-HALNy-GPON-SFP.patch
@@ -0,0 +1,42 @@
+From 6dc810804bb2fa91d9972601c209471fab852509 Mon Sep 17 00:00:00 2001
+From: Josef Schlehofer <pepe.schlehofer@gmail.com>
+Date: Fri, 26 Aug 2022 20:50:51 +0200
+Subject: [PATCH] fixup! net: sfp: add support for HALNy GPON SFP
+
+There are a few typos, which was reported by Orest Worhacz
+(@AreYouLoco), which needs to be fixed.
+
+SFP is detected as
+[ 9.607535] sfp sfp: module HALNy HL-GSFP rev V1.0 sn HALN1010493c dc 20150525
+---
+ drivers/net/phy/sfp.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+index 2ebc577bfc56..277292e2ed35 100644
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -329,7 +329,7 @@ static void sfp_fixup_inverted_los(struct sfp *sfp)
+ sfp->id.ext.options |= los_inverted;
+ }
+
+-static void sfp_fixup_hanly_gsfp(struct sfp *sfp)
++static void sfp_fixup_halny_gsfp(struct sfp *sfp)
+ {
+ /* LOS is inverted */
+ sfp_fixup_inverted_los(sfp);
+@@ -369,9 +369,9 @@ static const struct sfp_quirk sfp_quirks[] = {
+ .modes = sfp_quirk_2500basex,
+ .fixup = sfp_fixup_long_startup,
+ }, {
+- .vendor = "HANLy",
++ .vendor = "HALNy",
+ .part = "HL-GSFP",
+- .fixup = sfp_fixup_hanly_gsfp,
++ .fixup = sfp_fixup_halny_gsfp,
+ }, {
+ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
+ // NRZ in their EEPROM
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/115-net-sfp-redo-soft-state-polling.patch b/pkgs/patches-linux-5.15/115-net-sfp-redo-soft-state-polling.patch
new file mode 100644
index 0000000..8b7adea
--- /dev/null
+++ b/pkgs/patches-linux-5.15/115-net-sfp-redo-soft-state-polling.patch
@@ -0,0 +1,84 @@
+From f2721c55176d31ff46d06bcfa512b97ada701ef7 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 26 Aug 2022 08:48:20 +0100
+Subject: [PATCH 1/2] net: sfp: redo soft state polling
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/sfp.c | 35 ++++++++++++++++++++++++-----------
+ 1 file changed, 24 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+index 277292e2ed35..8db84e0dbc04 100644
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -234,6 +234,7 @@ struct sfp {
+ bool need_poll;
+
+ struct mutex st_mutex; /* Protects state */
++ unsigned int state_ignore_hw_mask;
+ unsigned int state_soft_mask;
+ unsigned int state;
+ struct delayed_work poll;
+@@ -623,17 +624,18 @@ static void sfp_soft_set_state(struct sfp *sfp, unsigned int state)
+ 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_ignore_hw_mask & mask;
+
+ if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
+ !sfp->need_poll)
+@@ -647,10 +649,12 @@ static void sfp_soft_stop_poll(struct sfp *sfp)
+
+ static unsigned int sfp_get_state(struct sfp *sfp)
+ {
++ unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT);
+ unsigned int state = sfp->get_state(sfp);
+
+- if (state & SFP_F_PRESENT &&
+- sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT))
++ state &= ~sfp->state_ignore_hw_mask;
++
++ if (state & SFP_F_PRESENT && soft)
+ state |= sfp_soft_get_state(sfp);
+
+ return state;
+@@ -2064,6 +2068,15 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
+ if (ret < 0)
+ return ret;
+
++ /* Initialise state bits to ignore from hardware */
++ sfp->state_ignore_hw_mask = 0;
++ if (!sfp->gpio[GPIO_TX_DISABLE])
++ sfp->state_ignore_hw_mask |= SFP_F_TX_DISABLE;
++ if (!sfp->gpio[GPIO_TX_FAULT])
++ sfp->state_ignore_hw_mask |= SFP_F_TX_FAULT;
++ if (!sfp->gpio[GPIO_LOS])
++ sfp->state_ignore_hw_mask |= SFP_F_LOS;
++
+ sfp->module_t_start_up = T_START_UP;
+
+ sfp->tx_fault_ignore = false;
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/116-net-sfp-change-HALNy-to-ignore-hardware-pins.patch b/pkgs/patches-linux-5.15/116-net-sfp-change-HALNy-to-ignore-hardware-pins.patch
new file mode 100644
index 0000000..11b3e31
--- /dev/null
+++ b/pkgs/patches-linux-5.15/116-net-sfp-change-HALNy-to-ignore-hardware-pins.patch
@@ -0,0 +1,40 @@
+From 8852f9aa3c1ae0c58a64b8517f11ae62309b84ad Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 26 Aug 2022 08:48:25 +0100
+Subject: [PATCH 2/2] net: sfp: change HALNy to ignore hardware pins
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/sfp.c | 14 +-------------
+ 1 file changed, 1 insertion(+), 13 deletions(-)
+
+diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
+index 8db84e0dbc04..1df5d5008ab2 100644
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -321,21 +321,9 @@ static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
+ sfp->tx_fault_ignore = true;
+ }
+
+-static void sfp_fixup_inverted_los(struct sfp *sfp)
+-{
+- const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
+- const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
+-
+- sfp->id.ext.options &= ~los_normal;
+- sfp->id.ext.options |= los_inverted;
+-}
+-
+ static void sfp_fixup_halny_gsfp(struct sfp *sfp)
+ {
+- /* LOS is inverted */
+- sfp_fixup_inverted_los(sfp);
+- /* TX fault might be inverted, but we don't know for certain. */
+- sfp_fixup_ignore_tx_fault(sfp);
++ sfp->state_ignore_hw_mask |= SFP_F_TX_FAULT | SFP_F_LOS;
+ }
+
+ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
+--
+2.34.1
+
diff --git a/pkgs/patches-linux-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch b/pkgs/patches-linux-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch
new file mode 100644
index 0000000..30c70a6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -7552,7 +7552,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/pkgs/patches-linux-5.15/130-add-linux-spidev-compatible-si3210.patch b/pkgs/patches-linux-5.15/130-add-linux-spidev-compatible-si3210.patch
new file mode 100644
index 0000000..d260cf1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/130-add-linux-spidev-compatible-si3210.patch
@@ -0,0 +1,18 @@
+From: Giuseppe Lippolis <giu.lippolis@gmail.com>
+Subject: Add the linux,spidev compatible in spidev Several device in ramips have this binding in the dts
+
+Signed-off-by: Giuseppe Lippolis <giu.lippolis@gmail.com>
+---
+ drivers/spi/spidev.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/spi/spidev.c
++++ b/drivers/spi/spidev.c
+@@ -696,6 +696,7 @@ static const struct of_device_id spidev_
+ { .compatible = "menlo,m53cpld" },
+ { .compatible = "cisco,spi-petra" },
+ { .compatible = "micron,spi-authenta" },
++ { .compatible = "siliconlabs,si3210" },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, spidev_dt_ids);
diff --git a/pkgs/patches-linux-5.15/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch b/pkgs/patches-linux-5.15/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch
new file mode 100644
index 0000000..8f40ae3
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/141-jffs2-add-RENAME_EXCHANGE-support.patch b/pkgs/patches-linux-5.15/141-jffs2-add-RENAME_EXCHANGE-support.patch
new file mode 100644
index 0000000..f58fc79
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/142-jffs2-add-splice-ops.patch b/pkgs/patches-linux-5.15/142-jffs2-add-splice-ops.patch
new file mode 100644
index 0000000..de847a1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/150-bridge_allow_receiption_on_disabled_port.patch b/pkgs/patches-linux-5.15/150-bridge_allow_receiption_on_disabled_port.patch
new file mode 100644
index 0000000..9968a79
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/190-rtc-rs5c372-support_alarms_up_to_1_week.patch b/pkgs/patches-linux-5.15/190-rtc-rs5c372-support_alarms_up_to_1_week.patch
new file mode 100644
index 0000000..13b79b5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch b/pkgs/patches-linux-5.15/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch
new file mode 100644
index 0000000..7e9d0e6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch b/pkgs/patches-linux-5.15/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch
new file mode 100644
index 0000000..caec8db
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/203-kallsyms_uncompressed.patch b/pkgs/patches-linux-5.15/203-kallsyms_uncompressed.patch
new file mode 100644
index 0000000..ea407d6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -1443,6 +1443,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
+@@ -263,6 +263,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/pkgs/patches-linux-5.15/204-module_strip.patch b/pkgs/patches-linux-5.15/204-module_strip.patch
new file mode 100644
index 0000000..dee240d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -2352,6 +2352,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
+@@ -2034,7 +2034,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++) {
+@@ -2213,8 +2215,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");
+@@ -2231,8 +2235,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 */
+@@ -2245,8 +2251,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
+ }
+
+ /**
+@@ -2326,11 +2334,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)
+@@ -2579,7 +2589,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/pkgs/patches-linux-5.15/205-backtrace_module_info.patch b/pkgs/patches-linux-5.15/205-backtrace_module_info.patch
new file mode 100644
index 0000000..6379ce0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/205-kconfig-exit.patch b/pkgs/patches-linux-5.15/205-kconfig-exit.patch
new file mode 100644
index 0000000..9f3bb8f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/205-kconfig-exit.patch
@@ -0,0 +1,20 @@
+From 300d26562ce4dc427154cb247beb75db4b1f0774 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:29:57 +0200
+Subject: [PATCH] scripts/Kconfig: Kconfig exit
+
+---
+ scripts/kconfig/conf.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/scripts/kconfig/conf.c
++++ b/scripts/kconfig/conf.c
+@@ -435,6 +435,8 @@ static int conf_sym(struct menu *menu)
+ break;
+ continue;
+ case 0:
++ if (!sym_has_value(sym) && !tty_stdio && getenv("FAIL_ON_UNCONFIGURED"))
++ exit(1);
+ newval = oldval;
+ break;
+ case '?':
diff --git a/pkgs/patches-linux-5.15/210-darwin_scripts_include.patch b/pkgs/patches-linux-5.15/210-darwin_scripts_include.patch
new file mode 100644
index 0000000..be6adc0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/211-darwin-uuid-typedef-clash.patch b/pkgs/patches-linux-5.15/211-darwin-uuid-typedef-clash.patch
new file mode 100644
index 0000000..50a6227
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/212-tools_portability.patch b/pkgs/patches-linux-5.15/212-tools_portability.patch
new file mode 100644
index 0000000..b488155
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/214-spidev_h_portability.patch b/pkgs/patches-linux-5.15/214-spidev_h_portability.patch
new file mode 100644
index 0000000..db754a2
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/220-arm-gc_sections.patch b/pkgs/patches-linux-5.15/220-arm-gc_sections.patch
new file mode 100644
index 0000000..0aa3eb8
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/221-module_exports.patch b/pkgs/patches-linux-5.15/221-module_exports.patch
new file mode 100644
index 0000000..204027d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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)
+
+@@ -484,14 +494,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 = .; \
+ } \
+ \
+@@ -511,7 +521,7 @@
+ \
+ /* Kernel symbol table: strings */ \
+ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \
+- *(__ksymtab_strings) \
++ *(__ksymtab_strings+*) \
+ } \
+ \
+ /* __*init sections */ \
+@@ -1018,6 +1028,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/pkgs/patches-linux-5.15/230-openwrt_lzma_options.patch b/pkgs/patches-linux-5.15/230-openwrt_lzma_options.patch
new file mode 100644
index 0000000..f9361b0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/240-remove-unsane-filenames-from-deps_initramfs-list.patch b/pkgs/patches-linux-5.15/240-remove-unsane-filenames-from-deps_initramfs-list.patch
new file mode 100644
index 0000000..29cfade
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/249-udp-tunnel-selection.patch b/pkgs/patches-linux-5.15/249-udp-tunnel-selection.patch
new file mode 100644
index 0000000..5e15400
--- /dev/null
+++ b/pkgs/patches-linux-5.15/249-udp-tunnel-selection.patch
@@ -0,0 +1,20 @@
+From 4487708144118bfd5e1b1da7003a486951fb9c5a Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 13:31:51 +0200
+Subject: [PATCH] net/ipv4: add udp tunnel tristate string
+
+---
+ net/ipv4/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/net/ipv4/Kconfig
++++ b/net/ipv4/Kconfig
+@@ -315,7 +315,7 @@ config NET_IPVTI
+ on top.
+
+ config NET_UDP_TUNNEL
+- tristate
++ tristate "IP: UDP tunneling support"
+ select NET_IP_TUNNEL
+ default n
+
diff --git a/pkgs/patches-linux-5.15/250-netfilter_depends.patch b/pkgs/patches-linux-5.15/250-netfilter_depends.patch
new file mode 100644
index 0000000..d9a2b81
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/251-kconfig.patch b/pkgs/patches-linux-5.15/251-kconfig.patch
new file mode 100644
index 0000000..5a8a820
--- /dev/null
+++ b/pkgs/patches-linux-5.15/251-kconfig.patch
@@ -0,0 +1,199 @@
+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
diff --git a/pkgs/patches-linux-5.15/252-SATA_PMP.patch b/pkgs/patches-linux-5.15/252-SATA_PMP.patch
new file mode 100644
index 0000000..6502d1d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/252-SATA_PMP.patch
@@ -0,0 +1,23 @@
+From 8c817e33be829c7249c2cfd59ff48ad5fac6a31d Mon Sep 17 00:00:00 2001
+From: Sungbo Eo <mans0n@gorani.run>
+Date: Fri, 7 Jul 2017 17:09:21 +0200
+Subject: [PATCH] kconfig: solidify SATA_PMP config
+
+SATA_PMP option in kernel config file disappears for every kernel_oldconfig refresh.
+To prevent this, SATA_HOST is now selected automatically when SATA_PMP is enabled.
+This patch can be dropped if SATA_MV is ever re-added into the config.
+---
+ drivers/ata/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/ata/Kconfig
++++ b/drivers/ata/Kconfig
+@@ -112,7 +112,7 @@ config SATA_ZPODD
+
+ config SATA_PMP
+ bool "SATA Port Multiplier support"
+- depends on SATA_HOST
++ select SATA_HOST
+ default y
+ help
+ This option adds support for SATA Port Multipliers
diff --git a/pkgs/patches-linux-5.15/253-ksmbd-config.patch b/pkgs/patches-linux-5.15/253-ksmbd-config.patch
new file mode 100644
index 0000000..a29cf3b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -2384,7 +2384,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/pkgs/patches-linux-5.15/259-regmap_dynamic.patch b/pkgs/patches-linux-5.15/259-regmap_dynamic.patch
new file mode 100644
index 0000000..6be5875
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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>
+@@ -3341,3 +3342,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/pkgs/patches-linux-5.15/260-crypto_test_dependencies.patch b/pkgs/patches-linux-5.15/260-crypto_test_dependencies.patch
new file mode 100644
index 0000000..64daa82
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/261-enable_wilink_platform_without_drivers.patch b/pkgs/patches-linux-5.15/261-enable_wilink_platform_without_drivers.patch
new file mode 100644
index 0000000..cd31f9d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/261-lib-arc4-unhide.patch b/pkgs/patches-linux-5.15/261-lib-arc4-unhide.patch
new file mode 100644
index 0000000..ee923c7
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/280-rfkill-stubs.patch b/pkgs/patches-linux-5.15/280-rfkill-stubs.patch
new file mode 100644
index 0000000..7a650d1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch b/pkgs/patches-linux-5.15/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch
new file mode 100644
index 0000000..f21f200
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/300-mips_expose_boot_raw.patch b/pkgs/patches-linux-5.15/300-mips_expose_boot_raw.patch
new file mode 100644
index 0000000..05d0249
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch b/pkgs/patches-linux-5.15/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch
new file mode 100644
index 0000000..071ea67
--- /dev/null
+++ b/pkgs/patches-linux-5.15/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch
@@ -0,0 +1,208 @@
+From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001
+From: Adrian Panella <ianchi74@outlook.com>
+Date: Thu, 9 Mar 2017 09:37:17 +0100
+Subject: [PATCH 67/69] generic: Mangle bootloader's kernel arguments
+
+The command-line arguments provided by the boot loader will be
+appended to a new device tree property: bootloader-args.
+If there is a property "append-rootblock" in DT under /chosen
+and a root= option in bootloaders command line it will be parsed
+and added to DT bootargs with the form: <append-rootblock>XX.
+Only command line ATAG will be processed, the rest of the ATAGs
+sent by bootloader will be ignored.
+This is usefull in dual boot systems, to get the current root partition
+without afecting the rest of the system.
+
+Signed-off-by: Adrian Panella <ianchi74@outlook.com>
+
+This patch has been modified to be mvebu specific. The original patch
+did not pass the bootloader cmdline on if no append-rootblock stanza
+was found, resulting in blank cmdline and failure to boot.
+
+Signed-off-by: Michael Gray <michael.gray@lantisproject.com>
+---
+ arch/arm/Kconfig | 11 ++++
+ arch/arm/boot/compressed/atags_to_fdt.c | 85 ++++++++++++++++++++++++-
+ init/main.c | 16 +++++
+ 3 files changed, 111 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -1727,6 +1727,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN
+ The command-line arguments provided by the boot loader will be
+ appended to the the device tree bootargs property.
+
++config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
++ bool "Append rootblock parsing bootloader's kernel arguments"
++ help
++ The command-line arguments provided by the boot loader will be
++ appended to a new device tree property: bootloader-args.
++ If there is a property "append-rootblock" in DT under /chosen
++ and a root= option in bootloaders command line it will be parsed
++ and added to DT bootargs with the form: <append-rootblock>XX.
++ Only command line ATAG will be processed, the rest of the ATAGs
++ sent by bootloader will be ignored.
++
+ endchoice
+
+ config CMDLINE
+--- a/arch/arm/boot/compressed/atags_to_fdt.c
++++ b/arch/arm/boot/compressed/atags_to_fdt.c
+@@ -5,6 +5,8 @@
+
+ #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND)
+ #define do_extend_cmdline 1
++#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++#define do_extend_cmdline 1
+ #else
+ #define do_extend_cmdline 0
+ #endif
+@@ -69,6 +71,72 @@ static uint32_t get_cell_size(const void
+ return cell_size;
+ }
+
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++
++static char *append_rootblock(char *dest, const char *str, int len, void *fdt)
++{
++ char *ptr, *end;
++ char *root="root=";
++ int i, l;
++ const char *rootblock;
++
++ //ARM doesn't have __HAVE_ARCH_STRSTR, so search manually
++ ptr = str - 1;
++
++ do {
++ //first find an 'r' at the begining or after a space
++ do {
++ ptr++;
++ ptr = strchr(ptr, 'r');
++ if (!ptr)
++ goto no_append;
++
++ } while (ptr != str && *(ptr-1) != ' ');
++
++ //then check for the rest
++ for(i = 1; i <= 4; i++)
++ if(*(ptr+i) != *(root+i)) break;
++
++ } while (i != 5);
++
++ end = strchr(ptr, ' ');
++ end = end ? (end - 1) : (strchr(ptr, 0) - 1);
++
++ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX )
++ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++);
++ ptr = end + 1;
++
++ /* if append-rootblock property is set use it to append to command line */
++ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l);
++ if (rootblock == NULL)
++ goto no_append;
++
++ if (*dest != ' ') {
++ *dest = ' ';
++ dest++;
++ len++;
++ }
++
++ if (len + l + i <= COMMAND_LINE_SIZE) {
++ memcpy(dest, rootblock, l);
++ dest += l - 1;
++ memcpy(dest, ptr, i);
++ dest += i;
++ }
++
++ return dest;
++
++no_append:
++ len = strlen(str);
++ if (len + 1 < COMMAND_LINE_SIZE) {
++ memcpy(dest, str, len);
++ dest += len;
++ }
++
++ return dest;
++}
++#endif
++
+ static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
+ {
+ char cmdline[COMMAND_LINE_SIZE];
+@@ -88,12 +156,21 @@ static void merge_fdt_bootargs(void *fdt
+
+ /* and append the ATAG_CMDLINE */
+ if (fdt_cmdline) {
++
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++ //save original bootloader args
++ //and append ubi.mtd with root partition number to current cmdline
++ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline);
++ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt);
++
++#else
+ len = strlen(fdt_cmdline);
+ if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) {
+ *ptr++ = ' ';
+ memcpy(ptr, fdt_cmdline, len);
+ ptr += len;
+ }
++#endif
+ }
+ *ptr = '\0';
+
+@@ -168,7 +245,9 @@ int atags_to_fdt(void *atag_list, void *
+ else
+ setprop_string(fdt, "/chosen", "bootargs",
+ atag->u.cmdline.cmdline);
+- } else if (atag->hdr.tag == ATAG_MEM) {
++ }
++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE
++ else if (atag->hdr.tag == ATAG_MEM) {
+ if (memcount >= sizeof(mem_reg_property)/4)
+ continue;
+ if (!atag->u.mem.size)
+@@ -212,6 +291,10 @@ int atags_to_fdt(void *atag_list, void *
+ setprop(fdt, "/memory", "reg", mem_reg_property,
+ 4 * memcount * memsize);
+ }
++#else
++
++ }
++#endif
+
+ return fdt_pack(fdt);
+ }
+--- a/init/main.c
++++ b/init/main.c
+@@ -113,6 +113,10 @@
+
+ #include <kunit/test.h>
+
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++#include <linux/of.h>
++#endif
++
+ static int kernel_init(void *);
+
+ extern void init_IRQ(void);
+@@ -990,6 +994,18 @@ asmlinkage __visible void __init __no_sa
+ page_alloc_init();
+
+ pr_notice("Kernel command line: %s\n", saved_command_line);
++
++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE)
++ //Show bootloader's original command line for reference
++ if(of_chosen) {
++ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL);
++ if(prop)
++ pr_notice("Bootloader command line (ignored): %s\n", prop);
++ else
++ pr_notice("Bootloader command line not present\n");
++ }
++#endif
++
+ /* parameters may set static keys */
+ jump_label_init();
+ parse_early_param();
diff --git a/pkgs/patches-linux-5.15/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch b/pkgs/patches-linux-5.15/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch
new file mode 100644
index 0000000..18a8752
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/301-mips_image_cmdline_hack.patch b/pkgs/patches-linux-5.15/301-mips_image_cmdline_hack.patch
new file mode 100644
index 0000000..15e233a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/301-mips_image_cmdline_hack.patch
@@ -0,0 +1,38 @@
+From: John Crispin <john@phrozen.org>
+Subject: hack: kernel: add generic image_cmdline hack to MIPS targets
+
+lede-commit: d59f5b3a987a48508257a0ddbaeadc7909f9f976
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ arch/mips/Kconfig | 4 ++++
+ arch/mips/kernel/head.S | 6 ++++++
+ 2 files changed, 10 insertions(+)
+
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -1180,6 +1180,10 @@ config MIPS_MSC
+ config SYNC_R4K
+ bool
+
++config IMAGE_CMDLINE_HACK
++ bool "OpenWrt specific image command line hack"
++ default n
++
+ config NO_IOPORT_MAP
+ def_bool n
+
+--- a/arch/mips/kernel/head.S
++++ b/arch/mips/kernel/head.S
+@@ -79,6 +79,12 @@ FEXPORT(__kernel_entry)
+ j kernel_entry
+ #endif /* CONFIG_BOOT_RAW */
+
++#ifdef CONFIG_IMAGE_CMDLINE_HACK
++ .ascii "CMDLINE:"
++EXPORT(__image_cmdline)
++ .fill 0x400
++#endif /* CONFIG_IMAGE_CMDLINE_HACK */
++
+ __REF
+
+ NESTED(kernel_entry, 16, sp) # kernel entry point
diff --git a/pkgs/patches-linux-5.15/301-mvebu-armada-38x-enable-libata-leds.patch b/pkgs/patches-linux-5.15/301-mvebu-armada-38x-enable-libata-leds.patch
new file mode 100644
index 0000000..615caac
--- /dev/null
+++ b/pkgs/patches-linux-5.15/301-mvebu-armada-38x-enable-libata-leds.patch
@@ -0,0 +1,10 @@
+--- a/arch/arm/mach-mvebu/Kconfig
++++ b/arch/arm/mach-mvebu/Kconfig
+@@ -67,6 +67,7 @@ config MACH_ARMADA_38X
+ select HAVE_ARM_TWD if SMP
+ select MACH_MVEBU_V7
+ select PINCTRL_ARMADA_38X
++ select ARCH_WANT_LIBATA_LEDS
+ help
+ Say 'Y' here if you want your kernel to support boards based
+ on the Marvell Armada 380/385 SoC with device tree.
diff --git a/pkgs/patches-linux-5.15/302-add_powertables.patch b/pkgs/patches-linux-5.15/302-add_powertables.patch
new file mode 100644
index 0000000..93ad5de
--- /dev/null
+++ b/pkgs/patches-linux-5.15/302-add_powertables.patch
@@ -0,0 +1,770 @@
+--- a/arch/arm/boot/dts/armada-385-linksys.dtsi
++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi
+@@ -214,11 +214,19 @@
+ &pcie1 {
+ /* Marvell 88W8864, 5GHz-only */
+ status = "okay";
++
++ mwlwifi {
++ marvell,2ghz = <0>;
++ };
+ };
+
+ &pcie2 {
+ /* Marvell 88W8864, 2GHz-only */
+ status = "okay";
++
++ mwlwifi {
++ marvell,5ghz = <0>;
++ };
+ };
+
+ &pinctrl {
+--- a/arch/arm/boot/dts/armada-385-linksys-caiman.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-caiman.dts
+@@ -142,3 +142,205 @@
+ };
+ };
+ };
++
++&pcie1 {
++ mwlwifi {
++ marvell,chainmask = <2 2>;
++ marvell,powertable {
++ AU =
++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <100 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <104 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <108 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <112 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <116 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <120 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <124 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <128 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <132 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <136 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <140 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++ <149 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>;
++ CA =
++ <36 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++ <40 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++ <44 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++ <48 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>;
++ CN =
++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <149 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x16 0x16 0x16 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>;
++ ETSI =
++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++ <149 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>;
++ FCC =
++ <36 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <40 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <44 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <48 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>;
++ };
++ };
++};
++
++&pcie2 {
++ mwlwifi {
++ marvell,chainmask = <2 2>;
++ marvell,powertable {
++ AU =
++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>;
++ CA =
++ <1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x00 0x00 0x00 0x00 0 0xf>,
++ <2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++ <11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x00 0x00 0x00 0x00 0 0xf>;
++ CN =
++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <14 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>;
++ ETSI =
++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>;
++ FCC =
++ <1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x0 0x0 0x0 0x0 0 0xf>;
++ };
++ };
++};
+--- a/arch/arm/boot/dts/armada-385-linksys-cobra.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-cobra.dts
+@@ -142,3 +142,205 @@
+ };
+ };
+ };
++
++&pcie1 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ AU =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>;
++ CA =
++ <36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++ CN =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>;
++ ETSI =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>;
++ FCC =
++ <36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>,
++ <40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++ };
++ };
++};
++
++&pcie2 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ AU =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ CA =
++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++ CN =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ ETSI =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ FCC =
++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++ };
++ };
++};
+--- a/arch/arm/boot/dts/armada-385-linksys-shelby.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-shelby.dts
+@@ -142,3 +142,205 @@
+ };
+ };
+ };
++
++&pcie1 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ AU =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++ <165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>;
++ CA =
++ <36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++ CN =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++ <165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>;
++ ETSI =
++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++ <149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>;
++ FCC =
++ <36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>,
++ <40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++ };
++ };
++};
++
++&pcie2 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ AU =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ CA =
++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++ CN =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ ETSI =
++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++ FCC =
++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++ };
++ };
++};
+--- a/arch/arm/boot/dts/armada-385-linksys-rango.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-rango.dts
+@@ -157,6 +157,18 @@
+ };
+ };
+
++&pcie1 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ };
++};
++
++&pcie2 {
++ mwlwifi {
++ marvell,chainmask = <4 4>;
++ };
++};
++
+ &sdhci {
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdhci_pins>;
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -225,12 +225,100 @@
+ pcie@2,0 {
+ /* Port 0, Lane 1 */
+ status = "okay";
++
++ mwlwifi {
++ marvell,5ghz = <0>;
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ FCC =
++ <1 0 0x17 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0x17 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>;
++
++ ETSI =
++ <1 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <2 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <3 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <4 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <5 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <6 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <7 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <8 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <9 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <10 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <11 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <12 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++ <13 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>;
++ };
++ };
+ };
+
+ /* Second mini-PCIe port */
+ pcie@3,0 {
+ /* Port 0, Lane 3 */
+ status = "okay";
++
++ mwlwifi {
++ marvell,2ghz = <0>;
++ marvell,chainmask = <4 4>;
++ marvell,powertable {
++ FCC =
++ <36 0 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <40 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <44 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <48 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++ <52 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++ <56 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++ <60 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++ <64 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++ <149 0 0x16 0x16 0x16 0x16 0x14 0x14 0x14 0x14 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++ <153 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++ <157 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++ <161 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++ <165 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>;
++
++ ETSI =
++ <36 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <40 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <44 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <48 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <52 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <56 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <60 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <64 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <100 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <104 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <108 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <112 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <116 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <120 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <124 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <128 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <132 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <136 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <140 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++ <149 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>;
++ };
++ };
+ };
+ };
+
diff --git a/pkgs/patches-linux-5.15/302-mips_no_branch_likely.patch b/pkgs/patches-linux-5.15/302-mips_no_branch_likely.patch
new file mode 100644
index 0000000..271923f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/304-revert_i2c_delay.patch b/pkgs/patches-linux-5.15/304-revert_i2c_delay.patch
new file mode 100644
index 0000000..930c0f9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/304-revert_i2c_delay.patch
@@ -0,0 +1,15 @@
+--- a/arch/arm/boot/dts/armada-xp.dtsi
++++ b/arch/arm/boot/dts/armada-xp.dtsi
+@@ -237,12 +237,10 @@
+ };
+
+ &i2c0 {
+- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
+ reg = <0x11000 0x100>;
+ };
+
+ &i2c1 {
+- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
+ reg = <0x11100 0x100>;
+ };
+
diff --git a/pkgs/patches-linux-5.15/305-armada-385-rd-mtd-partitions.patch b/pkgs/patches-linux-5.15/305-armada-385-rd-mtd-partitions.patch
new file mode 100644
index 0000000..31bd53b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/305-armada-385-rd-mtd-partitions.patch
@@ -0,0 +1,19 @@
+--- a/arch/arm/boot/dts/armada-388-rd.dts
++++ b/arch/arm/boot/dts/armada-388-rd.dts
+@@ -103,6 +103,16 @@
+ compatible = "st,m25p128", "jedec,spi-nor";
+ reg = <0>; /* Chip select 0 */
+ spi-max-frequency = <108000000>;
++
++ partition@0 {
++ label = "uboot";
++ reg = <0 0x400000>;
++ };
++
++ partition@1 {
++ label = "firmware";
++ reg = <0x400000 0xc00000>;
++ };
+ };
+ };
+
diff --git a/pkgs/patches-linux-5.15/305-mips_module_reloc.patch b/pkgs/patches-linux-5.15/305-mips_module_reloc.patch
new file mode 100644
index 0000000..bbea1f6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/306-ARM-mvebu-385-ap-Add-partitions.patch b/pkgs/patches-linux-5.15/306-ARM-mvebu-385-ap-Add-partitions.patch
new file mode 100644
index 0000000..2057e31
--- /dev/null
+++ b/pkgs/patches-linux-5.15/306-ARM-mvebu-385-ap-Add-partitions.patch
@@ -0,0 +1,35 @@
+From 9861f93a59142a3131870df2521eb2deb73026d7 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@free-electrons.com>
+Date: Tue, 13 Jan 2015 11:14:09 +0100
+Subject: [PATCH 2/2] ARM: mvebu: 385-ap: Add partitions
+
+Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+---
+ arch/arm/boot/dts/armada-385-db-ap.dts | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/arch/arm/boot/dts/armada-385-db-ap.dts
++++ b/arch/arm/boot/dts/armada-385-db-ap.dts
+@@ -218,19 +218,19 @@
+ #size-cells = <1>;
+
+ partition@0 {
+- label = "U-Boot";
++ label = "u-boot";
+ reg = <0x00000000 0x00800000>;
+ read-only;
+ };
+
+ partition@800000 {
+- label = "uImage";
++ label = "kernel";
+ reg = <0x00800000 0x00400000>;
+ read-only;
+ };
+
+ partition@c00000 {
+- label = "Root";
++ label = "ubi";
+ reg = <0x00c00000 0x3f400000>;
+ };
+ };
diff --git a/pkgs/patches-linux-5.15/307-armada-xp-linksys-mamba-broken-idle.patch b/pkgs/patches-linux-5.15/307-armada-xp-linksys-mamba-broken-idle.patch
new file mode 100644
index 0000000..16112d5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/307-armada-xp-linksys-mamba-broken-idle.patch
@@ -0,0 +1,10 @@
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -485,3 +485,7 @@
+ };
+ };
+ };
++
++&coherencyfab {
++ broken-idle;
++};
diff --git a/pkgs/patches-linux-5.15/307-mips_highmem_offset.patch b/pkgs/patches-linux-5.15/307-mips_highmem_offset.patch
new file mode 100644
index 0000000..0529b0c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/308-armada-xp-linksys-mamba-wan.patch b/pkgs/patches-linux-5.15/308-armada-xp-linksys-mamba-wan.patch
new file mode 100644
index 0000000..4315abc
--- /dev/null
+++ b/pkgs/patches-linux-5.15/308-armada-xp-linksys-mamba-wan.patch
@@ -0,0 +1,11 @@
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -387,7 +387,7 @@
+
+ port@4 {
+ reg = <4>;
+- label = "internet";
++ label = "wan";
+ };
+
+ port@5 {
diff --git a/pkgs/patches-linux-5.15/308-mips32r2_tune.patch b/pkgs/patches-linux-5.15/308-mips32r2_tune.patch
new file mode 100644
index 0000000..ef92a5d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch b/pkgs/patches-linux-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch
new file mode 100644
index 0000000..318c0b1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch
@@ -0,0 +1,136 @@
+From 87ec87c2ad615c1a177cd08ef5fa29fc739f6e50 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 23 Dec 2018 18:06:53 +0100
+Subject: [PATCH] MIPS: Add CPU option reporting to /proc/cpuinfo
+
+Many MIPS CPUs have optional CPU features which are not activates for
+all CPU cores. Print the CPU options which are implemented in the core
+in /proc/cpuinfo. This makes it possible to see what features are
+supported and which are not supported. This should cover all standard
+MIPS extensions, before it only printed information about the main MIPS
+ASEs.
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+---
+ arch/mips/kernel/proc.c | 116 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 116 insertions(+)
+
+--- a/arch/mips/kernel/proc.c
++++ b/arch/mips/kernel/proc.c
+@@ -138,6 +138,116 @@ static int show_cpuinfo(struct seq_file
+ seq_printf(m, "micromips kernel\t: %s\n",
+ (read_c0_config3() & MIPS_CONF3_ISA_OE) ? "yes" : "no");
+ }
++
++ seq_printf(m, "Options implemented\t:");
++ if (cpu_has_tlb)
++ seq_printf(m, "%s", " tlb");
++ if (cpu_has_ftlb)
++ seq_printf(m, "%s", " ftlb");
++ if (cpu_has_tlbinv)
++ seq_printf(m, "%s", " tlbinv");
++ if (cpu_has_segments)
++ seq_printf(m, "%s", " segments");
++ if (cpu_has_rixiex)
++ seq_printf(m, "%s", " rixiex");
++ if (cpu_has_ldpte)
++ seq_printf(m, "%s", " ldpte");
++ if (cpu_has_maar)
++ seq_printf(m, "%s", " maar");
++ if (cpu_has_rw_llb)
++ seq_printf(m, "%s", " rw_llb");
++ if (cpu_has_4kex)
++ seq_printf(m, "%s", " 4kex");
++ if (cpu_has_3k_cache)
++ seq_printf(m, "%s", " 3k_cache");
++ if (cpu_has_4k_cache)
++ seq_printf(m, "%s", " 4k_cache");
++ if (cpu_has_tx39_cache)
++ seq_printf(m, "%s", " tx39_cache");
++ if (cpu_has_octeon_cache)
++ seq_printf(m, "%s", " octeon_cache");
++ if (cpu_has_fpu)
++ seq_printf(m, "%s", " fpu");
++ if (cpu_has_32fpr)
++ seq_printf(m, "%s", " 32fpr");
++ if (cpu_has_cache_cdex_p)
++ seq_printf(m, "%s", " cache_cdex_p");
++ if (cpu_has_cache_cdex_s)
++ seq_printf(m, "%s", " cache_cdex_s");
++ if (cpu_has_prefetch)
++ seq_printf(m, "%s", " prefetch");
++ if (cpu_has_mcheck)
++ seq_printf(m, "%s", " mcheck");
++ if (cpu_has_ejtag)
++ seq_printf(m, "%s", " ejtag");
++ if (cpu_has_llsc)
++ seq_printf(m, "%s", " llsc");
++ if (cpu_has_guestctl0ext)
++ seq_printf(m, "%s", " guestctl0ext");
++ if (cpu_has_guestctl1)
++ seq_printf(m, "%s", " guestctl1");
++ if (cpu_has_guestctl2)
++ seq_printf(m, "%s", " guestctl2");
++ if (cpu_has_guestid)
++ seq_printf(m, "%s", " guestid");
++ if (cpu_has_drg)
++ seq_printf(m, "%s", " drg");
++ if (cpu_has_rixi)
++ seq_printf(m, "%s", " rixi");
++ if (cpu_has_lpa)
++ seq_printf(m, "%s", " lpa");
++ if (cpu_has_mvh)
++ seq_printf(m, "%s", " mvh");
++ if (cpu_has_vtag_icache)
++ seq_printf(m, "%s", " vtag_icache");
++ if (cpu_has_dc_aliases)
++ seq_printf(m, "%s", " dc_aliases");
++ if (cpu_has_ic_fills_f_dc)
++ seq_printf(m, "%s", " ic_fills_f_dc");
++ if (cpu_has_pindexed_dcache)
++ seq_printf(m, "%s", " pindexed_dcache");
++ if (cpu_has_userlocal)
++ seq_printf(m, "%s", " userlocal");
++ if (cpu_has_nofpuex)
++ seq_printf(m, "%s", " nofpuex");
++ if (cpu_has_vint)
++ seq_printf(m, "%s", " vint");
++ if (cpu_has_veic)
++ seq_printf(m, "%s", " veic");
++ if (cpu_has_inclusive_pcaches)
++ seq_printf(m, "%s", " inclusive_pcaches");
++ if (cpu_has_perf_cntr_intr_bit)
++ seq_printf(m, "%s", " perf_cntr_intr_bit");
++ if (cpu_has_ufr)
++ seq_printf(m, "%s", " ufr");
++ if (cpu_has_fre)
++ seq_printf(m, "%s", " fre");
++ if (cpu_has_cdmm)
++ seq_printf(m, "%s", " cdmm");
++ if (cpu_has_small_pages)
++ seq_printf(m, "%s", " small_pages");
++ if (cpu_has_nan_legacy)
++ seq_printf(m, "%s", " nan_legacy");
++ if (cpu_has_nan_2008)
++ seq_printf(m, "%s", " nan_2008");
++ if (cpu_has_ebase_wg)
++ seq_printf(m, "%s", " ebase_wg");
++ if (cpu_has_badinstr)
++ seq_printf(m, "%s", " badinstr");
++ if (cpu_has_badinstrp)
++ seq_printf(m, "%s", " badinstrp");
++ if (cpu_has_contextconfig)
++ seq_printf(m, "%s", " contextconfig");
++ if (cpu_has_perf)
++ seq_printf(m, "%s", " perf");
++ if (cpu_has_shared_ftlb_ram)
++ seq_printf(m, "%s", " shared_ftlb_ram");
++ if (cpu_has_shared_ftlb_entries)
++ seq_printf(m, "%s", " shared_ftlb_entries");
++ if (cpu_has_mipsmt_pertccounters)
++ seq_printf(m, "%s", " mipsmt_pertccounters");
++ seq_printf(m, "\n");
++
+ seq_printf(m, "shadow register sets\t: %d\n",
+ cpu_data[n].srsets);
+ seq_printf(m, "kscratch registers\t: %d\n",
diff --git a/pkgs/patches-linux-5.15/309-linksys-status-led.patch b/pkgs/patches-linux-5.15/309-linksys-status-led.patch
new file mode 100644
index 0000000..e5e8357
--- /dev/null
+++ b/pkgs/patches-linux-5.15/309-linksys-status-led.patch
@@ -0,0 +1,50 @@
+--- a/arch/arm/boot/dts/armada-385-linksys.dtsi
++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi
+@@ -14,6 +14,13 @@
+ compatible = "linksys,armada385", "marvell,armada385",
+ "marvell,armada380";
+
++ aliases {
++ led-boot = &led_power;
++ led-failsafe = &led_power;
++ led-running = &led_power;
++ led-upgrade = &led_power;
++ };
++
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+@@ -71,7 +78,7 @@
+ pinctrl-0 = <&gpio_leds_pins>;
+ pinctrl-names = "default";
+
+- power {
++ led_power: power {
+ gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
+ default-state = "on";
+ };
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -26,6 +26,13 @@
+ compatible = "linksys,mamba", "marvell,armadaxp-mv78230",
+ "marvell,armadaxp", "marvell,armada-370-xp";
+
++ aliases {
++ led-boot = &led_power;
++ led-failsafe = &led_power;
++ led-running = &led_power;
++ led-upgrade = &led_power;
++ };
++
+ chosen {
+ bootargs = "console=ttyS0,115200";
+ stdout-path = &uart0;
+@@ -197,7 +204,7 @@
+ pinctrl-0 = <&power_led_pin>;
+ pinctrl-names = "default";
+
+- power {
++ led_power: power {
+ label = "mamba:white:power";
+ gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
+ default-state = "on";
diff --git a/pkgs/patches-linux-5.15/310-arm_module_unresolved_weak_sym.patch b/pkgs/patches-linux-5.15/310-arm_module_unresolved_weak_sym.patch
new file mode 100644
index 0000000..191dc6a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/310-linksys-use-eth0-as-cpu-port.patch b/pkgs/patches-linux-5.15/310-linksys-use-eth0-as-cpu-port.patch
new file mode 100644
index 0000000..84d49a0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/310-linksys-use-eth0-as-cpu-port.patch
@@ -0,0 +1,25 @@
+--- a/arch/arm/boot/dts/armada-385-linksys.dtsi
++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi
+@@ -116,7 +116,7 @@
+ };
+
+ &eth2 {
+- status = "okay";
++ status = "disabled";
+ phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <2>;
+@@ -200,10 +200,10 @@
+ label = "wan";
+ };
+
+- port@5 {
+- reg = <5>;
++ port@6 {
++ reg = <6>;
+ label = "cpu";
+- ethernet = <&eth2>;
++ ethernet = <&eth0>;
+
+ fixed-link {
+ speed = <1000>;
diff --git a/pkgs/patches-linux-5.15/311-adjust-compatible-for-linksys.patch b/pkgs/patches-linux-5.15/311-adjust-compatible-for-linksys.patch
new file mode 100644
index 0000000..a5d3e63
--- /dev/null
+++ b/pkgs/patches-linux-5.15/311-adjust-compatible-for-linksys.patch
@@ -0,0 +1,68 @@
+--- a/arch/arm/boot/dts/armada-385-linksys-rango.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-rango.dts
+@@ -12,8 +12,8 @@
+
+ / {
+ model = "Linksys WRT3200ACM";
+- compatible = "linksys,rango", "linksys,armada385", "marvell,armada385",
+- "marvell,armada380";
++ compatible = "linksys,wrt3200acm", "linksys,rango", "linksys,armada385",
++ "marvell,armada385", "marvell,armada380";
+ };
+
+ &expander0 {
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -22,9 +22,10 @@
+ #include "armada-xp-mv78230.dtsi"
+
+ / {
+- model = "Linksys WRT1900AC";
+- compatible = "linksys,mamba", "marvell,armadaxp-mv78230",
+- "marvell,armadaxp", "marvell,armada-370-xp";
++ model = "Linksys WRT1900AC v1";
++ compatible = "linksys,wrt1900ac-v1", "linksys,mamba",
++ "marvell,armadaxp-mv78230", "marvell,armadaxp",
++ "marvell,armada-370-xp";
+
+ aliases {
+ led-boot = &led_power;
+--- a/arch/arm/boot/dts/armada-385-linksys-cobra.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-cobra.dts
+@@ -9,8 +9,9 @@
+ #include "armada-385-linksys.dtsi"
+
+ / {
+- model = "Linksys WRT1900ACv2";
+- compatible = "linksys,cobra", "linksys,armada385", "marvell,armada385",
++ model = "Linksys WRT1900AC v2";
++ compatible = "linksys,wrt1900ac-v2", "linksys,cobra",
++ "linksys,armada385", "marvell,armada385",
+ "marvell,armada380";
+ };
+
+--- a/arch/arm/boot/dts/armada-385-linksys-caiman.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-caiman.dts
+@@ -10,8 +10,8 @@
+
+ / {
+ model = "Linksys WRT1200AC";
+- compatible = "linksys,caiman", "linksys,armada385", "marvell,armada385",
+- "marvell,armada380";
++ compatible = "linksys,wrt1200ac", "linksys,caiman", "linksys,armada385",
++ "marvell,armada385", "marvell,armada380";
+ };
+
+ &expander0 {
+--- a/arch/arm/boot/dts/armada-385-linksys-shelby.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-shelby.dts
+@@ -10,7 +10,8 @@
+
+ / {
+ model = "Linksys WRT1900ACS";
+- compatible = "linksys,shelby", "linksys,armada385", "marvell,armada385",
++ compatible = "linksys,wrt1900acs", "linksys,shelby",
++ "linksys,armada385", "marvell,armada385",
+ "marvell,armada380";
+ };
+
diff --git a/pkgs/patches-linux-5.15/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch b/pkgs/patches-linux-5.15/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch
new file mode 100644
index 0000000..dd2bef7
--- /dev/null
+++ b/pkgs/patches-linux-5.15/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch
@@ -0,0 +1,87 @@
+From 8137da20701c776ad3481115305a5e8e410871ba Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 29 Nov 2016 10:15:45 +0000
+Subject: ARM: dts: armada388-clearfog: emmc on clearfog base
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ arch/arm/boot/dts/armada-388-clearfog-base.dts | 1 +
+ .../dts/armada-38x-solidrun-microsom-emmc.dtsi | 62 ++++++++++++++++++++++
+ 2 files changed, 63 insertions(+)
+ create mode 100644 arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi
+
+--- a/arch/arm/boot/dts/armada-388-clearfog-base.dts
++++ b/arch/arm/boot/dts/armada-388-clearfog-base.dts
+@@ -7,6 +7,7 @@
+
+ /dts-v1/;
+ #include "armada-388-clearfog.dtsi"
++#include "armada-38x-solidrun-microsom-emmc.dtsi"
+
+ / {
+ model = "SolidRun Clearfog Base A1";
+--- /dev/null
++++ b/arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi
+@@ -0,0 +1,62 @@
++/*
++ * Device Tree file for SolidRun Armada 38x Microsom add-on for eMMC
++ *
++ * Copyright (C) 2015 Russell King
++ *
++ * This board is in development; the contents of this file work with
++ * the A1 rev 2.0 of the board, which does not represent final
++ * production board. Things will change, don't expect this file to
++ * remain compatible info the future.
++ *
++ * This file is dual-licensed: you can use it either under the terms
++ * of the GPL or the X11 license, at your option. Note that this dual
++ * licensing only applies to this file, and not this project as a
++ * whole.
++ *
++ * a) This file 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.
++ *
++ * This file 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 General Public License for more details.
++ *
++ * Or, alternatively
++ *
++ * b) Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use
++ * copy, modify, merge, publish, distribute, sublicense, and/or
++ * sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following
++ * conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ */
++/ {
++ soc {
++ internal-regs {
++ sdhci@d8000 {
++ bus-width = <4>;
++ no-1-8-v;
++ non-removable;
++ pinctrl-0 = <&microsom_sdhci_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++ wp-inverted;
++ };
++ };
++ };
++};
diff --git a/pkgs/patches-linux-5.15/313-helios4-dts-status-led-alias.patch b/pkgs/patches-linux-5.15/313-helios4-dts-status-led-alias.patch
new file mode 100644
index 0000000..607f436
--- /dev/null
+++ b/pkgs/patches-linux-5.15/313-helios4-dts-status-led-alias.patch
@@ -0,0 +1,28 @@
+--- a/arch/arm/boot/dts/armada-388-helios4.dts
++++ b/arch/arm/boot/dts/armada-388-helios4.dts
+@@ -15,6 +15,13 @@
+ model = "Helios4";
+ compatible = "kobol,helios4", "marvell,armada388",
+ "marvell,armada385", "marvell,armada380";
++
++ aliases {
++ led-boot = &led_status;
++ led-failsafe = &led_status;
++ led-running = &led_status;
++ led-upgrade = &led_status;
++ };
+
+ memory {
+ device_type = "memory";
+@@ -73,10 +80,9 @@
+ pinctrl-names = "default";
+ pinctrl-0 = <&helios_system_led_pins>;
+
+- status-led {
++ led_status: status-led {
+ label = "helios4:green:status";
+ gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
+- linux,default-trigger = "heartbeat";
+ default-state = "on";
+ };
+
diff --git a/pkgs/patches-linux-5.15/315-armada-xp-linksys-mamba-resize-kernel.patch b/pkgs/patches-linux-5.15/315-armada-xp-linksys-mamba-resize-kernel.patch
new file mode 100644
index 0000000..f1fddce
--- /dev/null
+++ b/pkgs/patches-linux-5.15/315-armada-xp-linksys-mamba-resize-kernel.patch
@@ -0,0 +1,37 @@
+From 258233f00bcd013050efee00c5d9128ef8cd62dd Mon Sep 17 00:00:00 2001
+From: Tad <tad@spotco.us>
+Date: Fri, 5 Feb 2021 22:32:11 -0500
+Subject: [PATCH] ARM: dts: armada-xp-linksys-mamba: Increase kernel
+ partition to 4MB
+
+Signed-off-by: Tad Davanzo <tad@spotco.us>
+---
+ arch/arm/boot/dts/armada-xp-linksys-mamba.dts | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -456,9 +456,9 @@
+ reg = <0xa00000 0x2800000>; /* 40MB */
+ };
+
+- partition@d00000 {
++ partition@e00000 {
+ label = "rootfs1";
+- reg = <0xd00000 0x2500000>; /* 37MB */
++ reg = <0xe00000 0x2400000>; /* 36MB */
+ };
+
+ /* kernel2 overlaps with rootfs2 by design */
+@@ -467,9 +467,9 @@
+ reg = <0x3200000 0x2800000>; /* 40MB */
+ };
+
+- partition@3500000 {
++ partition@3600000 {
+ label = "rootfs2";
+- reg = <0x3500000 0x2500000>; /* 37MB */
++ reg = <0x3600000 0x2400000>; /* 36MB */
+ };
+
+ /*
diff --git a/pkgs/patches-linux-5.15/316-armada-370-dts-fix-crypto-engine.patch b/pkgs/patches-linux-5.15/316-armada-370-dts-fix-crypto-engine.patch
new file mode 100644
index 0000000..1937887
--- /dev/null
+++ b/pkgs/patches-linux-5.15/316-armada-370-dts-fix-crypto-engine.patch
@@ -0,0 +1,29 @@
+--- a/arch/arm/boot/dts/armada-370.dtsi
++++ b/arch/arm/boot/dts/armada-370.dtsi
+@@ -234,7 +234,7 @@
+ clocks = <&gateclk 23>;
+ clock-names = "cesa0";
+ marvell,crypto-srams = <&crypto_sram>;
+- marvell,crypto-sram-size = <0x7e0>;
++ marvell,crypto-sram-size = <0x800>;
+ };
+ };
+
+@@ -255,12 +255,17 @@
+ * cpuidle workaround.
+ */
+ idle-sram@0 {
++ status = "disabled";
+ reg = <0x0 0x20>;
+ };
+ };
+ };
+ };
+
++&coherencyfab {
++ broken-idle;
++};
++
+ /*
+ * Default UART pinctrl setting without RTS/CTS, can be overwritten on
+ * board level if a different configuration is used.
diff --git a/pkgs/patches-linux-5.15/321-powerpc_crtsavres_prereq.patch b/pkgs/patches-linux-5.15/321-powerpc_crtsavres_prereq.patch
new file mode 100644
index 0000000..f1942e2
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch b/pkgs/patches-linux-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
new file mode 100644
index 0000000..3a5f5a2
--- /dev/null
+++ b/pkgs/patches-linux-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
@@ -0,0 +1,283 @@
+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
+ /*
+@@ -181,9 +182,15 @@ 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
+
+-relocate_new_kernel_size:
+- EXPORT(relocate_new_kernel_size)
+- PTR_WD relocate_new_kernel_end - relocate_new_kernel
+- .size relocate_new_kernel_size, PTRSIZE
++kexec_relocate_new_kernel_end:
++ EXPORT(kexec_relocate_new_kernel_end)
diff --git a/pkgs/patches-linux-5.15/332-arc-add-OWRTDTB-section.patch b/pkgs/patches-linux-5.15/332-arc-add-OWRTDTB-section.patch
new file mode 100644
index 0000000..30158cf
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/333-arc-enable-unaligned-access-in-kernel-mode.patch b/pkgs/patches-linux-5.15/333-arc-enable-unaligned-access-in-kernel-mode.patch
new file mode 100644
index 0000000..1848a84
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch b/pkgs/patches-linux-5.15/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch
new file mode 100644
index 0000000..082b122
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/343-netfilter-nft_flow_offload-handle-netdevice-events-f.patch b/pkgs/patches-linux-5.15/343-netfilter-nft_flow_offload-handle-netdevice-events-f.patch
new file mode 100644
index 0000000..19ec9d9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/343-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
+@@ -608,13 +608,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/pkgs/patches-linux-5.15/400-find_active_root.patch b/pkgs/patches-linux-5.15/400-find_active_root.patch
new file mode 100644
index 0000000..5582d20
--- /dev/null
+++ b/pkgs/patches-linux-5.15/400-find_active_root.patch
@@ -0,0 +1,60 @@
+The WRT1900AC among other Linksys routers uses a dual-firmware layout.
+Dynamically rename the active partition to "ubi".
+
+Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
+
+--- a/drivers/mtd/parsers/ofpart_core.c
++++ b/drivers/mtd/parsers/ofpart_core.c
+@@ -38,6 +38,8 @@ static bool node_has_compatible(struct d
+ return of_get_property(pp, "compatible", NULL);
+ }
+
++static int mangled_rootblock;
++
+ static int parse_fixed_partitions(struct mtd_info *master,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+@@ -48,6 +50,7 @@ static int parse_fixed_partitions(struct
+ struct device_node *mtd_node;
+ struct device_node *ofpart_node;
+ const char *partname;
++ const char *owrtpart = "ubi";
+ struct device_node *pp;
+ int nr_parts, i, ret = 0;
+ bool dedicated = true;
+@@ -133,9 +136,13 @@ static int parse_fixed_partitions(struct
+ parts[i].size = of_read_number(reg + a_cells, s_cells);
+ parts[i].of_node = pp;
+
+- partname = of_get_property(pp, "label", &len);
+- if (!partname)
+- partname = of_get_property(pp, "name", &len);
++ if (mangled_rootblock && (i == mangled_rootblock)) {
++ partname = owrtpart;
++ } else {
++ partname = of_get_property(pp, "label", &len);
++ if (!partname)
++ partname = of_get_property(pp, "name", &len);
++ }
+ parts[i].name = partname;
+
+ if (of_get_property(pp, "read-only", &len))
+@@ -252,6 +259,18 @@ static int __init ofpart_parser_init(voi
+ return 0;
+ }
+
++static int __init active_root(char *str)
++{
++ get_option(&str, &mangled_rootblock);
++
++ if (!mangled_rootblock)
++ return 1;
++
++ return 1;
++}
++
++__setup("mangled_rootblock=", active_root);
++
+ static void __exit ofpart_parser_exit(void)
+ {
+ deregister_mtd_parser(&ofpart_parser);
diff --git a/pkgs/patches-linux-5.15/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch b/pkgs/patches-linux-5.15/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch
new file mode 100644
index 0000000..1f3aae1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/401-v5.20-mtd-parsers-add-support-for-Sercomm-partitions.patch b/pkgs/patches-linux-5.15/401-v5.20-mtd-parsers-add-support-for-Sercomm-partitions.patch
new file mode 100644
index 0000000..113a96a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/401-v5.20-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/pkgs/patches-linux-5.15/402-mtd-blktrans-call-add-disks-after-mtd-device.patch b/pkgs/patches-linux-5.15/402-mtd-blktrans-call-add-disks-after-mtd-device.patch
new file mode 100644
index 0000000..011f790
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -31,6 +31,7 @@
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/mtd/blktrans.h>
+
+ #include "mtdcore.h"
+
+@@ -1073,6 +1074,8 @@ int mtd_device_parse_register(struct mtd
+
+ ret = mtd_otp_nvmem_add(mtd);
+
++ register_mtd_blktrans_devs();
++
+ out:
+ if (ret && device_is_registered(&mtd->dev))
+ del_mtd_device(mtd);
+--- 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/pkgs/patches-linux-5.15/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/pkgs/patches-linux-5.15/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch
new file mode 100644
index 0000000..ed9d1c9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -168,6 +168,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)
+ {
+@@ -313,6 +322,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;
+ }
+
+@@ -2388,6 +2390,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;
+@@ -2420,8 +2423,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];
+ }
+ }
+
+@@ -2429,6 +2433,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
+@@ -243,6 +243,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/pkgs/patches-linux-5.15/402-v5.20-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch b/pkgs/patches-linux-5.15/402-v5.20-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch
new file mode 100644
index 0000000..8b8e478
--- /dev/null
+++ b/pkgs/patches-linux-5.15/402-v5.20-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/pkgs/patches-linux-5.15/410-block-fit-partition-parser.patch b/pkgs/patches-linux-5.15/410-block-fit-partition-parser.patch
new file mode 100644
index 0000000..96c89f4
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch b/pkgs/patches-linux-5.15/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch
new file mode 100644
index 0000000..5c49841
--- /dev/null
+++ b/pkgs/patches-linux-5.15/410-mtd-next-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/pkgs/patches-linux-5.15/420-mtd-redboot_space.patch b/pkgs/patches-linux-5.15/420-mtd-redboot_space.patch
new file mode 100644
index 0000000..fee1936
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -277,14 +277,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/pkgs/patches-linux-5.15/420-mtd-set-rootfs-to-be-root-dev.patch b/pkgs/patches-linux-5.15/420-mtd-set-rootfs-to-be-root-dev.patch
new file mode 100644
index 0000000..90254c6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/420-mtd-set-rootfs-to-be-root-dev.patch
@@ -0,0 +1,39 @@
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: kernel/3.1[02]: move MTD root device setup code to mtdcore
+
+The current code only allows to automatically set
+root device on MTD partitions. Move the code to MTD
+core to allow to use it with all MTD devices.
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/mtd/mtdcore.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -27,6 +27,7 @@
+ #include <linux/reboot.h>
+ #include <linux/leds.h>
+ #include <linux/debugfs.h>
++#include <linux/root_dev.h>
+ #include <linux/nvmem-provider.h>
+
+ #include <linux/mtd/mtd.h>
+@@ -768,6 +769,16 @@ int add_mtd_device(struct mtd_info *mtd)
+ of this try_ nonsense, and no bitching about it
+ either. :) */
+ __module_get(THIS_MODULE);
++
++ if (!strcmp(mtd->name, "rootfs") &&
++ IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
++ ROOT_DEV == 0) {
++ unsigned int index = mtd->index;
++ pr_notice("mtd: device %d (%s) set to be root filesystem\n",
++ mtd->index, mtd->name);
++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, index);
++ }
++
+ return 0;
+
+ fail_nvmem_add:
diff --git a/pkgs/patches-linux-5.15/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch b/pkgs/patches-linux-5.15/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch
new file mode 100644
index 0000000..181c912
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch b/pkgs/patches-linux-5.15/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch
new file mode 100644
index 0000000..3a1cc9e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch b/pkgs/patches-linux-5.15/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch
new file mode 100644
index 0000000..cee9d9d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch b/pkgs/patches-linux-5.15/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch
new file mode 100644
index 0000000..d63113e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch b/pkgs/patches-linux-5.15/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch
new file mode 100644
index 0000000..965a331
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/430-mtd-add-myloader-partition-parser.patch b/pkgs/patches-linux-5.15/430-mtd-add-myloader-partition-parser.patch
new file mode 100644
index 0000000..0889c9a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/430-mtk-bmt-support.patch b/pkgs/patches-linux-5.15/430-mtk-bmt-support.patch
new file mode 100644
index 0000000..2a83f46
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch b/pkgs/patches-linux-5.15/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch
new file mode 100644
index 0000000..bcea45d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch b/pkgs/patches-linux-5.15/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch
new file mode 100644
index 0000000..852654d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch b/pkgs/patches-linux-5.15/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch
new file mode 100644
index 0000000..0be74a5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch b/pkgs/patches-linux-5.15/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch
new file mode 100644
index 0000000..ca56de8
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/465-m25p80-mx-disable-software-protection.patch b/pkgs/patches-linux-5.15/465-m25p80-mx-disable-software-protection.patch
new file mode 100644
index 0000000..f58d545
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/476-mtd-spi-nor-add-eon-en25q128.patch b/pkgs/patches-linux-5.15/476-mtd-spi-nor-add-eon-en25q128.patch
new file mode 100644
index 0000000..325fca6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -15,6 +15,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/pkgs/patches-linux-5.15/479-mtd-spi-nor-add-xtx-xt25f128b.patch b/pkgs/patches-linux-5.15/479-mtd-spi-nor-add-xtx-xt25f128b.patch
new file mode 100644
index 0000000..58a336a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -1857,6 +1857,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/pkgs/patches-linux-5.15/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch b/pkgs/patches-linux-5.15/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch
new file mode 100644
index 0000000..c32cde5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/482-mtd-spi-nor-add-gd25q512.patch b/pkgs/patches-linux-5.15/482-mtd-spi-nor-add-gd25q512.patch
new file mode 100644
index 0000000..96848e1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/482-mtd-spi-nor-add-gd25q512.patch
@@ -0,0 +1,25 @@
+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(+)
+
+diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c
+index e52ed19d864f..d25292ec1eb9 100644
+--- a/drivers/mtd/spi-nor/gigadevice.c
++++ b/drivers/mtd/spi-nor/gigadevice.c
+@@ -53,6 +53,9 @@ static const struct flash_info gigadevice_parts[] = {
+ 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/pkgs/patches-linux-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch b/pkgs/patches-linux-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch
new file mode 100644
index 0000000..a73c9fa
--- /dev/null
+++ b/pkgs/patches-linux-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch
@@ -0,0 +1,178 @@
+From a07e31adf2753cad2fd9790db5bfc047c81e8152 Mon Sep 17 00:00:00 2001
+From: Felix Matouschek <felix@matouschek.org>
+Date: Fri, 2 Jul 2021 20:31:23 +0200
+Subject: [PATCH] mtd: spinand: Add support for XTX XT26G0xA
+
+Add support for XTX Technology XT26G01AXXXXX, XTX26G02AXXXXX and
+XTX26G04AXXXXX SPI NAND.
+
+These are 3V, 1G/2G/4Gbit serial SLC NAND flash devices with on-die ECC
+(8bit strength per 512bytes).
+
+Tested on Teltonika RUTX10 flashed with OpenWrt.
+
+Datasheets available at
+http://www.xtxtech.com/download/?AId=225
+https://datasheet.lcsc.com/szlcsc/2005251034_XTX-XT26G01AWSEGA_C558841.pdf
+
+Signed-off-by: Felix Matouschek <felix@matouschek.org>
+---
+ drivers/mtd/nand/spi/Makefile | 2 +-
+ drivers/mtd/nand/spi/core.c | 1 +
+ drivers/mtd/nand/spi/xtx.c | 122 ++++++++++++++++++++++++++++++++++
+ include/linux/mtd/spinand.h | 1 +
+ 4 files changed, 125 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/mtd/nand/spi/xtx.c
+
+--- a/drivers/mtd/nand/spi/Makefile
++++ b/drivers/mtd/nand/spi/Makefile
+@@ -1,3 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
+-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o
++spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
+ obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -902,6 +902,7 @@ static const struct spinand_manufacturer
+ &paragon_spinand_manufacturer,
+ &toshiba_spinand_manufacturer,
+ &winbond_spinand_manufacturer,
++ &xtx_spinand_manufacturer,
+ };
+
+ static int spinand_manufacturer_match(struct spinand_device *spinand,
+--- /dev/null
++++ b/drivers/mtd/nand/spi/xtx.c
+@@ -0,0 +1,122 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Author:
++ * Felix Matouschek <felix@matouschek.org>
++ */
++
++#include <linux/device.h>
++#include <linux/kernel.h>
++#include <linux/mtd/spinand.h>
++
++#define SPINAND_MFR_XTX 0x0B
++
++#define XT26G0XA_STATUS_ECC_MASK GENMASK(5, 2)
++#define XT26G0XA_STATUS_ECC_NO_DETECTED (0 << 2)
++#define XT26G0XA_STATUS_ECC_8_CORRECTED (3 << 4)
++#define XT26G0XA_STATUS_ECC_UNCOR_ERROR (2 << 4)
++
++static SPINAND_OP_VARIANTS(read_cache_variants,
++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
++
++static SPINAND_OP_VARIANTS(write_cache_variants,
++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
++ SPINAND_PROG_LOAD(true, 0, NULL, 0));
++
++static SPINAND_OP_VARIANTS(update_cache_variants,
++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
++ SPINAND_PROG_LOAD(false, 0, NULL, 0));
++
++static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section)
++ return -ERANGE;
++
++ region->offset = 48;
++ region->length = 16;
++
++ return 0;
++}
++
++static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section)
++ return -ERANGE;
++
++ region->offset = 1;
++ region->length = 47;
++
++ return 0;
++}
++
++static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = {
++ .ecc = xt26g0xa_ooblayout_ecc,
++ .free = xt26g0xa_ooblayout_free,
++};
++
++static int xt26g0xa_ecc_get_status(struct spinand_device *spinand,
++ u8 status)
++{
++ switch (status & XT26G0XA_STATUS_ECC_MASK) {
++ case XT26G0XA_STATUS_ECC_NO_DETECTED:
++ return 0;
++ case XT26G0XA_STATUS_ECC_8_CORRECTED:
++ return 8;
++ case XT26G0XA_STATUS_ECC_UNCOR_ERROR:
++ return -EBADMSG;
++ default: /* (1 << 2) through (7 << 2) are 1-7 corrected errors */
++ return (status & XT26G0XA_STATUS_ECC_MASK) >> 2;
++ }
++
++ return -EINVAL;
++}
++
++static const struct spinand_info xtx_spinand_table[] = {
++ SPINAND_INFO("XT26G01A",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&xt26g0xa_ooblayout,
++ xt26g0xa_ecc_get_status)),
++ SPINAND_INFO("XT26G02A",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2),
++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&xt26g0xa_ooblayout,
++ xt26g0xa_ecc_get_status)),
++ SPINAND_INFO("XT26G04A",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3),
++ NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&xt26g0xa_ooblayout,
++ xt26g0xa_ecc_get_status)),
++};
++
++static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
++};
++
++const struct spinand_manufacturer xtx_spinand_manufacturer = {
++ .id = SPINAND_MFR_XTX,
++ .name = "XTX",
++ .chips = xtx_spinand_table,
++ .nchips = ARRAY_SIZE(xtx_spinand_table),
++ .ops = &xtx_spinand_manuf_ops,
++};
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -266,6 +266,7 @@ extern const struct spinand_manufacturer
+ extern const struct spinand_manufacturer paragon_spinand_manufacturer;
+ extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
+ extern const struct spinand_manufacturer winbond_spinand_manufacturer;
++extern const struct spinand_manufacturer xtx_spinand_manufacturer;
+
+ /**
+ * struct spinand_op_variants - SPI NAND operation variants
diff --git a/pkgs/patches-linux-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch b/pkgs/patches-linux-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch
new file mode 100644
index 0000000..d5b7259
--- /dev/null
+++ b/pkgs/patches-linux-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch
@@ -0,0 +1,27 @@
+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(+)
+
+diff --git a/drivers/mtd/spi-nor/esmt.c b/drivers/mtd/spi-nor/esmt.c
+index cfc9218c1053..d1750807f005 100644
+--- a/drivers/mtd/spi-nor/esmt.c
++++ b/drivers/mtd/spi-nor/esmt.c
+@@ -10,6 +10,8 @@
+
+ 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/pkgs/patches-linux-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch b/pkgs/patches-linux-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch
new file mode 100644
index 0000000..2823464
--- /dev/null
+++ b/pkgs/patches-linux-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch
@@ -0,0 +1,28 @@
+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(+)
+
+diff --git a/drivers/mtd/spi-nor/xmc.c b/drivers/mtd/spi-nor/xmc.c
+index 2c7773b68993..99ee43a654c4 100644
+--- a/drivers/mtd/spi-nor/xmc.c
++++ b/drivers/mtd/spi-nor/xmc.c
+@@ -14,6 +14,8 @@ static const struct flash_info xmc_parts[] = {
+ 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/pkgs/patches-linux-5.15/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch b/pkgs/patches-linux-5.15/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch
new file mode 100644
index 0000000..c170fed
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -896,6 +896,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/pkgs/patches-linux-5.15/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch b/pkgs/patches-linux-5.15/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch
new file mode 100644
index 0000000..b120548
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -1184,6 +1184,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;
+@@ -1267,6 +1334,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/pkgs/patches-linux-5.15/491-ubi-auto-create-ubiblock-device-for-rootfs.patch b/pkgs/patches-linux-5.15/491-ubi-auto-create-ubiblock-device-for-rootfs.patch
new file mode 100644
index 0000000..ae53770
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch b/pkgs/patches-linux-5.15/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch
new file mode 100644
index 0000000..cf41c8c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch b/pkgs/patches-linux-5.15/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch
new file mode 100644
index 0000000..266a633
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/494-mtd-ubi-add-EOF-marker-support.patch b/pkgs/patches-linux-5.15/494-mtd-ubi-add-EOF-marker-support.patch
new file mode 100644
index 0000000..4134317
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/495-mtd-core-add-get_mtd_device_by_node.patch b/pkgs/patches-linux-5.15/495-mtd-core-add-get_mtd_device_by_node.patch
new file mode 100644
index 0000000..431c807
--- /dev/null
+++ b/pkgs/patches-linux-5.15/495-mtd-core-add-get_mtd_device_by_node.patch
@@ -0,0 +1,75 @@
+From 1bd1b740f208d1cf4071932cc51860d37266c402 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Sat, 1 Sep 2018 00:30:11 +0200
+Subject: [PATCH 495/497] mtd: core: add get_mtd_device_by_node
+
+Add function to retrieve a mtd device by its OF node. Since drivers can
+assign arbitrary names to mtd devices in the absence of a label
+property, there is no other reliable way to retrieve a mtd device for a
+given OF node.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/mtd/mtdcore.c | 38 ++++++++++++++++++++++++++++++++++++++
+ include/linux/mtd/mtd.h | 2 ++
+ 2 files changed, 40 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -1274,6 +1274,44 @@ out_unlock:
+ }
+ EXPORT_SYMBOL_GPL(get_mtd_device_nm);
+
++/**
++ * get_mtd_device_by_node - obtain a validated handle for an MTD device
++ * by of_node
++ * @of_node: OF node of MTD device to open
++ *
++ * This function returns MTD device description structure in case of
++ * success and an error code in case of failure.
++ */
++struct mtd_info *get_mtd_device_by_node(const struct device_node *of_node)
++{
++ int err = -ENODEV;
++ struct mtd_info *mtd = NULL, *other;
++
++ mutex_lock(&mtd_table_mutex);
++
++ mtd_for_each_device(other) {
++ if (of_node == other->dev.of_node) {
++ mtd = other;
++ break;
++ }
++ }
++
++ if (!mtd)
++ goto out_unlock;
++
++ err = __get_mtd_device(mtd);
++ if (err)
++ goto out_unlock;
++
++ mutex_unlock(&mtd_table_mutex);
++ return mtd;
++
++out_unlock:
++ mutex_unlock(&mtd_table_mutex);
++ return ERR_PTR(err);
++}
++EXPORT_SYMBOL_GPL(get_mtd_device_by_node);
++
+ void put_mtd_device(struct mtd_info *mtd)
+ {
+ mutex_lock(&mtd_table_mutex);
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -703,6 +703,8 @@ extern struct mtd_info *get_mtd_device(s
+ extern int __get_mtd_device(struct mtd_info *mtd);
+ extern void __put_mtd_device(struct mtd_info *mtd);
+ extern struct mtd_info *get_mtd_device_nm(const char *name);
++extern struct mtd_info *get_mtd_device_by_node(
++ const struct device_node *of_node);
+ extern void put_mtd_device(struct mtd_info *mtd);
+
+ static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
diff --git a/pkgs/patches-linux-5.15/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/pkgs/patches-linux-5.15/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch
new file mode 100644
index 0000000..01f3b9e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/pkgs/patches-linux-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch
new file mode 100644
index 0000000..3216801
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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 = 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/pkgs/patches-linux-5.15/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch b/pkgs/patches-linux-5.15/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch
new file mode 100644
index 0000000..81de764
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch b/pkgs/patches-linux-5.15/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch
new file mode 100644
index 0000000..ec14f63
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/500-fs_cdrom_dependencies.patch b/pkgs/patches-linux-5.15/500-fs_cdrom_dependencies.patch
new file mode 100644
index 0000000..b46865f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/500-fs_cdrom_dependencies.patch
@@ -0,0 +1,62 @@
+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(+)
+
+diff --git a/fs/hfs/Kconfig b/fs/hfs/Kconfig
+index 129926b5142d..e0d2c647aa07 100644
+--- a/fs/hfs/Kconfig
++++ b/fs/hfs/Kconfig
+@@ -2,6 +2,7 @@
+ 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
+diff --git a/fs/hfsplus/Kconfig b/fs/hfsplus/Kconfig
+index 7d4229aecec0..648d91d1881f 100644
+--- a/fs/hfsplus/Kconfig
++++ b/fs/hfsplus/Kconfig
+@@ -2,6 +2,7 @@
+ config HFSPLUS_FS
+ tristate "Apple Extended HFS file system support"
+ depends on BLOCK
++ select CDROM
+ select NLS
+ select NLS_UTF8
+ help
+diff --git a/fs/isofs/Kconfig b/fs/isofs/Kconfig
+index 08ffd37b9bb8..f74680379207 100644
+--- a/fs/isofs/Kconfig
++++ b/fs/isofs/Kconfig
+@@ -1,6 +1,7 @@
+ # 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
+diff --git a/fs/udf/Kconfig b/fs/udf/Kconfig
+index 26e1a49f3ba7..3f85a084d2b5 100644
+--- a/fs/udf/Kconfig
++++ b/fs/udf/Kconfig
+@@ -1,6 +1,7 @@
+ # 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/pkgs/patches-linux-5.15/530-jffs2_make_lzma_available.patch b/pkgs/patches-linux-5.15/530-jffs2_make_lzma_available.patch
new file mode 100644
index 0000000..fd6ca78
--- /dev/null
+++ b/pkgs/patches-linux-5.15/530-jffs2_make_lzma_available.patch
@@ -0,0 +1,5180 @@
+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,115 @@
++/* 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)
++
++int MatchFinder_NeedMove(CMatchFinder *p);
++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
++void MatchFinder_MoveBlock(CMatchFinder *p);
++void MatchFinder_ReadIfRequired(CMatchFinder *p);
++
++void MatchFinder_Construct(CMatchFinder *p);
++
++/* Conditions:
++ 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);
++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems);
++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue);
++
++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son,
++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
++ UInt32 *distances, UInt32 maxLen);
++
++/*
++Conditions:
++ 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);
++
++void MatchFinder_Init(CMatchFinder *p);
++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
++
++#ifdef __cplusplus
++}
++#endif
++
++#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,231 @@
++/* 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;
++
++/* LzmaProps_Decode - decodes properties
++Returns:
++ SZ_OK
++ SZ_ERROR_UNSUPPORTED - Unsupported properties
++*/
++
++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
++
++
++/* ---------- LZMA Decoder state ---------- */
++
++/* 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; }
++
++void LzmaDec_Init(CLzmaDec *p);
++
++/* There are two types of LZMA streams:
++ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
++ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */
++
++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 */
++
++
++/* ---------- Interfaces ---------- */
++
++/* There are 3 levels of interfaces:
++ 1) Dictionary Interface
++ 2) Buffer Interface
++ 3) One Call Interface
++ You can select any of these interfaces, but don't mix functions from different
++ groups for same object. */
++
++
++/* There are two variants to allocate state for Dictionary Interface:
++ 1) LzmaDec_Allocate / LzmaDec_Free
++ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
++ You can use variant 2, if you set dictionary buffer manually.
++ For Buffer Interface you must always use variant 1.
++
++LzmaDec_Allocate* can return:
++ SZ_OK
++ SZ_ERROR_MEM - Memory allocation error
++ SZ_ERROR_UNSUPPORTED - Unsupported properties
++*/
++
++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc);
++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc);
++
++SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc);
++void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc);
++
++/* ---------- Dictionary Interface ---------- */
++
++/* You can use it, if you want to eliminate the overhead for data copying from
++ dictionary to some other external buffer.
++ You must work with CLzmaDec variables directly in this interface.
++
++ STEPS:
++ LzmaDec_Constr()
++ LzmaDec_Allocate()
++ for (each new stream)
++ {
++ LzmaDec_Init()
++ while (it needs more decompression)
++ {
++ LzmaDec_DecodeToDic()
++ use data from CLzmaDec::dic and update CLzmaDec::dicPos
++ }
++ }
++ LzmaDec_Free()
++*/
++
++/* LzmaDec_DecodeToDic
++
++ The decoding to internal dictionary buffer (CLzmaDec::dic).
++ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
++
++finishMode:
++ It has meaning only if the decoding reaches output limit (dicLimit).
++ LZMA_FINISH_ANY - Decode just dicLimit bytes.
++ LZMA_FINISH_END - Stream must be finished after dicLimit.
++
++Returns:
++ SZ_OK
++ status:
++ LZMA_STATUS_FINISHED_WITH_MARK
++ LZMA_STATUS_NOT_FINISHED
++ LZMA_STATUS_NEEDS_MORE_INPUT
++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
++ SZ_ERROR_DATA - Data error
++*/
++
++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
++
++
++/* ---------- Buffer Interface ---------- */
++
++/* It's zlib-like interface.
++ See LzmaDec_DecodeToDic description for information about STEPS and return results,
++ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
++ to work with CLzmaDec variables manually.
++
++finishMode:
++ It has meaning only if the decoding reaches output limit (*destLen).
++ LZMA_FINISH_ANY - Decode just destLen bytes.
++ LZMA_FINISH_END - Stream must be finished after (*destLen).
++*/
++
++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
++
++
++/* ---------- One Call Interface ---------- */
++
++/* LzmaDecode
++
++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,80 @@
++/* 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);
++void LzmaEncProps_Normalize(CLzmaEncProps *p);
++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2);
++
++
++/* ---------- 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_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream,
++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
++SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
++
++/* ---------- One Call Interface ---------- */
++
++/* LzmaEncode
++Return code:
++ SZ_OK - OK
++ SZ_ERROR_MEM - Memory allocation error
++ SZ_ERROR_PARAM - Incorrect paramater
++ SZ_ERROR_OUTPUT_EOF - output buffer overflow
++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
++*/
++
++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
++
++#ifdef __cplusplus
++}
++#endif
++
++#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,761 @@
++/* 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
++
++static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc)
++{
++ if (!p->directInput)
++ {
++ 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 (p->directInput)
++ {
++ 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);
++}
++
++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
++Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
++
++UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
++
++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 (p->directInput)
++ {
++ 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;
++ }
++}
++
++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;
++}
++
++int MatchFinder_NeedMove(CMatchFinder *p)
++{
++ if (p->directInput)
++ return 0;
++ /* if (p->streamEndWasReached) return 0; */
++ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter);
++}
++
++void MatchFinder_ReadIfRequired(CMatchFinder *p)
++{
++ if (p->streamEndWasReached)
++ return;
++ if (p->keepSizeAfter >= p->streamPos - p->pos)
++ MatchFinder_ReadBlock(p);
++}
++
++static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p)
++{
++ if (MatchFinder_NeedMove(p))
++ 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;
++}
++
++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;
++}
++
++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 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
++ UInt32 *distances, UInt32 maxLen)
++{
++ son[_cyclicBufferPos] = curMatch;
++ for (;;)
++ {
++ UInt32 delta = pos - curMatch;
++ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
++ return distances;
++ {
++ const Byte *pb = cur - delta;
++ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
++ if (pb[maxLen] == cur[maxLen] && *pb == *cur)
++ {
++ UInt32 len = 0;
++ while (++len != lenLimit)
++ if (pb[len] != cur[len])
++ break;
++ if (maxLen < len)
++ {
++ *distances++ = maxLen = len;
++ *distances++ = delta - 1;
++ if (len == lenLimit)
++ return distances;
++ }
++ }
++ }
++ }
++}
++
++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
++ 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);
++
++#define MOVE_POS_RET MOVE_POS return offset;
++
++static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; }
++
++#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)); MOVE_POS;
++
++static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 offset;
++ GET_MATCHES_HEADER(2)
++ HASH2_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ offset = 0;
++ GET_MATCHES_FOOTER(offset, 1)
++}
++
++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 offset;
++ GET_MATCHES_HEADER(3)
++ HASH_ZIP_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ offset = 0;
++ GET_MATCHES_FOOTER(offset, 2)
++}
++
++static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 hash2Value, delta2, maxLen, offset;
++ GET_MATCHES_HEADER(3)
++
++ HASH3_CALC;
++
++ delta2 = p->pos - p->hash[hash2Value];
++ curMatch = p->hash[kFix3HashSize + hashValue];
++
++ p->hash[hash2Value] =
++ p->hash[kFix3HashSize + hashValue] = p->pos;
++
++
++ maxLen = 2;
++ offset = 0;
++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
++ {
++ for (; maxLen != lenLimit; maxLen++)
++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
++ break;
++ distances[0] = maxLen;
++ distances[1] = delta2 - 1;
++ offset = 2;
++ if (maxLen == lenLimit)
++ {
++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
++ MOVE_POS_RET;
++ }
++ }
++ GET_MATCHES_FOOTER(offset, maxLen)
++}
++
++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 UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
++ GET_MATCHES_HEADER(4)
++
++ HASH4_CALC;
++
++ delta2 = p->pos - p->hash[ hash2Value];
++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
++ curMatch = p->hash[kFix4HashSize + hashValue];
++
++ p->hash[ hash2Value] =
++ p->hash[kFix3HashSize + hash3Value] =
++ p->hash[kFix4HashSize + hashValue] = p->pos;
++
++ maxLen = 1;
++ offset = 0;
++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
++ {
++ distances[0] = maxLen = 2;
++ distances[1] = delta2 - 1;
++ offset = 2;
++ }
++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
++ {
++ maxLen = 3;
++ distances[offset + 1] = delta3 - 1;
++ offset += 2;
++ delta2 = delta3;
++ }
++ if (offset != 0)
++ {
++ for (; maxLen != lenLimit; maxLen++)
++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
++ break;
++ distances[offset - 2] = maxLen;
++ if (maxLen == lenLimit)
++ {
++ p->son[p->cyclicBufferPos] = curMatch;
++ MOVE_POS_RET;
++ }
++ }
++ if (maxLen < 3)
++ maxLen = 3;
++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
++ distances + offset, maxLen) - (distances));
++ MOVE_POS_RET
++}
++
++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
++{
++ UInt32 offset;
++ GET_MATCHES_HEADER(3)
++ HASH_ZIP_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
++ distances, 2) - (distances));
++ MOVE_POS_RET
++}
++
++static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ SKIP_HEADER(2)
++ HASH2_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ SKIP_FOOTER
++ }
++ while (--num != 0);
++}
++
++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ SKIP_HEADER(3)
++ HASH_ZIP_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ SKIP_FOOTER
++ }
++ while (--num != 0);
++}
++
++static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ UInt32 hash2Value;
++ SKIP_HEADER(3)
++ HASH3_CALC;
++ curMatch = p->hash[kFix3HashSize + hashValue];
++ p->hash[hash2Value] =
++ p->hash[kFix3HashSize + hashValue] = p->pos;
++ SKIP_FOOTER
++ }
++ while (--num != 0);
++}
++
++static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ 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);
++}
++
++static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ UInt32 hash2Value, hash3Value;
++ SKIP_HEADER(4)
++ HASH4_CALC;
++ curMatch = p->hash[kFix4HashSize + hashValue];
++ p->hash[ hash2Value] =
++ p->hash[kFix3HashSize + hash3Value] =
++ p->hash[kFix4HashSize + hashValue] = p->pos;
++ p->son[p->cyclicBufferPos] = curMatch;
++ MOVE_POS
++ }
++ while (--num != 0);
++}
++
++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
++{
++ do
++ {
++ SKIP_HEADER(3)
++ HASH_ZIP_CALC;
++ curMatch = p->hash[hashValue];
++ p->hash[hashValue] = p->pos;
++ p->son[p->cyclicBufferPos] = curMatch;
++ MOVE_POS
++ }
++ while (--num != 0);
++}
++
++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable)
++{
++ vTable->Init = (Mf_Init_Func)MatchFinder_Init;
++ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte;
++ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes;
++ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos;
++ if (!p->btMode)
++ {
++ vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip;
++ }
++ else if (p->numHashBytes == 2)
++ {
++ vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip;
++ }
++ else if (p->numHashBytes == 3)
++ {
++ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip;
++ }
++ else
++ {
++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
++ }
++}
+--- /dev/null
++++ b/lib/lzma/LzmaDec.c
+@@ -0,0 +1,999 @@
++/* 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;
++}
++
++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;
++}
++
++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;
++}
++
++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;
++}
++
++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
++{
++ SizeT outSize = *destLen;
++ SizeT inSize = *srcLen;
++ *srcLen = *destLen = 0;
++ for (;;)
++ {
++ SizeT inSizeCur = inSize, outSizeCur, dicPos;
++ ELzmaFinishMode curFinishMode;
++ SRes res;
++ if (p->dicPos == p->dicBufSize)
++ p->dicPos = 0;
++ dicPos = p->dicPos;
++ if (outSize > p->dicBufSize - dicPos)
++ {
++ outSizeCur = p->dicBufSize;
++ curFinishMode = LZMA_FINISH_ANY;
++ }
++ else
++ {
++ outSizeCur = dicPos + outSize;
++ curFinishMode = finishMode;
++ }
++
++ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status);
++ src += inSizeCur;
++ inSize -= inSizeCur;
++ *srcLen += inSizeCur;
++ outSizeCur = p->dicPos - dicPos;
++ memcpy(dest, p->dic + dicPos, outSizeCur);
++ dest += outSizeCur;
++ outSize -= outSizeCur;
++ *destLen += outSizeCur;
++ if (res != 0)
++ return res;
++ if (outSizeCur == 0 || outSize == 0)
++ return SZ_OK;
++ }
++}
++
++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->probs);
++ p->probs = 0;
++}
++
++static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc)
++{
++ alloc->Free(alloc, p->dic);
++ p->dic = 0;
++}
++
++void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc)
++{
++ LzmaDec_FreeProbs(p, alloc);
++ LzmaDec_FreeDict(p, alloc);
++}
++
++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
++{
++ 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;
++}
++
++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
++{
++ CLzmaProps propNew;
++ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
++ p->prop = propNew;
++ return SZ_OK;
++}
++
++SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
++{
++ CLzmaProps propNew;
++ SizeT dicBufSize;
++ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
++ dicBufSize = propNew.dicSize;
++ if (p->dic == 0 || dicBufSize != p->dicBufSize)
++ {
++ LzmaDec_FreeDict(p, alloc);
++ p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize);
++ if (p->dic == 0)
++ {
++ LzmaDec_FreeProbs(p, alloc);
++ return SZ_ERROR_MEM;
++ }
++ }
++ p->dicBufSize = dicBufSize;
++ p->prop = propNew;
++ return SZ_OK;
++}
++
++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,2271 @@
++/* 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;
++}
++
++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
++}
++
++UInt32 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); }
++
++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)
++
++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;
++
++void LzmaEnc_SaveState(CLzmaEncHandle pp)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ CSaveState *dest = &p->saveState;
++ int i;
++ dest->lenEnc = p->lenEnc;
++ dest->repLenEnc = p->repLenEnc;
++ dest->state = p->state;
++
++ for (i = 0; i < kNumStates; i++)
++ {
++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
++ }
++ for (i = 0; i < kNumLenToPosStates; i++)
++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
++ memcpy(dest->reps, p->reps, sizeof(p->reps));
++ memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb));
++}
++
++void LzmaEnc_RestoreState(CLzmaEncHandle pp)
++{
++ CLzmaEnc *dest = (CLzmaEnc *)pp;
++ const CSaveState *p = &dest->saveState;
++ int i;
++ dest->lenEnc = p->lenEnc;
++ dest->repLenEnc = p->repLenEnc;
++ dest->state = p->state;
++
++ for (i = 0; i < kNumStates; i++)
++ {
++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
++ }
++ for (i = 0; i < kNumLenToPosStates; i++)
++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
++ memcpy(dest->reps, p->reps, sizeof(p->reps));
++ memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb));
++}
++
++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2)
++{
++ 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);
++}
++
++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;
++}
++
++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;
++}
++
++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;
++}
++
++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;
++}
++
++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;
++}
++
++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 SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream,
++ ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ p->matchFinderBase.stream = inStream;
++ p->needInit = 1;
++ p->rc.outStream = outStream;
++ return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig);
++}
++
++SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp,
++ ISeqInStream *inStream, UInt32 keepWindowSize,
++ ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ p->matchFinderBase.stream = inStream;
++ p->needInit = 1;
++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
++}
++
++static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen)
++{
++ p->matchFinderBase.directInput = 1;
++ p->matchFinderBase.bufferBase = (Byte *)src;
++ p->matchFinderBase.directInputRem = srcLen;
++}
++
++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);
++}
++
++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;
++}
++
++
++UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp)
++{
++ const CLzmaEnc *p = (CLzmaEnc *)pp;
++ return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
++}
++
++const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp)
++{
++ const CLzmaEnc *p = (CLzmaEnc *)pp;
++ return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
++}
++
++SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit,
++ Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ UInt64 nowPos64;
++ SRes res;
++ CSeqOutStreamBuf outStream;
++
++ outStream.funcTable.Write = MyWrite;
++ outStream.data = dest;
++ outStream.rem = *destLen;
++ outStream.overflow = False;
++
++ p->writeEndMark = False;
++ p->finished = False;
++ p->result = SZ_OK;
++
++ if (reInit)
++ LzmaEnc_Init(p);
++ LzmaEnc_InitPrices(p);
++ nowPos64 = p->nowPos64;
++ RangeEnc_Init(&p->rc);
++ p->rc.outStream = &outStream.funcTable;
++
++ res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize);
++
++ *unpackSize = (UInt32)(p->nowPos64 - nowPos64);
++ *destLen -= outStream.rem;
++ if (outStream.overflow)
++ return SZ_ERROR_OUTPUT_EOF;
++
++ return res;
++}
++
++static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress)
++{
++ SRes res = SZ_OK;
++
++ #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_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress,
++ ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig));
++ return LzmaEnc_Encode2((CLzmaEnc *)pp, progress);
++}
++
++SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size)
++{
++ CLzmaEnc *p = (CLzmaEnc *)pp;
++ 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;
++}
++
++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
++{
++ CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc);
++ SRes res;
++ if (p == 0)
++ return SZ_ERROR_MEM;
++
++ res = LzmaEnc_SetProps(p, props);
++ if (res == SZ_OK)
++ {
++ res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize);
++ if (res == SZ_OK)
++ res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen,
++ writeEndMark, progress, alloc, allocBig);
++ }
++
++ LzmaEnc_Destroy(p, alloc, allocBig);
++ return res;
++}
+--- /dev/null
++++ b/lib/lzma/Makefile
+@@ -0,0 +1,7 @@
++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/pkgs/patches-linux-5.15/531-debloat_lzma.patch b/pkgs/patches-linux-5.15/531-debloat_lzma.patch
new file mode 100644
index 0000000..2f70eee
--- /dev/null
+++ b/pkgs/patches-linux-5.15/531-debloat_lzma.patch
@@ -0,0 +1,1040 @@
+From 3fd297761ac246c54d7723c57fca95c112b99465 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 15 Jul 2017 21:15:44 +0200
+Subject: lzma: de-bloat the lzma library used by jffs2
+
+lede-commit: 3fd1dd08fbcbb78b34efefd32c3032e5c99108d6
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/lzma/LzFind.h | 17 ---
+ include/linux/lzma/LzmaDec.h | 101 ---------------
+ include/linux/lzma/LzmaEnc.h | 20 ---
+ lib/lzma/LzFind.c | 287 ++++---------------------------------------
+ lib/lzma/LzmaDec.c | 86 +------------
+ lib/lzma/LzmaEnc.c | 172 ++------------------------
+ 6 files changed, 42 insertions(+), 641 deletions(-)
+
+--- a/include/linux/lzma/LzFind.h
++++ b/include/linux/lzma/LzFind.h
+@@ -55,11 +55,6 @@ typedef struct _CMatchFinder
+
+ #define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos)
+
+-int MatchFinder_NeedMove(CMatchFinder *p);
+-Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
+-void MatchFinder_MoveBlock(CMatchFinder *p);
+-void MatchFinder_ReadIfRequired(CMatchFinder *p);
+-
+ void MatchFinder_Construct(CMatchFinder *p);
+
+ /* Conditions:
+@@ -70,12 +65,6 @@ int MatchFinder_Create(CMatchFinder *p,
+ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
+ ISzAlloc *alloc);
+ void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc);
+-void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems);
+-void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue);
+-
+-UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son,
+- UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
+- UInt32 *distances, UInt32 maxLen);
+
+ /*
+ Conditions:
+@@ -102,12 +91,6 @@ typedef struct _IMatchFinder
+
+ void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable);
+
+-void MatchFinder_Init(CMatchFinder *p);
+-UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
+-UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
+-void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
+-void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
+-
+ #ifdef __cplusplus
+ }
+ #endif
+--- a/include/linux/lzma/LzmaDec.h
++++ b/include/linux/lzma/LzmaDec.h
+@@ -31,14 +31,6 @@ typedef struct _CLzmaProps
+ UInt32 dicSize;
+ } CLzmaProps;
+
+-/* LzmaProps_Decode - decodes properties
+-Returns:
+- SZ_OK
+- SZ_ERROR_UNSUPPORTED - Unsupported properties
+-*/
+-
+-SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
+-
+
+ /* ---------- LZMA Decoder state ---------- */
+
+@@ -70,8 +62,6 @@ typedef struct
+
+ #define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; }
+
+-void LzmaDec_Init(CLzmaDec *p);
+-
+ /* There are two types of LZMA streams:
+ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
+ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */
+@@ -108,97 +98,6 @@ typedef enum
+
+ /* ELzmaStatus is used only as output value for function call */
+
+-
+-/* ---------- Interfaces ---------- */
+-
+-/* There are 3 levels of interfaces:
+- 1) Dictionary Interface
+- 2) Buffer Interface
+- 3) One Call Interface
+- You can select any of these interfaces, but don't mix functions from different
+- groups for same object. */
+-
+-
+-/* There are two variants to allocate state for Dictionary Interface:
+- 1) LzmaDec_Allocate / LzmaDec_Free
+- 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
+- You can use variant 2, if you set dictionary buffer manually.
+- For Buffer Interface you must always use variant 1.
+-
+-LzmaDec_Allocate* can return:
+- SZ_OK
+- SZ_ERROR_MEM - Memory allocation error
+- SZ_ERROR_UNSUPPORTED - Unsupported properties
+-*/
+-
+-SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc);
+-void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc);
+-
+-SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc);
+-void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc);
+-
+-/* ---------- Dictionary Interface ---------- */
+-
+-/* You can use it, if you want to eliminate the overhead for data copying from
+- dictionary to some other external buffer.
+- You must work with CLzmaDec variables directly in this interface.
+-
+- STEPS:
+- LzmaDec_Constr()
+- LzmaDec_Allocate()
+- for (each new stream)
+- {
+- LzmaDec_Init()
+- while (it needs more decompression)
+- {
+- LzmaDec_DecodeToDic()
+- use data from CLzmaDec::dic and update CLzmaDec::dicPos
+- }
+- }
+- LzmaDec_Free()
+-*/
+-
+-/* LzmaDec_DecodeToDic
+-
+- The decoding to internal dictionary buffer (CLzmaDec::dic).
+- You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
+-
+-finishMode:
+- It has meaning only if the decoding reaches output limit (dicLimit).
+- LZMA_FINISH_ANY - Decode just dicLimit bytes.
+- LZMA_FINISH_END - Stream must be finished after dicLimit.
+-
+-Returns:
+- SZ_OK
+- status:
+- LZMA_STATUS_FINISHED_WITH_MARK
+- LZMA_STATUS_NOT_FINISHED
+- LZMA_STATUS_NEEDS_MORE_INPUT
+- LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
+- SZ_ERROR_DATA - Data error
+-*/
+-
+-SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
+- const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
+-
+-
+-/* ---------- Buffer Interface ---------- */
+-
+-/* It's zlib-like interface.
+- See LzmaDec_DecodeToDic description for information about STEPS and return results,
+- but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
+- to work with CLzmaDec variables manually.
+-
+-finishMode:
+- It has meaning only if the decoding reaches output limit (*destLen).
+- LZMA_FINISH_ANY - Decode just destLen bytes.
+- LZMA_FINISH_END - Stream must be finished after (*destLen).
+-*/
+-
+-SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
+- const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
+-
+-
+ /* ---------- One Call Interface ---------- */
+
+ /* LzmaDecode
+--- a/include/linux/lzma/LzmaEnc.h
++++ b/include/linux/lzma/LzmaEnc.h
+@@ -31,9 +31,6 @@ typedef struct _CLzmaEncProps
+ } CLzmaEncProps;
+
+ void LzmaEncProps_Init(CLzmaEncProps *p);
+-void LzmaEncProps_Normalize(CLzmaEncProps *p);
+-UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2);
+-
+
+ /* ---------- CLzmaEncHandle Interface ---------- */
+
+@@ -53,26 +50,9 @@ CLzmaEncHandle LzmaEnc_Create(ISzAlloc *
+ void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig);
+ SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props);
+ SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size);
+-SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream,
+- ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+ SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+
+-/* ---------- One Call Interface ---------- */
+-
+-/* LzmaEncode
+-Return code:
+- SZ_OK - OK
+- SZ_ERROR_MEM - Memory allocation error
+- SZ_ERROR_PARAM - Incorrect paramater
+- SZ_ERROR_OUTPUT_EOF - output buffer overflow
+- SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
+-*/
+-
+-SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+- const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
+- ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+-
+ #ifdef __cplusplus
+ }
+ #endif
+--- a/lib/lzma/LzFind.c
++++ b/lib/lzma/LzFind.c
+@@ -14,9 +14,15 @@
+
+ #define kStartMaxLen 3
+
++#if 0
++#define DIRECT_INPUT p->directInput
++#else
++#define DIRECT_INPUT 1
++#endif
++
+ static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc)
+ {
+- if (!p->directInput)
++ if (!DIRECT_INPUT)
+ {
+ alloc->Free(alloc, p->bufferBase);
+ p->bufferBase = 0;
+@@ -28,7 +34,7 @@ static void LzInWindow_Free(CMatchFinder
+ static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc)
+ {
+ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv;
+- if (p->directInput)
++ if (DIRECT_INPUT)
+ {
+ p->blockSize = blockSize;
+ return 1;
+@@ -42,12 +48,12 @@ static int LzInWindow_Create(CMatchFinde
+ return (p->bufferBase != 0);
+ }
+
+-Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
+-Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
++static Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
++static Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
+
+-UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
++static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
+
+-void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
++static void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
+ {
+ p->posLimit -= subValue;
+ p->pos -= subValue;
+@@ -58,7 +64,7 @@ static void MatchFinder_ReadBlock(CMatch
+ {
+ if (p->streamEndWasReached || p->result != SZ_OK)
+ return;
+- if (p->directInput)
++ if (DIRECT_INPUT)
+ {
+ UInt32 curSize = 0xFFFFFFFF - p->streamPos;
+ if (curSize > p->directInputRem)
+@@ -89,7 +95,7 @@ static void MatchFinder_ReadBlock(CMatch
+ }
+ }
+
+-void MatchFinder_MoveBlock(CMatchFinder *p)
++static void MatchFinder_MoveBlock(CMatchFinder *p)
+ {
+ memmove(p->bufferBase,
+ p->buffer - p->keepSizeBefore,
+@@ -97,22 +103,14 @@ void MatchFinder_MoveBlock(CMatchFinder
+ p->buffer = p->bufferBase + p->keepSizeBefore;
+ }
+
+-int MatchFinder_NeedMove(CMatchFinder *p)
++static int MatchFinder_NeedMove(CMatchFinder *p)
+ {
+- if (p->directInput)
++ if (DIRECT_INPUT)
+ return 0;
+ /* if (p->streamEndWasReached) return 0; */
+ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter);
+ }
+
+-void MatchFinder_ReadIfRequired(CMatchFinder *p)
+-{
+- if (p->streamEndWasReached)
+- return;
+- if (p->keepSizeAfter >= p->streamPos - p->pos)
+- MatchFinder_ReadBlock(p);
+-}
+-
+ static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p)
+ {
+ if (MatchFinder_NeedMove(p))
+@@ -268,7 +266,7 @@ static void MatchFinder_SetLimits(CMatch
+ p->posLimit = p->pos + limit;
+ }
+
+-void MatchFinder_Init(CMatchFinder *p)
++static void MatchFinder_Init(CMatchFinder *p)
+ {
+ UInt32 i;
+ for (i = 0; i < p->hashSizeSum; i++)
+@@ -287,7 +285,7 @@ static UInt32 MatchFinder_GetSubValue(CM
+ return (p->pos - p->historySize - 1) & kNormalizeMask;
+ }
+
+-void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
++static void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
+ {
+ UInt32 i;
+ for (i = 0; i < numItems; i++)
+@@ -319,38 +317,7 @@ static void MatchFinder_CheckLimits(CMat
+ MatchFinder_SetLimits(p);
+ }
+
+-static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+- UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
+- UInt32 *distances, UInt32 maxLen)
+-{
+- son[_cyclicBufferPos] = curMatch;
+- for (;;)
+- {
+- UInt32 delta = pos - curMatch;
+- if (cutValue-- == 0 || delta >= _cyclicBufferSize)
+- return distances;
+- {
+- const Byte *pb = cur - delta;
+- curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
+- if (pb[maxLen] == cur[maxLen] && *pb == *cur)
+- {
+- UInt32 len = 0;
+- while (++len != lenLimit)
+- if (pb[len] != cur[len])
+- break;
+- if (maxLen < len)
+- {
+- *distances++ = maxLen = len;
+- *distances++ = delta - 1;
+- if (len == lenLimit)
+- return distances;
+- }
+- }
+- }
+- }
+-}
+-
+-UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
++static UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
+ UInt32 *distances, UInt32 maxLen)
+ {
+@@ -460,10 +427,10 @@ static void SkipMatchesSpec(UInt32 lenLi
+ p->buffer++; \
+ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p);
+
+-#define MOVE_POS_RET MOVE_POS return offset;
+-
+ static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; }
+
++#define MOVE_POS_RET MatchFinder_MovePos(p); return offset;
++
+ #define GET_MATCHES_HEADER2(minLen, ret_op) \
+ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \
+ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \
+@@ -479,62 +446,7 @@ static void MatchFinder_MovePos(CMatchFi
+ distances + offset, maxLen) - distances); MOVE_POS_RET;
+
+ #define SKIP_FOOTER \
+- SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS;
+-
+-static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 offset;
+- GET_MATCHES_HEADER(2)
+- HASH2_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- offset = 0;
+- GET_MATCHES_FOOTER(offset, 1)
+-}
+-
+-UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 offset;
+- GET_MATCHES_HEADER(3)
+- HASH_ZIP_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- offset = 0;
+- GET_MATCHES_FOOTER(offset, 2)
+-}
+-
+-static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 hash2Value, delta2, maxLen, offset;
+- GET_MATCHES_HEADER(3)
+-
+- HASH3_CALC;
+-
+- delta2 = p->pos - p->hash[hash2Value];
+- curMatch = p->hash[kFix3HashSize + hashValue];
+-
+- p->hash[hash2Value] =
+- p->hash[kFix3HashSize + hashValue] = p->pos;
+-
+-
+- maxLen = 2;
+- offset = 0;
+- if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
+- {
+- for (; maxLen != lenLimit; maxLen++)
+- if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
+- break;
+- distances[0] = maxLen;
+- distances[1] = delta2 - 1;
+- offset = 2;
+- if (maxLen == lenLimit)
+- {
+- SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
+- MOVE_POS_RET;
+- }
+- }
+- GET_MATCHES_FOOTER(offset, maxLen)
+-}
++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MatchFinder_MovePos(p);
+
+ static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+ {
+@@ -583,108 +495,6 @@ static UInt32 Bt4_MatchFinder_GetMatches
+ GET_MATCHES_FOOTER(offset, maxLen)
+ }
+
+-static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
+- GET_MATCHES_HEADER(4)
+-
+- HASH4_CALC;
+-
+- delta2 = p->pos - p->hash[ hash2Value];
+- delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
+- curMatch = p->hash[kFix4HashSize + hashValue];
+-
+- p->hash[ hash2Value] =
+- p->hash[kFix3HashSize + hash3Value] =
+- p->hash[kFix4HashSize + hashValue] = p->pos;
+-
+- maxLen = 1;
+- offset = 0;
+- if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
+- {
+- distances[0] = maxLen = 2;
+- distances[1] = delta2 - 1;
+- offset = 2;
+- }
+- if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
+- {
+- maxLen = 3;
+- distances[offset + 1] = delta3 - 1;
+- offset += 2;
+- delta2 = delta3;
+- }
+- if (offset != 0)
+- {
+- for (; maxLen != lenLimit; maxLen++)
+- if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
+- break;
+- distances[offset - 2] = maxLen;
+- if (maxLen == lenLimit)
+- {
+- p->son[p->cyclicBufferPos] = curMatch;
+- MOVE_POS_RET;
+- }
+- }
+- if (maxLen < 3)
+- maxLen = 3;
+- offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
+- distances + offset, maxLen) - (distances));
+- MOVE_POS_RET
+-}
+-
+-UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+-{
+- UInt32 offset;
+- GET_MATCHES_HEADER(3)
+- HASH_ZIP_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
+- distances, 2) - (distances));
+- MOVE_POS_RET
+-}
+-
+-static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- SKIP_HEADER(2)
+- HASH2_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- SKIP_FOOTER
+- }
+- while (--num != 0);
+-}
+-
+-void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- SKIP_HEADER(3)
+- HASH_ZIP_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- SKIP_FOOTER
+- }
+- while (--num != 0);
+-}
+-
+-static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- UInt32 hash2Value;
+- SKIP_HEADER(3)
+- HASH3_CALC;
+- curMatch = p->hash[kFix3HashSize + hashValue];
+- p->hash[hash2Value] =
+- p->hash[kFix3HashSize + hashValue] = p->pos;
+- SKIP_FOOTER
+- }
+- while (--num != 0);
+-}
+-
+ static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+ {
+ do
+@@ -701,61 +511,12 @@ static void Bt4_MatchFinder_Skip(CMatchF
+ while (--num != 0);
+ }
+
+-static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- UInt32 hash2Value, hash3Value;
+- SKIP_HEADER(4)
+- HASH4_CALC;
+- curMatch = p->hash[kFix4HashSize + hashValue];
+- p->hash[ hash2Value] =
+- p->hash[kFix3HashSize + hash3Value] =
+- p->hash[kFix4HashSize + hashValue] = p->pos;
+- p->son[p->cyclicBufferPos] = curMatch;
+- MOVE_POS
+- }
+- while (--num != 0);
+-}
+-
+-void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+-{
+- do
+- {
+- SKIP_HEADER(3)
+- HASH_ZIP_CALC;
+- curMatch = p->hash[hashValue];
+- p->hash[hashValue] = p->pos;
+- p->son[p->cyclicBufferPos] = curMatch;
+- MOVE_POS
+- }
+- while (--num != 0);
+-}
+-
+ void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable)
+ {
+ vTable->Init = (Mf_Init_Func)MatchFinder_Init;
+ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte;
+ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes;
+ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos;
+- if (!p->btMode)
+- {
+- vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches;
+- vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip;
+- }
+- else if (p->numHashBytes == 2)
+- {
+- vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches;
+- vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip;
+- }
+- else if (p->numHashBytes == 3)
+- {
+- vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches;
+- vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip;
+- }
+- else
+- {
+- vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
+- vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
+- }
++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
+ }
+--- a/lib/lzma/LzmaDec.c
++++ b/lib/lzma/LzmaDec.c
+@@ -682,7 +682,7 @@ static void LzmaDec_InitRc(CLzmaDec *p,
+ p->needFlush = 0;
+ }
+
+-void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
++static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
+ {
+ p->needFlush = 1;
+ p->remainLen = 0;
+@@ -698,7 +698,7 @@ void LzmaDec_InitDicAndState(CLzmaDec *p
+ p->needInitState = 1;
+ }
+
+-void LzmaDec_Init(CLzmaDec *p)
++static void LzmaDec_Init(CLzmaDec *p)
+ {
+ p->dicPos = 0;
+ LzmaDec_InitDicAndState(p, True, True);
+@@ -716,7 +716,7 @@ static void LzmaDec_InitStateReal(CLzmaD
+ p->needInitState = 0;
+ }
+
+-SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
++static SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
+ ELzmaFinishMode finishMode, ELzmaStatus *status)
+ {
+ SizeT inSize = *srcLen;
+@@ -837,65 +837,13 @@ SRes LzmaDec_DecodeToDic(CLzmaDec *p, Si
+ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA;
+ }
+
+-SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
+-{
+- SizeT outSize = *destLen;
+- SizeT inSize = *srcLen;
+- *srcLen = *destLen = 0;
+- for (;;)
+- {
+- SizeT inSizeCur = inSize, outSizeCur, dicPos;
+- ELzmaFinishMode curFinishMode;
+- SRes res;
+- if (p->dicPos == p->dicBufSize)
+- p->dicPos = 0;
+- dicPos = p->dicPos;
+- if (outSize > p->dicBufSize - dicPos)
+- {
+- outSizeCur = p->dicBufSize;
+- curFinishMode = LZMA_FINISH_ANY;
+- }
+- else
+- {
+- outSizeCur = dicPos + outSize;
+- curFinishMode = finishMode;
+- }
+-
+- res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status);
+- src += inSizeCur;
+- inSize -= inSizeCur;
+- *srcLen += inSizeCur;
+- outSizeCur = p->dicPos - dicPos;
+- memcpy(dest, p->dic + dicPos, outSizeCur);
+- dest += outSizeCur;
+- outSize -= outSizeCur;
+- *destLen += outSizeCur;
+- if (res != 0)
+- return res;
+- if (outSizeCur == 0 || outSize == 0)
+- return SZ_OK;
+- }
+-}
+-
+-void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
++static void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
+ {
+ alloc->Free(alloc, p->probs);
+ p->probs = 0;
+ }
+
+-static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc)
+-{
+- alloc->Free(alloc, p->dic);
+- p->dic = 0;
+-}
+-
+-void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc)
+-{
+- LzmaDec_FreeProbs(p, alloc);
+- LzmaDec_FreeDict(p, alloc);
+-}
+-
+-SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
++static SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
+ {
+ UInt32 dicSize;
+ Byte d;
+@@ -935,7 +883,7 @@ static SRes LzmaDec_AllocateProbs2(CLzma
+ return SZ_OK;
+ }
+
+-SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
++static SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
+ {
+ CLzmaProps propNew;
+ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
+@@ -943,28 +891,6 @@ SRes LzmaDec_AllocateProbs(CLzmaDec *p,
+ p->prop = propNew;
+ return SZ_OK;
+ }
+-
+-SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
+-{
+- CLzmaProps propNew;
+- SizeT dicBufSize;
+- RINOK(LzmaProps_Decode(&propNew, props, propsSize));
+- RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
+- dicBufSize = propNew.dicSize;
+- if (p->dic == 0 || dicBufSize != p->dicBufSize)
+- {
+- LzmaDec_FreeDict(p, alloc);
+- p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize);
+- if (p->dic == 0)
+- {
+- LzmaDec_FreeProbs(p, alloc);
+- return SZ_ERROR_MEM;
+- }
+- }
+- p->dicBufSize = dicBufSize;
+- p->prop = propNew;
+- return SZ_OK;
+-}
+
+ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
+ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
+--- a/lib/lzma/LzmaEnc.c
++++ b/lib/lzma/LzmaEnc.c
+@@ -53,7 +53,7 @@ void LzmaEncProps_Init(CLzmaEncProps *p)
+ p->writeEndMark = 0;
+ }
+
+-void LzmaEncProps_Normalize(CLzmaEncProps *p)
++static void LzmaEncProps_Normalize(CLzmaEncProps *p)
+ {
+ int level = p->level;
+ if (level < 0) level = 5;
+@@ -76,7 +76,7 @@ void LzmaEncProps_Normalize(CLzmaEncProp
+ #endif
+ }
+
+-UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
++static UInt32 __maybe_unused LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
+ {
+ CLzmaEncProps props = *props2;
+ LzmaEncProps_Normalize(&props);
+@@ -93,7 +93,7 @@ UInt32 LzmaEncProps_GetDictSize(const CL
+
+ #define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); }
+
+-UInt32 GetPosSlot1(UInt32 pos)
++static UInt32 GetPosSlot1(UInt32 pos)
+ {
+ UInt32 res;
+ BSR2_RET(pos, res);
+@@ -107,7 +107,7 @@ UInt32 GetPosSlot1(UInt32 pos)
+ #define kNumLogBits (9 + (int)sizeof(size_t) / 2)
+ #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7)
+
+-void LzmaEnc_FastPosInit(Byte *g_FastPos)
++static void LzmaEnc_FastPosInit(Byte *g_FastPos)
+ {
+ int c = 2, slotFast;
+ g_FastPos[0] = 0;
+@@ -339,58 +339,6 @@ typedef struct
+ CSaveState saveState;
+ } CLzmaEnc;
+
+-void LzmaEnc_SaveState(CLzmaEncHandle pp)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)pp;
+- CSaveState *dest = &p->saveState;
+- int i;
+- dest->lenEnc = p->lenEnc;
+- dest->repLenEnc = p->repLenEnc;
+- dest->state = p->state;
+-
+- for (i = 0; i < kNumStates; i++)
+- {
+- memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
+- memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
+- }
+- for (i = 0; i < kNumLenToPosStates; i++)
+- memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
+- memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
+- memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
+- memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
+- memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
+- memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
+- memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
+- memcpy(dest->reps, p->reps, sizeof(p->reps));
+- memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb));
+-}
+-
+-void LzmaEnc_RestoreState(CLzmaEncHandle pp)
+-{
+- CLzmaEnc *dest = (CLzmaEnc *)pp;
+- const CSaveState *p = &dest->saveState;
+- int i;
+- dest->lenEnc = p->lenEnc;
+- dest->repLenEnc = p->repLenEnc;
+- dest->state = p->state;
+-
+- for (i = 0; i < kNumStates; i++)
+- {
+- memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
+- memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
+- }
+- for (i = 0; i < kNumLenToPosStates; i++)
+- memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
+- memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
+- memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
+- memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
+- memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
+- memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
+- memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
+- memcpy(dest->reps, p->reps, sizeof(p->reps));
+- memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb));
+-}
+-
+ SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2)
+ {
+ CLzmaEnc *p = (CLzmaEnc *)pp;
+@@ -600,7 +548,7 @@ static void LitEnc_EncodeMatched(CRangeE
+ while (symbol < 0x10000);
+ }
+
+-void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
++static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
+ {
+ UInt32 i;
+ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits))
+@@ -1676,7 +1624,7 @@ static void FillDistancesPrices(CLzmaEnc
+ p->matchPriceCount = 0;
+ }
+
+-void LzmaEnc_Construct(CLzmaEnc *p)
++static void LzmaEnc_Construct(CLzmaEnc *p)
+ {
+ RangeEnc_Construct(&p->rc);
+ MatchFinder_Construct(&p->matchFinderBase);
+@@ -1709,7 +1657,7 @@ CLzmaEncHandle LzmaEnc_Create(ISzAlloc *
+ return p;
+ }
+
+-void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
++static void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
+ {
+ alloc->Free(alloc, p->litProbs);
+ alloc->Free(alloc, p->saveState.litProbs);
+@@ -1717,7 +1665,7 @@ void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAl
+ p->saveState.litProbs = 0;
+ }
+
+-void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
++static void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
+ {
+ #ifndef _7ZIP_ST
+ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig);
+@@ -1947,7 +1895,7 @@ static SRes LzmaEnc_Alloc(CLzmaEnc *p, U
+ return SZ_OK;
+ }
+
+-void LzmaEnc_Init(CLzmaEnc *p)
++static void LzmaEnc_Init(CLzmaEnc *p)
+ {
+ UInt32 i;
+ p->state = 0;
+@@ -2005,7 +1953,7 @@ void LzmaEnc_Init(CLzmaEnc *p)
+ p->lpMask = (1 << p->lp) - 1;
+ }
+
+-void LzmaEnc_InitPrices(CLzmaEnc *p)
++static void LzmaEnc_InitPrices(CLzmaEnc *p)
+ {
+ if (!p->fastMode)
+ {
+@@ -2037,26 +1985,6 @@ static SRes LzmaEnc_AllocAndInit(CLzmaEn
+ return SZ_OK;
+ }
+
+-static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream,
+- ISzAlloc *alloc, ISzAlloc *allocBig)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)pp;
+- p->matchFinderBase.stream = inStream;
+- p->needInit = 1;
+- p->rc.outStream = outStream;
+- return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig);
+-}
+-
+-SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp,
+- ISeqInStream *inStream, UInt32 keepWindowSize,
+- ISzAlloc *alloc, ISzAlloc *allocBig)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)pp;
+- p->matchFinderBase.stream = inStream;
+- p->needInit = 1;
+- return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
+-}
+-
+ static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen)
+ {
+ p->matchFinderBase.directInput = 1;
+@@ -2064,7 +1992,7 @@ static void LzmaEnc_SetInputBuf(CLzmaEnc
+ p->matchFinderBase.directInputRem = srcLen;
+ }
+
+-SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
++static SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
+ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
+ {
+ CLzmaEnc *p = (CLzmaEnc *)pp;
+@@ -2074,7 +2002,7 @@ SRes LzmaEnc_MemPrepare(CLzmaEncHandle p
+ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
+ }
+
+-void LzmaEnc_Finish(CLzmaEncHandle pp)
++static void LzmaEnc_Finish(CLzmaEncHandle pp)
+ {
+ #ifndef _7ZIP_ST
+ CLzmaEnc *p = (CLzmaEnc *)pp;
+@@ -2107,53 +2035,6 @@ static size_t MyWrite(void *pp, const vo
+ return size;
+ }
+
+-
+-UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp)
+-{
+- const CLzmaEnc *p = (CLzmaEnc *)pp;
+- return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
+-}
+-
+-const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp)
+-{
+- const CLzmaEnc *p = (CLzmaEnc *)pp;
+- return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
+-}
+-
+-SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit,
+- Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)pp;
+- UInt64 nowPos64;
+- SRes res;
+- CSeqOutStreamBuf outStream;
+-
+- outStream.funcTable.Write = MyWrite;
+- outStream.data = dest;
+- outStream.rem = *destLen;
+- outStream.overflow = False;
+-
+- p->writeEndMark = False;
+- p->finished = False;
+- p->result = SZ_OK;
+-
+- if (reInit)
+- LzmaEnc_Init(p);
+- LzmaEnc_InitPrices(p);
+- nowPos64 = p->nowPos64;
+- RangeEnc_Init(&p->rc);
+- p->rc.outStream = &outStream.funcTable;
+-
+- res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize);
+-
+- *unpackSize = (UInt32)(p->nowPos64 - nowPos64);
+- *destLen -= outStream.rem;
+- if (outStream.overflow)
+- return SZ_ERROR_OUTPUT_EOF;
+-
+- return res;
+-}
+-
+ static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress)
+ {
+ SRes res = SZ_OK;
+@@ -2184,13 +2065,6 @@ static SRes LzmaEnc_Encode2(CLzmaEnc *p,
+ return res;
+ }
+
+-SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress,
+- ISzAlloc *alloc, ISzAlloc *allocBig)
+-{
+- RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig));
+- return LzmaEnc_Encode2((CLzmaEnc *)pp, progress);
+-}
+-
+ SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size)
+ {
+ CLzmaEnc *p = (CLzmaEnc *)pp;
+@@ -2247,25 +2121,3 @@ SRes LzmaEnc_MemEncode(CLzmaEncHandle pp
+ return SZ_ERROR_OUTPUT_EOF;
+ return res;
+ }
+-
+-SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+- const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
+- ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
+-{
+- CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc);
+- SRes res;
+- if (p == 0)
+- return SZ_ERROR_MEM;
+-
+- res = LzmaEnc_SetProps(p, props);
+- if (res == SZ_OK)
+- {
+- res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize);
+- if (res == SZ_OK)
+- res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen,
+- writeEndMark, progress, alloc, allocBig);
+- }
+-
+- LzmaEnc_Destroy(p, alloc, allocBig);
+- return res;
+-}
diff --git a/pkgs/patches-linux-5.15/532-jffs2_eofdetect.patch b/pkgs/patches-linux-5.15/532-jffs2_eofdetect.patch
new file mode 100644
index 0000000..744fbd0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/600-netfilter_conntrack_flush.patch b/pkgs/patches-linux-5.15/600-netfilter_conntrack_flush.patch
new file mode 100644
index 0000000..a88e3d7
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/610-netfilter_match_bypass_default_checks.patch b/pkgs/patches-linux-5.15/610-netfilter_match_bypass_default_checks.patch
new file mode 100644
index 0000000..4577031
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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) {
+@@ -1223,12 +1260,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/pkgs/patches-linux-5.15/611-netfilter_match_bypass_default_table.patch b/pkgs/patches-linux-5.15/611-netfilter_match_bypass_default_table.patch
new file mode 100644
index 0000000..baf738a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/612-netfilter_match_reduce_memory_access.patch b/pkgs/patches-linux-5.15/612-netfilter_match_reduce_memory_access.patch
new file mode 100644
index 0000000..79da677
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/613-netfilter_optional_tcp_window_check.patch b/pkgs/patches-linux-5.15/613-netfilter_optional_tcp_window_check.patch
new file mode 100644
index 0000000..fd9e63a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/613-netfilter_optional_tcp_window_check.patch
@@ -0,0 +1,83 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: netfilter: optional tcp window check
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com>
+---
+ net/netfilter/nf_conntrack_proto_tcp.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/net/netfilter/nf_conntrack_proto_tcp.c
++++ b/net/netfilter/nf_conntrack_proto_tcp.c
+@@ -465,6 +465,9 @@ static bool tcp_in_window(struct nf_conn
+ s32 receiver_offset;
+ bool res, in_recv_win;
+
++ if (tn->tcp_no_window_check)
++ return true;
++
+ /*
+ * Get the required data from the packet.
+ */
+@@ -1160,7 +1163,7 @@ int nf_conntrack_tcp_packet(struct nf_co
+ IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED &&
+ timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK])
+ timeout = timeouts[TCP_CONNTRACK_UNACK];
+- else if (ct->proto.tcp.last_win == 0 &&
++ else if (!tn->tcp_no_window_check && ct->proto.tcp.last_win == 0 &&
+ timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS])
+ timeout = timeouts[TCP_CONNTRACK_RETRANS];
+ else
+@@ -1476,6 +1479,9 @@ void nf_conntrack_tcp_init_net(struct ne
+ */
+ tn->tcp_be_liberal = 0;
+
++ /* Skip Windows Check */
++ tn->tcp_no_window_check = 0;
++
+ /* If it's non-zero, we turn off RST sequence number check */
+ tn->tcp_ignore_invalid_rst = 0;
+
+--- a/net/netfilter/nf_conntrack_standalone.c
++++ b/net/netfilter/nf_conntrack_standalone.c
+@@ -633,6 +633,7 @@ enum nf_ct_sysctl_index {
+ #endif
+ NF_SYSCTL_CT_PROTO_TCP_LOOSE,
+ NF_SYSCTL_CT_PROTO_TCP_LIBERAL,
++ NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK,
+ NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST,
+ NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS,
+ NF_SYSCTL_CT_PROTO_TIMEOUT_UDP,
+@@ -849,6 +850,14 @@ static struct ctl_table nf_ct_sysctl_tab
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_ONE,
+ },
++ [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = {
++ .procname = "nf_conntrack_tcp_no_window_check",
++ .maxlen = sizeof(u8),
++ .mode = 0644,
++ .proc_handler = proc_dou8vec_minmax,
++ .extra1 = SYSCTL_ZERO,
++ .extra2 = SYSCTL_ONE,
++ },
+ [NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = {
+ .procname = "nf_conntrack_tcp_ignore_invalid_rst",
+ .maxlen = sizeof(u8),
+@@ -1065,6 +1074,7 @@ static void nf_conntrack_standalone_init
+
+ XASSIGN(LOOSE, &tn->tcp_loose);
+ XASSIGN(LIBERAL, &tn->tcp_be_liberal);
++ XASSIGN(NO_WINDOW_CHECK, &tn->tcp_no_window_check);
+ XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans);
+ XASSIGN(IGNORE_INVALID_RST, &tn->tcp_ignore_invalid_rst);
+ #undef XASSIGN
+--- a/include/net/netns/conntrack.h
++++ b/include/net/netns/conntrack.h
+@@ -26,6 +26,7 @@ struct nf_tcp_net {
+ unsigned int timeouts[TCP_CONNTRACK_TIMEOUT_MAX];
+ u8 tcp_loose;
+ u8 tcp_be_liberal;
++ u8 tcp_no_window_check;
+ u8 tcp_max_retrans;
+ u8 tcp_ignore_invalid_rst;
+ #if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
diff --git a/pkgs/patches-linux-5.15/620-net_sched-codel-do-not-defer-queue-length-update.patch b/pkgs/patches-linux-5.15/620-net_sched-codel-do-not-defer-queue-length-update.patch
new file mode 100644
index 0000000..4b4825a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/630-packet_socket_type.patch b/pkgs/patches-linux-5.15/630-packet_socket_type.patch
new file mode 100644
index 0000000..c61935f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -1825,6 +1825,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
+@@ -1832,6 +1833,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
+@@ -1844,7 +1846,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)))
+@@ -2082,12 +2084,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;
+
+@@ -2213,12 +2215,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;
+
+@@ -3330,6 +3332,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;
+@@ -3971,6 +3974,16 @@ packet_setsockopt(struct socket *sock, i
+ 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;
+ }
+@@ -4027,6 +4040,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
+@@ -137,6 +137,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/pkgs/patches-linux-5.15/645-netfilter-connmark-introduce-set-dscpmark.patch b/pkgs/patches-linux-5.15/645-netfilter-connmark-introduce-set-dscpmark.patch
new file mode 100644
index 0000000..2d3fe01
--- /dev/null
+++ b/pkgs/patches-linux-5.15/645-netfilter-connmark-introduce-set-dscpmark.patch
@@ -0,0 +1,212 @@
+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,12 +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_int8_t dscp;
+
+ ct = nf_ct_get(skb, &ctinfo);
+ if (ct == NULL)
+@@ -37,12 +38,24 @@ connmark_tg_shift(struct sk_buff *skb, c
+
+ switch (info->mode) {
+ case XT_CONNMARK_SET:
+- newmark = (ct->mark & ~info->ctmask) ^ info->ctmark;
+- if (info->shift_dir == D_SHIFT_RIGHT)
+- newmark >>= info->shift_bits;
+- else
+- newmark <<= info->shift_bits;
++ newmark = 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 (ct->mark != newmark) {
+ ct->mark = newmark;
+ nf_conntrack_event_cache(IPCT_MARK, ct);
+@@ -81,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);
+ }
+@@ -165,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/pkgs/patches-linux-5.15/650-netfilter-add-xt_FLOWOFFLOAD-target.patch b/pkgs/patches-linux-5.15/650-netfilter-add-xt_FLOWOFFLOAD-target.patch
new file mode 100644
index 0000000..961a930
--- /dev/null
+++ b/pkgs/patches-linux-5.15/650-netfilter-add-xt_FLOWOFFLOAD-target.patch
@@ -0,0 +1,860 @@
+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,697 @@
++/*
++ * 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;
++
++ 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/pkgs/patches-linux-5.15/651-wireless_mesh_header.patch b/pkgs/patches-linux-5.15/651-wireless_mesh_header.patch
new file mode 100644
index 0000000..12a031e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/655-increase_skb_pad.patch b/pkgs/patches-linux-5.15/655-increase_skb_pad.patch
new file mode 100644
index 0000000..9d03fe5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -2737,7 +2737,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/pkgs/patches-linux-5.15/660-fq_codel_defaults.patch b/pkgs/patches-linux-5.15/660-fq_codel_defaults.patch
new file mode 100644
index 0000000..5541c0b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -469,7 +469,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/pkgs/patches-linux-5.15/661-kernel-ct-size-the-hashtable-more-adequately.patch b/pkgs/patches-linux-5.15/661-kernel-ct-size-the-hashtable-more-adequately.patch
new file mode 100644
index 0000000..134e210
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -2727,7 +2727,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/pkgs/patches-linux-5.15/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch b/pkgs/patches-linux-5.15/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch
new file mode 100644
index 0000000..09efa1e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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;
+
+@@ -1525,6 +1686,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;
+@@ -1563,6 +1732,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));
+ }
+
+@@ -1949,6 +2119,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)
+ {
+@@ -1986,6 +2165,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[],
+@@ -2101,6 +2320,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) +
+@@ -2130,6 +2355,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;
+ }
+
+@@ -2137,6 +2380,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) ||
+@@ -2146,9 +2392,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) ||
+@@ -2188,6 +2452,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/pkgs/patches-linux-5.15/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/pkgs/patches-linux-5.15/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch
new file mode 100644
index 0000000..6896076
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -142,6 +142,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
+@@ -2767,6 +2767,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)
+@@ -6300,6 +6331,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
+@@ -6311,6 +6344,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
+ }
+@@ -6502,6 +6536,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);
+@@ -6512,11 +6548,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);
+@@ -6543,6 +6589,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:
+@@ -6562,6 +6610,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);
+@@ -6639,6 +6688,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/pkgs/patches-linux-5.15/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch b/pkgs/patches-linux-5.15/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch
new file mode 100644
index 0000000..bea43b2
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/pkgs/patches-linux-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
new file mode 100644
index 0000000..8b4cd10
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -2075,6 +2075,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
+@@ -855,6 +855,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
+@@ -6061,6 +6061,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;
+
+@@ -8075,6 +8078,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,
+@@ -8126,6 +8171,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);
+@@ -8222,6 +8268,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);
+
+@@ -9041,6 +9088,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/pkgs/patches-linux-5.15/682-of_net-add-mac-address-increment-support.patch b/pkgs/patches-linux-5.15/682-of_net-add-mac-address-increment-support.patch
new file mode 100644
index 0000000..fe6fadd
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/683-of_net-add-mac-address-to-of-tree.patch b/pkgs/patches-linux-5.15/683-of_net-add-mac-address-to-of-tree.patch
new file mode 100644
index 0000000..742d58b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/683-of_net-add-mac-address-to-of-tree.patch
@@ -0,0 +1,59 @@
+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(+)
+
+diff --git a/net/core/of_net.c b/net/core/of_net.c
+index 71c38b532f72..43b28c8ddff9 100644
+--- a/net/core/of_net.c
++++ b/net/core/of_net.c
+@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr)
+ 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 @@ int of_get_mac_address(struct device_node *np, u8 *addr)
+ addr[5] = (mac_val >> 0) & 0xff;
+ }
+
++ of_add_mac_address(np, addr);
+ return ret;
+ }
+ EXPORT_SYMBOL(of_get_mac_address);
+--
+
diff --git a/pkgs/patches-linux-5.15/700-mvneta-tx-queue-workaround.patch b/pkgs/patches-linux-5.15/700-mvneta-tx-queue-workaround.patch
new file mode 100644
index 0000000..6c07a29
--- /dev/null
+++ b/pkgs/patches-linux-5.15/700-mvneta-tx-queue-workaround.patch
@@ -0,0 +1,38 @@
+The hardware queue scheduling is apparently configured with fixed
+priorities, which creates a nasty fairness issue where traffic from one
+CPU can starve traffic from all other CPUs.
+
+Work around this issue by forcing all tx packets to go through one CPU,
+until this issue is fixed properly.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -4987,6 +4987,16 @@ static int mvneta_setup_tc(struct net_de
+ }
+ }
+
++#ifndef CONFIG_ARM64
++static u16 mvneta_select_queue(struct net_device *dev, struct sk_buff *skb,
++ struct net_device *sb_dev)
++{
++ /* XXX: hardware queue scheduling is broken,
++ * use only one queue until it is fixed */
++ return 0;
++}
++#endif
++
+ static const struct net_device_ops mvneta_netdev_ops = {
+ .ndo_open = mvneta_open,
+ .ndo_stop = mvneta_stop,
+@@ -4997,6 +5007,9 @@ static const struct net_device_ops mvnet
+ .ndo_fix_features = mvneta_fix_features,
+ .ndo_get_stats64 = mvneta_get_stats64,
+ .ndo_eth_ioctl = mvneta_ioctl,
++#ifndef CONFIG_ARM64
++ .ndo_select_queue = mvneta_select_queue,
++#endif
+ .ndo_bpf = mvneta_xdp,
+ .ndo_xdp_xmit = mvneta_xdp_xmit,
+ .ndo_setup_tc = mvneta_setup_tc,
diff --git a/pkgs/patches-linux-5.15/700-net-next-net-dsa-introduce-tagger-owned-storage-for-private.patch b/pkgs/patches-linux-5.15/700-net-next-net-dsa-introduce-tagger-owned-storage-for-private.patch
new file mode 100644
index 0000000..fe47c17
--- /dev/null
+++ b/pkgs/patches-linux-5.15/700-net-next-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_tree *dst = ds->dst;
+ const struct dsa_device_ops *tag_ops;
+ enum dsa_tag_protocol default_proto;
++ int err;
+
+ /* Find out which protocol the switch would prefer. */
+ default_proto = dsa_get_tag_protocol(dp, master);
+@@ -1304,6 +1363,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/pkgs/patches-linux-5.15/701-net-dsa-make-tagging-protocols-connect-to-individual-switches.patch b/pkgs/patches-linux-5.15/701-net-dsa-make-tagging-protocols-connect-to-individual-switches.patch
new file mode 100644
index 0000000..f682260
--- /dev/null
+++ b/pkgs/patches-linux-5.15/701-net-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_tree *dst = ds->dst;
+ const struct dsa_device_ops *tag_ops;
+ enum dsa_tag_protocol default_proto;
+- int err;
+
+ /* Find out which protocol the switch would prefer. */
+ default_proto = dsa_get_tag_protocol(dp, master);
+@@ -1363,12 +1361,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/pkgs/patches-linux-5.15/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch b/pkgs/patches-linux-5.15/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch
new file mode 100644
index 0000000..96f5042
--- /dev/null
+++ b/pkgs/patches-linux-5.15/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch
@@ -0,0 +1,66 @@
+From 75fa71e3acadbb4ab5eda18505277eb9a1f69b23 Mon Sep 17 00:00:00 2001
+From: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Date: Fri, 26 Nov 2021 12:20:53 +0100
+Subject: net: mvneta: Use struct tc_mqprio_qopt_offload for MQPrio
+ configuration
+
+The struct tc_mqprio_qopt_offload is a container for struct tc_mqprio_qopt,
+that allows passing extra parameters, such as traffic shaping. This commit
+converts the current mqprio code to that new struct.
+
+Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 17 ++++++++++-------
+ 1 file changed, 10 insertions(+), 7 deletions(-)
+
+(limited to 'drivers/net/ethernet/marvell/mvneta.c')
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -38,6 +38,7 @@
+ #include <net/ipv6.h>
+ #include <net/tso.h>
+ #include <net/page_pool.h>
++#include <net/pkt_cls.h>
+ #include <linux/bpf_trace.h>
+
+ /* Registers */
+@@ -4947,14 +4948,14 @@ static void mvneta_setup_rx_prio_map(str
+ }
+
+ static int mvneta_setup_mqprio(struct net_device *dev,
+- struct tc_mqprio_qopt *qopt)
++ struct tc_mqprio_qopt_offload *mqprio)
+ {
+ struct mvneta_port *pp = netdev_priv(dev);
+ u8 num_tc;
+ int i;
+
+- qopt->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
+- num_tc = qopt->num_tc;
++ mqprio->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
++ num_tc = mqprio->qopt.num_tc;
+
+ if (num_tc > rxq_number)
+ return -EINVAL;
+@@ -4965,13 +4966,15 @@ static int mvneta_setup_mqprio(struct ne
+ return 0;
+ }
+
+- memcpy(pp->prio_tc_map, qopt->prio_tc_map, sizeof(pp->prio_tc_map));
++ memcpy(pp->prio_tc_map, mqprio->qopt.prio_tc_map,
++ sizeof(pp->prio_tc_map));
+
+ mvneta_setup_rx_prio_map(pp);
+
+- netdev_set_num_tc(dev, qopt->num_tc);
+- for (i = 0; i < qopt->num_tc; i++)
+- netdev_set_tc_queue(dev, i, qopt->count[i], qopt->offset[i]);
++ netdev_set_num_tc(dev, mqprio->qopt.num_tc);
++ for (i = 0; i < mqprio->qopt.num_tc; i++)
++ netdev_set_tc_queue(dev, i, mqprio->qopt.count[i],
++ mqprio->qopt.offset[i]);
+
+ return 0;
+ }
diff --git a/pkgs/patches-linux-5.15/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch b/pkgs/patches-linux-5.15/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch
new file mode 100644
index 0000000..997f301
--- /dev/null
+++ b/pkgs/patches-linux-5.15/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch
@@ -0,0 +1,30 @@
+From e7ca75fe6662f78bfeb0112671c812e4c7b8e214 Mon Sep 17 00:00:00 2001
+From: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Date: Fri, 26 Nov 2021 12:20:54 +0100
+Subject: net: mvneta: Don't force-set the offloading flag
+
+The qopt->hw flag is set by the TC code according to the offloading mode
+asked by user. Don't force-set it in the driver, but instead read it to
+make sure we do what's asked.
+
+Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+(limited to 'drivers/net/ethernet/marvell/mvneta.c')
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -4954,7 +4954,9 @@ static int mvneta_setup_mqprio(struct ne
+ u8 num_tc;
+ int i;
+
+- mqprio->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
++ if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS)
++ return 0;
++
+ num_tc = mqprio->qopt.num_tc;
+
+ if (num_tc > rxq_number)
diff --git a/pkgs/patches-linux-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch b/pkgs/patches-linux-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch
new file mode 100644
index 0000000..21c322b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -1738,6 +1738,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
+@@ -789,6 +789,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/pkgs/patches-linux-5.15/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch b/pkgs/patches-linux-5.15/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch
new file mode 100644
index 0000000..73563a5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch
@@ -0,0 +1,97 @@
+From e9f7099d0730341b24c057acbf545dd019581db6 Mon Sep 17 00:00:00 2001
+From: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Date: Fri, 26 Nov 2021 12:20:55 +0100
+Subject: net: mvneta: Allow having more than one queue per TC
+
+The current mqprio implementation assumed that we are only using one
+queue per TC. Use the offset and count parameters to allow using
+multiple queues per TC. In that case, the controller will use a standard
+round-robin algorithm to pick queues assigned to the same TC, with the
+same priority.
+
+This only applies to VLAN priorities in ingress traffic, each TC
+corresponding to a vlan priority.
+
+Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 35 ++++++++++++++++++++---------------
+ 1 file changed, 20 insertions(+), 15 deletions(-)
+
+(limited to 'drivers/net/ethernet/marvell/mvneta.c')
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -493,7 +493,6 @@ struct mvneta_port {
+ u8 mcast_count[256];
+ u16 tx_ring_size;
+ u16 rx_ring_size;
+- u8 prio_tc_map[8];
+
+ phy_interface_t phy_interface;
+ struct device_node *dn;
+@@ -4936,13 +4935,12 @@ static void mvneta_clear_rx_prio_map(str
+ mvreg_write(pp, MVNETA_VLAN_PRIO_TO_RXQ, 0);
+ }
+
+-static void mvneta_setup_rx_prio_map(struct mvneta_port *pp)
++static void mvneta_map_vlan_prio_to_rxq(struct mvneta_port *pp, u8 pri, u8 rxq)
+ {
+- u32 val = 0;
+- int i;
++ u32 val = mvreg_read(pp, MVNETA_VLAN_PRIO_TO_RXQ);
+
+- for (i = 0; i < rxq_number; i++)
+- val |= MVNETA_VLAN_PRIO_RXQ_MAP(i, pp->prio_tc_map[i]);
++ val &= ~MVNETA_VLAN_PRIO_RXQ_MAP(pri, 0x7);
++ val |= MVNETA_VLAN_PRIO_RXQ_MAP(pri, rxq);
+
+ mvreg_write(pp, MVNETA_VLAN_PRIO_TO_RXQ, val);
+ }
+@@ -4951,8 +4949,8 @@ static int mvneta_setup_mqprio(struct ne
+ struct tc_mqprio_qopt_offload *mqprio)
+ {
+ struct mvneta_port *pp = netdev_priv(dev);
++ int rxq, tc;
+ u8 num_tc;
+- int i;
+
+ if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS)
+ return 0;
+@@ -4962,21 +4960,28 @@ static int mvneta_setup_mqprio(struct ne
+ if (num_tc > rxq_number)
+ return -EINVAL;
+
++ mvneta_clear_rx_prio_map(pp);
++
+ if (!num_tc) {
+- mvneta_clear_rx_prio_map(pp);
+ netdev_reset_tc(dev);
+ return 0;
+ }
+
+- memcpy(pp->prio_tc_map, mqprio->qopt.prio_tc_map,
+- sizeof(pp->prio_tc_map));
++ netdev_set_num_tc(dev, mqprio->qopt.num_tc);
+
+- mvneta_setup_rx_prio_map(pp);
++ for (tc = 0; tc < mqprio->qopt.num_tc; tc++) {
++ netdev_set_tc_queue(dev, tc, mqprio->qopt.count[tc],
++ mqprio->qopt.offset[tc]);
++
++ for (rxq = mqprio->qopt.offset[tc];
++ rxq < mqprio->qopt.count[tc] + mqprio->qopt.offset[tc];
++ rxq++) {
++ if (rxq >= rxq_number)
++ return -EINVAL;
+
+- netdev_set_num_tc(dev, mqprio->qopt.num_tc);
+- for (i = 0; i < mqprio->qopt.num_tc; i++)
+- netdev_set_tc_queue(dev, i, mqprio->qopt.count[i],
+- mqprio->qopt.offset[i]);
++ mvneta_map_vlan_prio_to_rxq(pp, tc, rxq);
++ }
++ }
+
+ return 0;
+ }
diff --git a/pkgs/patches-linux-5.15/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch b/pkgs/patches-linux-5.15/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch
new file mode 100644
index 0000000..e27ac35
--- /dev/null
+++ b/pkgs/patches-linux-5.15/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch
@@ -0,0 +1,29 @@
+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
+@@ -25,6 +25,14 @@ static struct sk_buff *mtk_tag_xmit(stru
+ u8 xmit_tpid;
+ u8 *mtk_tag;
+
++ /* 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).
++ */
++ if (__skb_put_padto(skb, ETH_ZLEN + MTK_HDR_LEN, false))
++ return NULL;
++
+ /* 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/pkgs/patches-linux-5.15/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch b/pkgs/patches-linux-5.15/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch
new file mode 100644
index 0000000..7b837d0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch
@@ -0,0 +1,182 @@
+From 2551dc9e398c37a15e52122d385c29a8b06be45f Mon Sep 17 00:00:00 2001
+From: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Date: Fri, 26 Nov 2021 12:20:56 +0100
+Subject: net: mvneta: Add TC traffic shaping offload
+
+The mvneta controller is able to do some tocken-bucket per-queue traffic
+shaping. This commit adds support for setting these using the TC mqprio
+interface.
+
+The token-bucket parameters are customisable, but the current
+implementation configures them to have a 10kbps resolution for the
+rate limitation, since it allows to cover the whole range of max_rate
+values from 10kbps to 5Gbps with 10kbps increments.
+
+Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 120 +++++++++++++++++++++++++++++++++-
+ 1 file changed, 119 insertions(+), 1 deletion(-)
+
+(limited to 'drivers/net/ethernet/marvell/mvneta.c')
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -248,12 +248,39 @@
+ #define MVNETA_TXQ_SENT_DESC_MASK 0x3fff0000
+ #define MVNETA_PORT_TX_RESET 0x3cf0
+ #define MVNETA_PORT_TX_DMA_RESET BIT(0)
++#define MVNETA_TXQ_CMD1_REG 0x3e00
++#define MVNETA_TXQ_CMD1_BW_LIM_SEL_V1 BIT(3)
++#define MVNETA_TXQ_CMD1_BW_LIM_EN BIT(0)
++#define MVNETA_REFILL_NUM_CLK_REG 0x3e08
++#define MVNETA_REFILL_MAX_NUM_CLK 0x0000ffff
+ #define MVNETA_TX_MTU 0x3e0c
+ #define MVNETA_TX_TOKEN_SIZE 0x3e14
+ #define MVNETA_TX_TOKEN_SIZE_MAX 0xffffffff
++#define MVNETA_TXQ_BUCKET_REFILL_REG(q) (0x3e20 + ((q) << 2))
++#define MVNETA_TXQ_BUCKET_REFILL_PERIOD_MASK 0x3ff00000
++#define MVNETA_TXQ_BUCKET_REFILL_PERIOD_SHIFT 20
++#define MVNETA_TXQ_BUCKET_REFILL_VALUE_MAX 0x0007ffff
+ #define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2))
+ #define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff
+
++/* The values of the bucket refill base period and refill period are taken from
++ * the reference manual, and adds up to a base resolution of 10Kbps. This allows
++ * to cover all rate-limit values from 10Kbps up to 5Gbps
++ */
++
++/* Base period for the rate limit algorithm */
++#define MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS 100
++
++/* Number of Base Period to wait between each bucket refill */
++#define MVNETA_TXQ_BUCKET_REFILL_PERIOD 1000
++
++/* The base resolution for rate limiting, in bps. Any max_rate value should be
++ * a multiple of that value.
++ */
++#define MVNETA_TXQ_RATE_LIMIT_RESOLUTION (NSEC_PER_SEC / \
++ (MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS * \
++ MVNETA_TXQ_BUCKET_REFILL_PERIOD))
++
+ #define MVNETA_LPI_CTRL_0 0x2cc0
+ #define MVNETA_LPI_CTRL_1 0x2cc4
+ #define MVNETA_LPI_REQUEST_ENABLE BIT(0)
+@@ -4945,11 +4972,74 @@ static void mvneta_map_vlan_prio_to_rxq(
+ mvreg_write(pp, MVNETA_VLAN_PRIO_TO_RXQ, val);
+ }
+
++static int mvneta_enable_per_queue_rate_limit(struct mvneta_port *pp)
++{
++ unsigned long core_clk_rate;
++ u32 refill_cycles;
++ u32 val;
++
++ core_clk_rate = clk_get_rate(pp->clk);
++ if (!core_clk_rate)
++ return -EINVAL;
++
++ refill_cycles = MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS /
++ (NSEC_PER_SEC / core_clk_rate);
++
++ if (refill_cycles > MVNETA_REFILL_MAX_NUM_CLK)
++ return -EINVAL;
++
++ /* Enable bw limit algorithm version 3 */
++ val = mvreg_read(pp, MVNETA_TXQ_CMD1_REG);
++ val &= ~(MVNETA_TXQ_CMD1_BW_LIM_SEL_V1 | MVNETA_TXQ_CMD1_BW_LIM_EN);
++ mvreg_write(pp, MVNETA_TXQ_CMD1_REG, val);
++
++ /* Set the base refill rate */
++ mvreg_write(pp, MVNETA_REFILL_NUM_CLK_REG, refill_cycles);
++
++ return 0;
++}
++
++static void mvneta_disable_per_queue_rate_limit(struct mvneta_port *pp)
++{
++ u32 val = mvreg_read(pp, MVNETA_TXQ_CMD1_REG);
++
++ val |= (MVNETA_TXQ_CMD1_BW_LIM_SEL_V1 | MVNETA_TXQ_CMD1_BW_LIM_EN);
++ mvreg_write(pp, MVNETA_TXQ_CMD1_REG, val);
++}
++
++static int mvneta_setup_queue_rates(struct mvneta_port *pp, int queue,
++ u64 min_rate, u64 max_rate)
++{
++ u32 refill_val, rem;
++ u32 val = 0;
++
++ /* Convert to from Bps to bps */
++ max_rate *= 8;
++
++ if (min_rate)
++ return -EINVAL;
++
++ refill_val = div_u64_rem(max_rate, MVNETA_TXQ_RATE_LIMIT_RESOLUTION,
++ &rem);
++
++ if (rem || !refill_val ||
++ refill_val > MVNETA_TXQ_BUCKET_REFILL_VALUE_MAX)
++ return -EINVAL;
++
++ val = refill_val;
++ val |= (MVNETA_TXQ_BUCKET_REFILL_PERIOD <<
++ MVNETA_TXQ_BUCKET_REFILL_PERIOD_SHIFT);
++
++ mvreg_write(pp, MVNETA_TXQ_BUCKET_REFILL_REG(queue), val);
++
++ return 0;
++}
++
+ static int mvneta_setup_mqprio(struct net_device *dev,
+ struct tc_mqprio_qopt_offload *mqprio)
+ {
+ struct mvneta_port *pp = netdev_priv(dev);
+- int rxq, tc;
++ int rxq, txq, tc, ret;
+ u8 num_tc;
+
+ if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS)
+@@ -4963,6 +5053,7 @@ static int mvneta_setup_mqprio(struct ne
+ mvneta_clear_rx_prio_map(pp);
+
+ if (!num_tc) {
++ mvneta_disable_per_queue_rate_limit(pp);
+ netdev_reset_tc(dev);
+ return 0;
+ }
+@@ -4983,6 +5074,33 @@ static int mvneta_setup_mqprio(struct ne
+ }
+ }
+
++ if (mqprio->shaper != TC_MQPRIO_SHAPER_BW_RATE) {
++ mvneta_disable_per_queue_rate_limit(pp);
++ return 0;
++ }
++
++ if (mqprio->qopt.num_tc > txq_number)
++ return -EINVAL;
++
++ ret = mvneta_enable_per_queue_rate_limit(pp);
++ if (ret)
++ return ret;
++
++ for (tc = 0; tc < mqprio->qopt.num_tc; tc++) {
++ for (txq = mqprio->qopt.offset[tc];
++ txq < mqprio->qopt.count[tc] + mqprio->qopt.offset[tc];
++ txq++) {
++ if (txq >= txq_number)
++ return -EINVAL;
++
++ ret = mvneta_setup_queue_rates(pp, txq,
++ mqprio->min_rate[tc],
++ mqprio->max_rate[tc]);
++ if (ret)
++ return ret;
++ }
++ }
++
+ return 0;
+ }
+
diff --git a/pkgs/patches-linux-5.15/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch b/pkgs/patches-linux-5.15/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch
new file mode 100644
index 0000000..2a2ca7f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/pkgs/patches-linux-5.15/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch
new file mode 100644
index 0000000..ffa44f0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -2980,6 +2980,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/pkgs/patches-linux-5.15/720-net-phy-add-aqr-phys.patch b/pkgs/patches-linux-5.15/720-net-phy-add-aqr-phys.patch
new file mode 100644
index 0000000..65d8f47
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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)
+@@ -372,6 +374,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;
+@@ -502,7 +547,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);
+ }
+
+@@ -674,6 +719,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,
+@@ -699,6 +762,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);
+@@ -709,8 +790,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/pkgs/patches-linux-5.15/721-net-add-packet-mangeling.patch b/pkgs/patches-linux-5.15/721-net-add-packet-mangeling.patch
new file mode 100644
index 0000000..206e16c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -1669,6 +1669,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
+@@ -1701,6 +1705,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 {
+@@ -2002,6 +2007,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;
+@@ -2062,6 +2068,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;
+@@ -2136,6 +2147,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
+@@ -2852,6 +2852,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
+@@ -3002,16 +3006,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
+@@ -3588,6 +3588,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/pkgs/patches-linux-5.15/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch b/pkgs/patches-linux-5.15/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch
new file mode 100644
index 0000000..f190185
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -126,6 +128,29 @@
+ #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
+ #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
+
++/* 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;
+@@ -257,6 +282,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;
+@@ -780,6 +850,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);
+@@ -790,9 +884,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/pkgs/patches-linux-5.15/723-net-phy-aquantia-fix-system-side-protocol-mi.patch b/pkgs/patches-linux-5.15/723-net-phy-aquantia-fix-system-side-protocol-mi.patch
new file mode 100644
index 0000000..d99b495
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -315,10 +315,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/pkgs/patches-linux-5.15/724-net-phy-aquantia-Add-AQR113-driver-support.patch b/pkgs/patches-linux-5.15/724-net-phy-aquantia-Add-AQR113-driver-support.patch
new file mode 100644
index 0000000..634288c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -869,6 +870,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,
+@@ -891,6 +900,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/pkgs/patches-linux-5.15/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch b/pkgs/patches-linux-5.15/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch
new file mode 100644
index 0000000..f7ccc45
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -870,6 +872,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,
+@@ -900,6 +926,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/pkgs/patches-linux-5.15/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch b/pkgs/patches-linux-5.15/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch
new file mode 100644
index 0000000..6788a2e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch b/pkgs/patches-linux-5.15/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch
new file mode 100644
index 0000000..f134828
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch b/pkgs/patches-linux-5.15/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch
new file mode 100644
index 0000000..8f000ba
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch b/pkgs/patches-linux-5.15/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch
new file mode 100644
index 0000000..eb84b45
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -2302,7 +2302,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,
+@@ -2313,7 +2313,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,
+@@ -2324,7 +2324,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,
+@@ -2338,7 +2338,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,
+@@ -2352,7 +2352,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,
+@@ -2366,7 +2366,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,
+@@ -2380,7 +2380,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,
+@@ -2394,7 +2394,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,
+@@ -2436,7 +2436,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,
+@@ -2478,7 +2478,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,
+@@ -2492,7 +2492,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,
+@@ -2634,7 +2634,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/pkgs/patches-linux-5.15/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch b/pkgs/patches-linux-5.15/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch
new file mode 100644
index 0000000..23805a9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch b/pkgs/patches-linux-5.15/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch
new file mode 100644
index 0000000..941fa23
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch b/pkgs/patches-linux-5.15/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch
new file mode 100644
index 0000000..746a1e3
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -2300,7 +2300,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,
+ },
+ {
+@@ -2311,7 +2310,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,
+ },
+ {
+@@ -2322,7 +2320,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,
+@@ -2336,7 +2333,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,
+@@ -2350,7 +2346,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,
+@@ -2364,7 +2359,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,
+@@ -2379,7 +2373,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,
+@@ -2392,7 +2385,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,
+@@ -2406,7 +2398,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,
+@@ -2420,7 +2411,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,
+@@ -2434,7 +2424,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,
+@@ -2448,7 +2437,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,
+@@ -2462,7 +2450,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,
+@@ -2476,7 +2463,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,
+@@ -2490,7 +2476,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,
+@@ -2504,7 +2489,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,
+@@ -2518,7 +2502,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,
+@@ -2547,7 +2530,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,
+@@ -2561,7 +2543,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,
+@@ -2587,7 +2568,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;
+@@ -2619,13 +2599,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/pkgs/patches-linux-5.15/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch b/pkgs/patches-linux-5.15/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch
new file mode 100644
index 0000000..99d91df
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch b/pkgs/patches-linux-5.15/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch
new file mode 100644
index 0000000..cd83fac
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch b/pkgs/patches-linux-5.15/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch
new file mode 100644
index 0000000..586d895
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch b/pkgs/patches-linux-5.15/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch
new file mode 100644
index 0000000..09797ae
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch b/pkgs/patches-linux-5.15/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch
new file mode 100644
index 0000000..c504c37
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch b/pkgs/patches-linux-5.15/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch
new file mode 100644
index 0000000..9f88059
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch b/pkgs/patches-linux-5.15/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch
new file mode 100644
index 0000000..89e9b3f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch b/pkgs/patches-linux-5.15/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch
new file mode 100644
index 0000000..c8d424d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch b/pkgs/patches-linux-5.15/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch
new file mode 100644
index 0000000..bd768ec
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch b/pkgs/patches-linux-5.15/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch
new file mode 100644
index 0000000..e464452
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch b/pkgs/patches-linux-5.15/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch
new file mode 100644
index 0000000..606ac0a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch b/pkgs/patches-linux-5.15/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch
new file mode 100644
index 0000000..320db8f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch b/pkgs/patches-linux-5.15/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch
new file mode 100644
index 0000000..de20176
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch b/pkgs/patches-linux-5.15/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch
new file mode 100644
index 0000000..8abd264
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch b/pkgs/patches-linux-5.15/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch
new file mode 100644
index 0000000..2b5a84a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch b/pkgs/patches-linux-5.15/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch
new file mode 100644
index 0000000..38dc954
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch b/pkgs/patches-linux-5.15/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch
new file mode 100644
index 0000000..aa5d92a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch b/pkgs/patches-linux-5.15/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch
new file mode 100644
index 0000000..1bfb00c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch b/pkgs/patches-linux-5.15/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch
new file mode 100644
index 0000000..70f227f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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;
+ }
+@@ -2105,7 +2112,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,
+ };
+
+@@ -2114,7 +2126,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/pkgs/patches-linux-5.15/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch b/pkgs/patches-linux-5.15/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch
new file mode 100644
index 0000000..27f94dc
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch b/pkgs/patches-linux-5.15/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch
new file mode 100644
index 0000000..b991798
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch b/pkgs/patches-linux-5.15/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch
new file mode 100644
index 0000000..f7cb514
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch b/pkgs/patches-linux-5.15/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch
new file mode 100644
index 0000000..b9bce97
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch b/pkgs/patches-linux-5.15/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch
new file mode 100644
index 0000000..a510cfd
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch b/pkgs/patches-linux-5.15/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch
new file mode 100644
index 0000000..71fa302
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch b/pkgs/patches-linux-5.15/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch
new file mode 100644
index 0000000..4a61703
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch b/pkgs/patches-linux-5.15/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch
new file mode 100644
index 0000000..df9518d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch b/pkgs/patches-linux-5.15/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch
new file mode 100644
index 0000000..7348d93
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch b/pkgs/patches-linux-5.15/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch
new file mode 100644
index 0000000..f477b1b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/753-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/pkgs/patches-linux-5.15/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch b/pkgs/patches-linux-5.15/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch
new file mode 100644
index 0000000..408a59d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/754-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);
+ }
+
+@@ -1916,11 +1911,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);
+ }
+
+@@ -1944,10 +1939,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;
+
+@@ -2041,7 +2035,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;
+@@ -2050,7 +2044,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/pkgs/patches-linux-5.15/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch b/pkgs/patches-linux-5.15/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch
new file mode 100644
index 0000000..8c39b8e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/755-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/pkgs/patches-linux-5.15/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch b/pkgs/patches-linux-5.15/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch
new file mode 100644
index 0000000..f873b70
--- /dev/null
+++ b/pkgs/patches-linux-5.15/756-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;
+@@ -2077,6 +2071,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/pkgs/patches-linux-5.15/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch b/pkgs/patches-linux-5.15/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch
new file mode 100644
index 0000000..4ca9c8b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/757-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/pkgs/patches-linux-5.15/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch b/pkgs/patches-linux-5.15/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch
new file mode 100644
index 0000000..1465d1f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/758-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
+@@ -2154,14 +2168,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/pkgs/patches-linux-5.15/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch b/pkgs/patches-linux-5.15/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch
new file mode 100644
index 0000000..973446e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/759-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)
+@@ -1998,6 +2008,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/pkgs/patches-linux-5.15/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch b/pkgs/patches-linux-5.15/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch
new file mode 100644
index 0000000..2953006
--- /dev/null
+++ b/pkgs/patches-linux-5.15/760-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)
+ {
+@@ -1999,6 +2023,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/pkgs/patches-linux-5.15/760-net-usb-r8152-add-LED-configuration-from-OF.patch b/pkgs/patches-linux-5.15/760-net-usb-r8152-add-LED-configuration-from-OF.patch
new file mode 100644
index 0000000..c54332f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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>
+@@ -6861,6 +6862,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;
+@@ -6902,6 +6919,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)
+@@ -7042,6 +7061,8 @@ static void r8153_init(struct r8152 *tp)
+ tp->coalesce = COALESCE_SLOW;
+ break;
+ }
++
++ r8152_led_configuration(tp);
+ }
+
+ static void r8153b_init(struct r8152 *tp)
+@@ -7124,6 +7145,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/pkgs/patches-linux-5.15/761-dt-bindings-net-add-RTL8152-binding-documentation.patch b/pkgs/patches-linux-5.15/761-dt-bindings-net-add-RTL8152-binding-documentation.patch
new file mode 100644
index 0000000..be262b9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch b/pkgs/patches-linux-5.15/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch
new file mode 100644
index 0000000..fa022d7
--- /dev/null
+++ b/pkgs/patches-linux-5.15/761-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;
+@@ -1930,6 +2005,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)
+ {
+@@ -2037,6 +2134,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/pkgs/patches-linux-5.15/762-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch b/pkgs/patches-linux-5.15/762-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch
new file mode 100644
index 0000000..69e9b38
--- /dev/null
+++ b/pkgs/patches-linux-5.15/762-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
+@@ -2027,6 +2027,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)
+ {
+@@ -2136,6 +2229,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/pkgs/patches-linux-5.15/763-net-next-net-dsa-qca8k-add-LAG-support.patch b/pkgs/patches-linux-5.15/763-net-next-net-dsa-qca8k-add-LAG-support.patch
new file mode 100644
index 0000000..bfc77db
--- /dev/null
+++ b/pkgs/patches-linux-5.15/763-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;
+ }
+
+@@ -2207,6 +2210,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,
+@@ -2240,6 +2415,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/pkgs/patches-linux-5.15/764-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch b/pkgs/patches-linux-5.15/764-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch
new file mode 100644
index 0000000..8c0a990
--- /dev/null
+++ b/pkgs/patches-linux-5.15/764-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
+@@ -2232,7 +2232,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;
+
+@@ -2246,8 +2246,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/pkgs/patches-linux-5.15/765-1-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch b/pkgs/patches-linux-5.15/765-1-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch
new file mode 100644
index 0000000..1786bf0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/765-1-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
+@@ -1986,13 +1986,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);
+@@ -2004,6 +1997,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/pkgs/patches-linux-5.15/765-2-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch b/pkgs/patches-linux-5.15/765-2-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch
new file mode 100644
index 0000000..c2493a0
--- /dev/null
+++ b/pkgs/patches-linux-5.15/765-2-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
+@@ -1997,14 +1997,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/pkgs/patches-linux-5.15/765-3-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch b/pkgs/patches-linux-5.15/765-3-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch
new file mode 100644
index 0000000..d1126de
--- /dev/null
+++ b/pkgs/patches-linux-5.15/765-3-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
+@@ -330,28 +330,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,
+@@ -361,13 +346,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.
+@@ -405,7 +383,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/pkgs/patches-linux-5.15/765-4-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch b/pkgs/patches-linux-5.15/765-4-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch
new file mode 100644
index 0000000..67d4340
--- /dev/null
+++ b/pkgs/patches-linux-5.15/765-4-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
+@@ -267,9 +267,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/pkgs/patches-linux-5.15/765-5-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch b/pkgs/patches-linux-5.15/765-5-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch
new file mode 100644
index 0000000..e6472c6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/765-5-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/pkgs/patches-linux-5.15/765-6-net-next-net-dsa-setup-master-before-ports.patch b/pkgs/patches-linux-5.15/765-6-net-next-net-dsa-setup-master-before-ports.patch
new file mode 100644
index 0000000..93cad0c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/765-6-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/pkgs/patches-linux-5.15/766-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch b/pkgs/patches-linux-5.15/766-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch
new file mode 100644
index 0000000..d73b745
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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;
+@@ -916,6 +926,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;
+@@ -548,6 +555,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
+@@ -2320,6 +2320,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;
+@@ -2331,6 +2361,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/pkgs/patches-linux-5.15/766-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch b/pkgs/patches-linux-5.15/766-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch
new file mode 100644
index 0000000..6478d58
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/766-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch b/pkgs/patches-linux-5.15/766-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch
new file mode 100644
index 0000000..82c94b3
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/766-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch b/pkgs/patches-linux-5.15/766-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch
new file mode 100644
index 0000000..c1e74ce
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/766-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch b/pkgs/patches-linux-5.15/766-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch
new file mode 100644
index 0000000..9394a0d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/766-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch b/pkgs/patches-linux-5.15/766-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch
new file mode 100644
index 0000000..459454e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/766-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch b/pkgs/patches-linux-5.15/766-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch
new file mode 100644
index 0000000..7e5dc65
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/766-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch b/pkgs/patches-linux-5.15/766-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch
new file mode 100644
index 0000000..ad25da3
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/766-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch b/pkgs/patches-linux-5.15/766-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch
new file mode 100644
index 0000000..ff8fdca
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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
+@@ -2382,6 +2382,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,
+@@ -2417,6 +2431,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/pkgs/patches-linux-5.15/766-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch b/pkgs/patches-linux-5.15/766-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch
new file mode 100644
index 0000000..43656ad
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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);
+@@ -2393,7 +2591,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 = {
+@@ -2432,6 +2653,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)
+@@ -2511,6 +2733,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/pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch b/pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch
new file mode 100644
index 0000000..c4bc2b3
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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++) {
+@@ -2592,9 +2690,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);
+ }
+
+@@ -2608,6 +2708,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:
+@@ -2736,6 +2837,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/pkgs/patches-linux-5.15/766-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch b/pkgs/patches-linux-5.15/766-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch
new file mode 100644
index 0000000..f5899eb
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/766-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch b/pkgs/patches-linux-5.15/766-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch
new file mode 100644
index 0000000..dc81609
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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;
+@@ -3041,6 +3037,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/pkgs/patches-linux-5.15/766-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch b/pkgs/patches-linux-5.15/766-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch
new file mode 100644
index 0000000..2d48373
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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);
+
+@@ -3038,6 +3073,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/pkgs/patches-linux-5.15/766-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch b/pkgs/patches-linux-5.15/766-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch
new file mode 100644
index 0000000..5acd13d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/766-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch b/pkgs/patches-linux-5.15/766-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch
new file mode 100644
index 0000000..f26c6b9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-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/pkgs/patches-linux-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/pkgs/patches-linux-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch
new file mode 100644
index 0000000..662b956
--- /dev/null
+++ b/pkgs/patches-linux-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch
@@ -0,0 +1,27 @@
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Subject: [RFC net-next 7/7] net: dsa: mv88e6xxx: Request assisted learning on CPU port
+Date: Sat, 16 Jan 2021 02:25:15 +0100
+Archived-At: <https://lore.kernel.org/netdev/20210116012515.3152-8-tobias@waldekranz.com/>
+
+While the hardware is capable of performing learning on the CPU port,
+it requires alot of additions to the bridge's forwarding path in order
+to handle multi-destination traffic correctly.
+
+Until that is in place, opt for the next best thing and let DSA sync
+the relevant addresses down to the hardware FDB.
+
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+---
+ drivers/net/dsa/mv88e6xxx/chip.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -6320,6 +6320,7 @@ static int mv88e6xxx_register_switch(str
+ ds->ops = &mv88e6xxx_switch_ops;
+ ds->ageing_time_min = chip->info->age_time_coeff;
+ ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
++ ds->assisted_learning_on_cpu_port = true;
+
+ /* 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/pkgs/patches-linux-5.15/773-bgmac-add-srab-switch.patch b/pkgs/patches-linux-5.15/773-bgmac-add-srab-switch.patch
new file mode 100644
index 0000000..1e4fc44
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+ **************************************************/
+@@ -1542,6 +1554,14 @@ int bgmac_enet_probe(struct bgmac *bgmac
+ /* Omit FCS from max MTU size */
+ net_dev->max_mtu = BGMAC_RX_MAX_FRAME_SIZE - ETH_FCS_LEN;
+
++ 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");
+@@ -1564,6 +1584,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 {
+@@ -495,6 +496,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/pkgs/patches-linux-5.15/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch b/pkgs/patches-linux-5.15/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch
new file mode 100644
index 0000000..44093ea
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch b/pkgs/patches-linux-5.15/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch
new file mode 100644
index 0000000..cdddbcf
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch b/pkgs/patches-linux-5.15/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch
new file mode 100644
index 0000000..78570c5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch b/pkgs/patches-linux-5.15/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch
new file mode 100644
index 0000000..e61349a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch b/pkgs/patches-linux-5.15/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch
new file mode 100644
index 0000000..2b19752
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch b/pkgs/patches-linux-5.15/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch
new file mode 100644
index 0000000..b56032c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch b/pkgs/patches-linux-5.15/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch
new file mode 100644
index 0000000..8cd1df9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch b/pkgs/patches-linux-5.15/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch
new file mode 100644
index 0000000..8306eb5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch b/pkgs/patches-linux-5.15/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch
new file mode 100644
index 0000000..a74108e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch b/pkgs/patches-linux-5.15/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch
new file mode 100644
index 0000000..e787ce9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/775-v5.16-1-net-phy-add-phy_interface_t-bitmap-support.patch b/pkgs/patches-linux-5.15/775-v5.16-1-net-phy-add-phy_interface_t-bitmap-support.patch
new file mode 100644
index 0000000..69f6a5d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/775-v5.16-1-net-phy-add-phy_interface_t-bitmap-support.patch
@@ -0,0 +1,66 @@
+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 1/3] 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(+)
+
+diff --git a/include/linux/phy.h b/include/linux/phy.h
+index 04e90423fa88..96e43fbb2dd8 100644
+--- 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
+ */
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/775-v5.16-2-net-phylink-add-MAC-phy_interface_t-bitmap.patch b/pkgs/patches-linux-5.15/775-v5.16-2-net-phylink-add-MAC-phy_interface_t-bitmap.patch
new file mode 100644
index 0000000..2141a3c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/775-v5.16-2-net-phylink-add-MAC-phy_interface_t-bitmap.patch
@@ -0,0 +1,29 @@
+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 2/3] 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(+)
+
+diff --git a/include/linux/phylink.h b/include/linux/phylink.h
+index f7b5ed06a815..bc4b866cd99b 100644
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -76,6 +76,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);
+ };
+
+ /**
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/775-v5.16-3-net-phylink-use-supported_interfaces-for-phylink-val.patch b/pkgs/patches-linux-5.15/775-v5.16-3-net-phylink-use-supported_interfaces-for-phylink-val.patch
new file mode 100644
index 0000000..fad5044
--- /dev/null
+++ b/pkgs/patches-linux-5.15/775-v5.16-3-net-phylink-use-supported_interfaces-for-phylink-val.patch
@@ -0,0 +1,105 @@
+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 3/3] 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(-)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 14c7d73790b4..6da245dacca4 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -155,9 +155,45 @@ static const char *phylink_an_mode_str(unsigned int mode)
+ 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;
+diff --git a/include/linux/phylink.h b/include/linux/phylink.h
+index bc4b866cd99b..f037470b6fb3 100644
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -67,6 +67,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;
+@@ -134,8 +136,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.
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/776-v5.16-1-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch b/pkgs/patches-linux-5.15/776-v5.16-1-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch
new file mode 100644
index 0000000..23d6f89
--- /dev/null
+++ b/pkgs/patches-linux-5.15/776-v5.16-1-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch
@@ -0,0 +1,1009 @@
+From 1cd141887a4d4576e6cbfebf8b1445b2be768900 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 1/4] 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(-)
+
+diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
+index a967de4fcc90..b32f25b42323 100644
+--- a/drivers/net/dsa/b53/b53_common.c
++++ b/drivers/net/dsa/b53/b53_common.c
+@@ -1343,10 +1343,8 @@ void b53_phylink_validate(struct dsa_switch *ds, int port,
+ 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);
+ }
+diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
+index d76b2377d66e..564f797a32aa 100644
+--- 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 dsa_switch *ds, int port,
+ 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 dsa_switch *ds, int port,
+ 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,
+diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
+index 950a54ec4b59..b2bab460d2e9 100644
+--- a/drivers/net/dsa/hirschmann/hellcreek.c
++++ b/drivers/net/dsa/hirschmann/hellcreek.c
+@@ -1476,10 +1476,8 @@ static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
+ 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
+diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c
+index 2240a3d35122..4599e958fa05 100644
+--- a/drivers/net/dsa/lantiq_gswip.c
++++ b/drivers/net/dsa/lantiq_gswip.c
+@@ -1452,10 +1452,8 @@ static void gswip_phylink_set_capab(unsigned long *supported,
+ 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_validate(struct dsa_switch *ds, int port,
+ 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_validate(struct dsa_switch *ds, int port,
+ 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_validate(struct dsa_switch *ds, int port,
+ 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_validate(struct dsa_switch *ds, int port,
+ 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);
+ }
+diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c
+index c5142f86a3c7..43fc3087aeb3 100644
+--- a/drivers/net/dsa/microchip/ksz8795.c
++++ b/drivers/net/dsa/microchip/ksz8795.c
+@@ -1542,15 +1542,13 @@ static void ksz8_validate(struct dsa_switch *ds, int port,
+ 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);
+ }
+diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
+index cad98ab421d7..d2751f73246b 100644
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -683,9 +683,8 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
+ 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.
+diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
+index 5ba7e5c820dd..aa3b7655a89f 100644
+--- 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(struct ocelot *ocelot, int port,
+
+ 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(struct ocelot *ocelot, int port,
+ 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,
+diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c
+index 05e4e75c0107..40d6d1f2c724 100644
+--- 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(struct ocelot *ocelot, int port,
+
+ 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(struct ocelot *ocelot, int port,
+ 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,
+diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c
+index 5d476f452396..c39de2a4c1fe 100644
+--- a/drivers/net/dsa/qca/ar9331.c
++++ b/drivers/net/dsa/qca/ar9331.c
+@@ -522,7 +522,7 @@ static void ar9331_sw_phylink_validate(struct dsa_switch *ds, int port,
+ 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(struct dsa_switch *ds, int port,
+ 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);
+ }
+diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
+index 1a2a7536ff8a..3e33d3b6fc59 100644
+--- 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(struct dsa_switch *ds, int port,
+ */
+ 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(struct dsa_switch *ds, int port,
+ 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
+diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c
+index cf363d5a3002..e79a336c6eb2 100644
+--- a/drivers/net/dsa/xrs700x/xrs700x.c
++++ b/drivers/net/dsa/xrs700x/xrs700x.c
+@@ -457,7 +457,7 @@ static void xrs700x_phylink_validate(struct dsa_switch *ds, int port,
+ 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(struct dsa_switch *ds, int port,
+ 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,
+diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
+index bafc51c34e0b..94879cf8b420 100644
+--- 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(struct net_device *netdev,
+ __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(struct net_device *netdev,
+ 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);
+diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c
+index 416a5c99db5a..d754bfc4f18f 100644
+--- a/drivers/net/ethernet/atheros/ag71xx.c
++++ b/drivers/net/ethernet/atheros/ag71xx.c
+@@ -1082,14 +1082,12 @@ static void ag71xx_mac_validate(struct phylink_config *config,
+ 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,
+diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
+index 3ca3f9d0fd9b..c6f28b084646 100644
+--- 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_config *config,
+ 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_config *config,
+ 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,
+diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+index d522bd5c90b4..1c318a932460 100644
+--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
++++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+@@ -940,7 +940,7 @@ static void enetc_pl_mac_validate(struct phylink_config *config,
+ 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;
+ }
+
+@@ -963,10 +963,8 @@ static void enetc_pl_mac_validate(struct phylink_config *config,
+ 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,
+diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c
+index b431c300ef1b..a85667078b72 100644
+--- 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(struct net_device *netdev,
+ }
+ }
+
+- 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;
+ }
+diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+index beda8e0ef7d4..8362822316a9 100644
+--- 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(struct net_device *netdev,
+ * 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 */
+diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
+index 7eb13fbf58e3..f2e959997e48 100644
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3862,14 +3862,14 @@ static void mvneta_validate(struct phylink_config *config,
+ */
+ 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;
+ }
+
+@@ -3898,10 +3898,8 @@ static void mvneta_validate(struct phylink_config *config,
+ 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.
+diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+index 2baa909290b3..c963115558cb 100644
+--- 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(struct phylink_config *config,
+ 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,
+diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
+index dbfa3bc39e34..4c882ceaeb1f 100644
+--- 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(struct net_device *netdev,
+ 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);
+diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c
+index fab53c9b8380..572061164f1f 100644
+--- 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_device *dev)
+ 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)
+diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+index c3cffb32fb06..4be2bc8f74f1 100644
+--- 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_mode(struct ptys2ethtool_config *cfg,
+ 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_modes(unsigned long *link_modes,
+ 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 unsigned long *link_modes,
+ 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;
+diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
+index af70e2795125..fb74752de0ca 100644
+--- 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(struct phylink_config *config,
+ }
+ 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,
+diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
+index c08c56e07b1d..6a8e391ecadd 100644
+--- 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(struct phylink_config *config,
+
+ 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(struct phylink_config *config,
+ 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,
+diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+index 3de1a03839e2..620fc13b5781 100644
+--- 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(struct net_device *netdev,
+ 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);
+diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+index fbbbcfe0e891..4cf0994f624c 100644
+--- 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 phylink_config *config,
+ 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 phylink_config *config,
+ 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,
+diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
+index fd4cbf8a55ad..ec75cf81c3af 100644
+--- a/drivers/net/pcs/pcs-xpcs.c
++++ b/drivers/net/pcs/pcs-xpcs.c
+@@ -646,7 +646,7 @@ void xpcs_validate(struct dw_xpcs *xpcs, unsigned long *supported,
+ 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);
+
+diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
+index 4566348a6d05..0a9099c77694 100644
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -286,7 +286,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
+ if (bus->sfp_quirk && bus->sfp_quirk->modes)
+ 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);
+diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
+index e4983f473a3c..b4aa3bbcf3c7 100644
+--- a/net/ethtool/ioctl.c
++++ b/net/ethtool/ioctl.c
+@@ -335,7 +335,7 @@ EXPORT_SYMBOL(ethtool_intersect_link_masks);
+ 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_u32(u32 *legacy_u32,
+ 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;
+ }
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/776-v5.16-2-net-mvneta-populate-supported_interfaces-member.patch b/pkgs/patches-linux-5.15/776-v5.16-2-net-mvneta-populate-supported_interfaces-member.patch
new file mode 100644
index 0000000..b755bd7
--- /dev/null
+++ b/pkgs/patches-linux-5.15/776-v5.16-2-net-mvneta-populate-supported_interfaces-member.patch
@@ -0,0 +1,53 @@
+From c7c29985c548c24efe90e337c575b475bc5e8c13 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 2/4] 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(+)
+
+diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
+index f2e959997e48..1056b4845ec4 100644
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -5308,6 +5308,31 @@ static int mvneta_probe(struct platform_device *pdev)
+
+ 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);
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/776-v5.16-3-net-mvneta-remove-interface-checks-in-mvneta_validat.patch b/pkgs/patches-linux-5.15/776-v5.16-3-net-mvneta-remove-interface-checks-in-mvneta_validat.patch
new file mode 100644
index 0000000..14c34f2
--- /dev/null
+++ b/pkgs/patches-linux-5.15/776-v5.16-3-net-mvneta-remove-interface-checks-in-mvneta_validat.patch
@@ -0,0 +1,40 @@
+From 10639f59c410911bb4a14e903d109dd426f2858c 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 3/4] 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(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
+index 1056b4845ec4..c13d4beb570f 100644
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3860,15 +3860,8 @@ static void mvneta_validate(struct phylink_config *config,
+ * "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;
+ }
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/776-v5.16-4-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch b/pkgs/patches-linux-5.15/776-v5.16-4-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch
new file mode 100644
index 0000000..0b8fbbd
--- /dev/null
+++ b/pkgs/patches-linux-5.15/776-v5.16-4-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch
@@ -0,0 +1,60 @@
+From 0e09bb9ae49bcb121c353861f440e7a9ede3b378 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 4/4] 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(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
+index c13d4beb570f..51fe2de0215e 100644
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3851,8 +3851,6 @@ static void mvneta_validate(struct phylink_config *config,
+ 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.
+@@ -3874,11 +3872,12 @@ static void mvneta_validate(struct phylink_config *config,
+ 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);
+ }
+@@ -3893,11 +3892,6 @@ static void mvneta_validate(struct phylink_config *config,
+
+ 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,
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/777-v5.16-1-net-dpaa2-mac-add-support-for-more-ethtool-10G-link-.patch b/pkgs/patches-linux-5.15/777-v5.16-1-net-dpaa2-mac-add-support-for-more-ethtool-10G-link-.patch
new file mode 100644
index 0000000..a54e4c5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/777-v5.16-1-net-dpaa2-mac-add-support-for-more-ethtool-10G-link-.patch
@@ -0,0 +1,45 @@
+From bc4b596770b44bbea030175bb2c48be4b08df543 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Fri, 17 Sep 2021 14:41:17 +0100
+Subject: [PATCH 1/3] net: dpaa2-mac: add support for more ethtool 10G link
+ modes
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Phylink documentation says:
+ Note that the PHY may be able to transform from one connection
+ technology to another, so, eg, don't clear 1000BaseX just
+ because the MAC is unable to BaseX mode. This is more about
+ clearing unsupported speeds and duplex settings. The port modes
+ should not be cleared; phylink_set_port_modes() will help with this.
+
+So add the missing 10G modes.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Acked-by: Marek Behún <kabel@kernel.org>
+Acked-by: Ioana Ciornei <ioana.ciornei@nxp.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
+index ae6d382d8735..543c1f202420 100644
+--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
+@@ -140,6 +140,11 @@ static void dpaa2_mac_validate(struct phylink_config *config,
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_USXGMII:
+ phylink_set(mask, 10000baseT_Full);
++ phylink_set(mask, 10000baseCR_Full);
++ phylink_set(mask, 10000baseSR_Full);
++ phylink_set(mask, 10000baseLR_Full);
++ phylink_set(mask, 10000baseLRM_Full);
++ phylink_set(mask, 10000baseER_Full);
+ if (state->interface == PHY_INTERFACE_MODE_10GBASER)
+ break;
+ phylink_set(mask, 5000baseT_Full);
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/777-v5.16-2-net-phylink-add-phylink_set_10g_modes-helper.patch b/pkgs/patches-linux-5.15/777-v5.16-2-net-phylink-add-phylink_set_10g_modes-helper.patch
new file mode 100644
index 0000000..83c5a4c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/777-v5.16-2-net-phylink-add-phylink_set_10g_modes-helper.patch
@@ -0,0 +1,52 @@
+From 429a7a12a7a11fd5df24915934bc4d01d521ae96 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 4 Oct 2021 12:03:28 +0100
+Subject: [PATCH 2/3] net: phylink: add phylink_set_10g_modes() helper
+
+Add a helper for setting 10Gigabit modes, so we have one central
+place that sets all appropriate 10G modes for a driver.
+
+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 | 11 +++++++++++
+ include/linux/phylink.h | 1 +
+ 2 files changed, 12 insertions(+)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 4861924303ee..0720f05ecb02 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -133,6 +133,17 @@ void phylink_set_port_modes(unsigned long *mask)
+ }
+ EXPORT_SYMBOL_GPL(phylink_set_port_modes);
+
++void phylink_set_10g_modes(unsigned long *mask)
++{
++ phylink_set(mask, 10000baseT_Full);
++ phylink_set(mask, 10000baseCR_Full);
++ phylink_set(mask, 10000baseSR_Full);
++ phylink_set(mask, 10000baseLR_Full);
++ phylink_set(mask, 10000baseLRM_Full);
++ phylink_set(mask, 10000baseER_Full);
++}
++EXPORT_SYMBOL_GPL(phylink_set_10g_modes);
++
+ static int phylink_is_empty_linkmode(const unsigned long *linkmode)
+ {
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, };
+diff --git a/include/linux/phylink.h b/include/linux/phylink.h
+index 174343ccd685..f037470b6fb3 100644
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -493,6 +493,7 @@ 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);
++void phylink_set_10g_modes(unsigned long *mask);
+ void phylink_helper_basex_speed(struct phylink_link_state *state);
+
+ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/777-v5.16-3-net-ethernet-use-phylink_set_10g_modes.patch b/pkgs/patches-linux-5.15/777-v5.16-3-net-ethernet-use-phylink_set_10g_modes.patch
new file mode 100644
index 0000000..0095d64
--- /dev/null
+++ b/pkgs/patches-linux-5.15/777-v5.16-3-net-ethernet-use-phylink_set_10g_modes.patch
@@ -0,0 +1,74 @@
+From 8259f96b710a2dd78e85bb46f12372dc0a3e75a0 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 4 Oct 2021 12:03:33 +0100
+Subject: [PATCH 3/3] net: ethernet: use phylink_set_10g_modes()
+
+Update three drivers to use the new phylink_set_10g_modes() helper:
+Cadence macb, Freescale DPAA2 and Marvell PP2.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/cadence/macb_main.c | 7 +------
+ drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 7 +------
+ drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 7 +------
+ 3 files changed, 3 insertions(+), 18 deletions(-)
+
+diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
+index c6f28b084646..97941cdfe1a1 100644
+--- a/drivers/net/ethernet/cadence/macb_main.c
++++ b/drivers/net/ethernet/cadence/macb_main.c
+@@ -548,13 +548,8 @@ static void macb_validate(struct phylink_config *config,
+ if (bp->caps & MACB_CAPS_GIGABIT_MODE_AVAILABLE &&
+ (state->interface == PHY_INTERFACE_MODE_NA ||
+ state->interface == PHY_INTERFACE_MODE_10GBASER)) {
+- phylink_set(mask, 10000baseCR_Full);
+- phylink_set(mask, 10000baseER_Full);
++ phylink_set_10g_modes(mask);
+ phylink_set(mask, 10000baseKR_Full);
+- phylink_set(mask, 10000baseLR_Full);
+- phylink_set(mask, 10000baseLRM_Full);
+- phylink_set(mask, 10000baseSR_Full);
+- phylink_set(mask, 10000baseT_Full);
+ if (state->interface != PHY_INTERFACE_MODE_NA)
+ goto out;
+ }
+diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
+index 543c1f202420..ef8f0a055024 100644
+--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
+@@ -139,12 +139,7 @@ static void dpaa2_mac_validate(struct phylink_config *config,
+ case PHY_INTERFACE_MODE_NA:
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_USXGMII:
+- phylink_set(mask, 10000baseT_Full);
+- phylink_set(mask, 10000baseCR_Full);
+- phylink_set(mask, 10000baseSR_Full);
+- phylink_set(mask, 10000baseLR_Full);
+- phylink_set(mask, 10000baseLRM_Full);
+- phylink_set(mask, 10000baseER_Full);
++ phylink_set_10g_modes(mask);
+ if (state->interface == PHY_INTERFACE_MODE_10GBASER)
+ break;
+ phylink_set(mask, 5000baseT_Full);
+diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+index c963115558cb..3c267a94f1ca 100644
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+@@ -6305,12 +6305,7 @@ static void mvpp2_phylink_validate(struct phylink_config *config,
+ case PHY_INTERFACE_MODE_XAUI:
+ case PHY_INTERFACE_MODE_NA:
+ if (mvpp2_port_supports_xlg(port)) {
+- phylink_set(mask, 10000baseT_Full);
+- phylink_set(mask, 10000baseCR_Full);
+- phylink_set(mask, 10000baseSR_Full);
+- phylink_set(mask, 10000baseLR_Full);
+- phylink_set(mask, 10000baseLRM_Full);
+- phylink_set(mask, 10000baseER_Full);
++ phylink_set_10g_modes(mask);
+ phylink_set(mask, 10000baseKR_Full);
+ }
+ if (state->interface != PHY_INTERFACE_MODE_NA)
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/779-v5.16-1-net-mvpp2-populate-supported_interfaces-member.patch b/pkgs/patches-linux-5.15/779-v5.16-1-net-mvpp2-populate-supported_interfaces-member.patch
new file mode 100644
index 0000000..28c7134
--- /dev/null
+++ b/pkgs/patches-linux-5.15/779-v5.16-1-net-mvpp2-populate-supported_interfaces-member.patch
@@ -0,0 +1,62 @@
+From 7d3f249f553251b6167feb8259b85699239eb64a Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Wed, 27 Oct 2021 10:49:14 +0100
+Subject: [PATCH 1/4] net: mvpp2: populate supported_interfaces member
+
+Populate the phy interface mode bitmap for the Marvell mvpp2 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>
+---
+ .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 34 +++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+index 3c267a94f1ca..d765559f7bd0 100644
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+@@ -6941,6 +6941,40 @@ static int mvpp2_port_probe(struct platform_device *pdev,
+ port->phylink_config.dev = &dev->dev;
+ port->phylink_config.type = PHYLINK_NETDEV;
+
++ if (mvpp2_port_supports_xlg(port)) {
++ __set_bit(PHY_INTERFACE_MODE_10GBASER,
++ port->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_XAUI,
++ port->phylink_config.supported_interfaces);
++ }
++
++ if (mvpp2_port_supports_rgmii(port))
++ phy_interface_set_rgmii(port->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,
++ port->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
++ port->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++ port->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,
++ port->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,
++ port->phylink_config.supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_SGMII,
++ port->phylink_config.supported_interfaces);
++ }
++
+ phylink = phylink_create(&port->phylink_config, port_fwnode,
+ phy_mode, &mvpp2_phylink_ops);
+ if (IS_ERR(phylink)) {
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/779-v5.16-2-net-mvpp2-remove-interface-checks-in-mvpp2_phylink_v.patch b/pkgs/patches-linux-5.15/779-v5.16-2-net-mvpp2-remove-interface-checks-in-mvpp2_phylink_v.patch
new file mode 100644
index 0000000..1ac1b4b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/779-v5.16-2-net-mvpp2-remove-interface-checks-in-mvpp2_phylink_v.patch
@@ -0,0 +1,63 @@
+From 7e6f41d50ea0af9f04c17937c4060ddcafa1bc90 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 27 Oct 2021 10:49:19 +0100
+Subject: [PATCH 2/4] net: mvpp2: remove interface checks in
+ mvpp2_phylink_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>
+---
+ .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 33 ++++---------------
+ 1 file changed, 7 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+index d765559f7bd0..9ee1b7ef6edf 100644
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+@@ -6265,32 +6265,13 @@ static void mvpp2_phylink_validate(struct phylink_config *config,
+ struct mvpp2_port *port = mvpp2_phylink_to_port(config);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+- /* Invalid combinations */
+- switch (state->interface) {
+- case PHY_INTERFACE_MODE_10GBASER:
+- case PHY_INTERFACE_MODE_XAUI:
+- if (!mvpp2_port_supports_xlg(port))
+- goto empty_set;
+- break;
+- case PHY_INTERFACE_MODE_RGMII:
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- if (!mvpp2_port_supports_rgmii(port))
+- goto empty_set;
+- break;
+- case PHY_INTERFACE_MODE_1000BASEX:
+- case PHY_INTERFACE_MODE_2500BASEX:
+- /* When in 802.3z mode, we must have AN enabled:
+- * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
+- * When <PortType> = 1 (1000BASE-X) this field must be set to 1.
+- */
+- if (!phylink_test(state->advertising, Autoneg))
+- goto empty_set;
+- break;
+- default:
+- break;
+- }
++ /* When in 802.3z mode, we must have AN enabled:
++ * 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) &&
++ !phylink_test(state->advertising, Autoneg))
++ goto empty_set;
+
+ phylink_set(mask, Autoneg);
+ phylink_set_port_modes(mask);
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/779-v5.16-3-net-mvpp2-drop-use-of-phylink_helper_basex_speed.patch b/pkgs/patches-linux-5.15/779-v5.16-3-net-mvpp2-drop-use-of-phylink_helper_basex_speed.patch
new file mode 100644
index 0000000..0d3add4
--- /dev/null
+++ b/pkgs/patches-linux-5.15/779-v5.16-3-net-mvpp2-drop-use-of-phylink_helper_basex_speed.patch
@@ -0,0 +1,58 @@
+From 5159f776d9ca2bf038499a551292231740df8122 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 27 Oct 2021 10:49:24 +0100
+Subject: [PATCH 3/4] net: mvpp2: 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>
+---
+ .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 19 +++++++------------
+ 1 file changed, 7 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+index 9ee1b7ef6edf..a354aa8d7b9a 100644
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+@@ -6307,17 +6307,14 @@ static void mvpp2_phylink_validate(struct phylink_config *config,
+ break;
+ fallthrough;
+ case PHY_INTERFACE_MODE_1000BASEX:
++ phylink_set(mask, 1000baseT_Full);
++ phylink_set(mask, 1000baseX_Full);
++ if (state->interface != PHY_INTERFACE_MODE_NA)
++ break;
++ fallthrough;
+ case PHY_INTERFACE_MODE_2500BASEX:
+- if (port->comphy ||
+- state->interface != PHY_INTERFACE_MODE_2500BASEX) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- }
+- if (port->comphy ||
+- state->interface == PHY_INTERFACE_MODE_2500BASEX) {
+- phylink_set(mask, 2500baseT_Full);
+- phylink_set(mask, 2500baseX_Full);
+- }
++ phylink_set(mask, 2500baseT_Full);
++ phylink_set(mask, 2500baseX_Full);
+ break;
+ default:
+ goto empty_set;
+@@ -6325,8 +6322,6 @@ static void mvpp2_phylink_validate(struct phylink_config *config,
+
+ linkmode_and(supported, supported, mask);
+ linkmode_and(state->advertising, state->advertising, mask);
+-
+- phylink_helper_basex_speed(state);
+ return;
+
+ empty_set:
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/779-v5.16-4-net-mvpp2-clean-up-mvpp2_phylink_validate.patch b/pkgs/patches-linux-5.15/779-v5.16-4-net-mvpp2-clean-up-mvpp2_phylink_validate.patch
new file mode 100644
index 0000000..fdd4d20
--- /dev/null
+++ b/pkgs/patches-linux-5.15/779-v5.16-4-net-mvpp2-clean-up-mvpp2_phylink_validate.patch
@@ -0,0 +1,64 @@
+From 4792da1beeaa5c782b660bc793d331501dc4216b Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 27 Oct 2021 10:49:29 +0100
+Subject: [PATCH 4/4] net: mvpp2: clean up mvpp2_phylink_validate()
+
+mvpp2_phylink_validate() no longer needs to check for
+PHY_INTERFACE_MODE_NA as phylink will walk the supported interface
+types to discover the link mode capabilities. Remove these checks.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 17 +++++++----------
+ 1 file changed, 7 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+index a354aa8d7b9a..be0dcba4649b 100644
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+@@ -6284,14 +6284,12 @@ static void mvpp2_phylink_validate(struct phylink_config *config,
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_XAUI:
+- case PHY_INTERFACE_MODE_NA:
+ if (mvpp2_port_supports_xlg(port)) {
+ phylink_set_10g_modes(mask);
+ phylink_set(mask, 10000baseKR_Full);
+ }
+- if (state->interface != PHY_INTERFACE_MODE_NA)
+- break;
+- fallthrough;
++ break;
++
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+@@ -6303,19 +6301,18 @@ static void mvpp2_phylink_validate(struct phylink_config *config,
+ phylink_set(mask, 100baseT_Full);
+ phylink_set(mask, 1000baseT_Full);
+ phylink_set(mask, 1000baseX_Full);
+- if (state->interface != PHY_INTERFACE_MODE_NA)
+- break;
+- fallthrough;
++ break;
++
+ case PHY_INTERFACE_MODE_1000BASEX:
+ phylink_set(mask, 1000baseT_Full);
+ phylink_set(mask, 1000baseX_Full);
+- if (state->interface != PHY_INTERFACE_MODE_NA)
+- break;
+- fallthrough;
++ break;
++
+ case PHY_INTERFACE_MODE_2500BASEX:
+ phylink_set(mask, 2500baseT_Full);
+ phylink_set(mask, 2500baseX_Full);
+ break;
++
+ default:
+ goto empty_set;
+ }
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/pkgs/patches-linux-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch
new file mode 100644
index 0000000..fcf7892
--- /dev/null
+++ b/pkgs/patches-linux-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch
@@ -0,0 +1,61 @@
+From patchwork Thu Aug 5 22:23:30 2021
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
+X-Patchwork-Id: 12422209
+Date: Thu, 5 Aug 2021 23:23:30 +0100
+From: Daniel Golle <daniel@makrotopia.org>
+To: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org,
+ linux-kernel@vger.kernel.org
+Cc: "David S. Miller" <davem@davemloft.net>, Andrew Lunn <andrew@lunn.ch>,
+ Michael Walle <michael@walle.cc>
+Subject: [PATCH] ARM: kirkwood: add missing <linux/if_ether.h> for ETH_ALEN
+Message-ID: <YQxk4jrbm31NM1US@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+X-BeenThere: linux-arm-kernel@lists.infradead.org
+X-Mailman-Version: 2.1.34
+Precedence: list
+List-Id: <linux-arm-kernel.lists.infradead.org>
+List-Archive: <http://lists.infradead.org/pipermail/linux-arm-kernel/>
+Sender: "linux-arm-kernel" <linux-arm-kernel-bounces@lists.infradead.org>
+
+After commit 83216e3988cd1 ("of: net: pass the dst buffer to
+of_get_mac_address()") build fails for kirkwood as ETH_ALEN is not
+defined.
+
+arch/arm/mach-mvebu/kirkwood.c: In function 'kirkwood_dt_eth_fixup':
+arch/arm/mach-mvebu/kirkwood.c:87:13: error: 'ETH_ALEN' undeclared (first use in this function); did you mean 'ESTALE'?
+ u8 tmpmac[ETH_ALEN];
+ ^~~~~~~~
+ ESTALE
+arch/arm/mach-mvebu/kirkwood.c:87:13: note: each undeclared identifier is reported only once for each function it appears in
+arch/arm/mach-mvebu/kirkwood.c:87:6: warning: unused variable 'tmpmac' [-Wunused-variable]
+ u8 tmpmac[ETH_ALEN];
+ ^~~~~~
+make[5]: *** [scripts/Makefile.build:262: arch/arm/mach-mvebu/kirkwood.o] Error 1
+make[5]: *** Waiting for unfinished jobs....
+
+Add missing #include <linux/if_ether.h> to fix this.
+
+Cc: David S. Miller <davem@davemloft.net>
+Cc: Andrew Lunn <andrew@lunn.ch>
+Cc: Michael Walle <michael@walle.cc>
+Reported-by: https://buildbot.openwrt.org/master/images/#/builders/56/builds/220/steps/44/logs/stdio
+Fixes: 83216e3988cd1 ("of: net: pass the dst buffer to of_get_mac_address()")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ arch/arm/mach-mvebu/kirkwood.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/mach-mvebu/kirkwood.c
++++ b/arch/arm/mach-mvebu/kirkwood.c
+@@ -14,6 +14,7 @@
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/mbus.h>
++#include <linux/if_ether.h>
+ #include <linux/of.h>
+ #include <linux/of_address.h>
+ #include <linux/of_net.h>
diff --git a/pkgs/patches-linux-5.15/780-usb-net-MeigLink_modem_support.patch b/pkgs/patches-linux-5.15/780-usb-net-MeigLink_modem_support.patch
new file mode 100644
index 0000000..096f143
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -243,6 +243,8 @@ static void option_instat_callback(struc
+ #define UBLOX_PRODUCT_R6XX 0x90fa
+ /* 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 */
+@@ -1140,6 +1142,11 @@ static const struct usb_device_id option
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95, 0xff, 0, 0) },
+ { 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/pkgs/patches-linux-5.15/780-v5.16-1-net-phylink-add-generic-validate-implementation.patch b/pkgs/patches-linux-5.15/780-v5.16-1-net-phylink-add-generic-validate-implementation.patch
new file mode 100644
index 0000000..22a25f4
--- /dev/null
+++ b/pkgs/patches-linux-5.15/780-v5.16-1-net-phylink-add-generic-validate-implementation.patch
@@ -0,0 +1,348 @@
+From 792d0a8e185e3ef101e2644503a0828246c8a5b3 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 1/3] 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(+)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 0720f05ecb02..3e1d7dea616d 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -167,6 +167,258 @@ static const char *phylink_an_mode_str(unsigned int mode)
+ return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
+ }
+
++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)
+ {
+diff --git a/include/linux/phylink.h b/include/linux/phylink.h
+index f037470b6fb3..3563820a1765 100644
+--- 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)
+@@ -69,6 +92,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;
+@@ -79,6 +103,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;
+ };
+
+ /**
+@@ -442,6 +467,12 @@ void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+ 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);
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/780-v5.16-2-net-mvneta-use-phylink_generic_validate.patch b/pkgs/patches-linux-5.15/780-v5.16-2-net-mvneta-use-phylink_generic_validate.patch
new file mode 100644
index 0000000..af0e06f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/780-v5.16-2-net-mvneta-use-phylink_generic_validate.patch
@@ -0,0 +1,77 @@
+From 50ba86adc61e95d49215c1c089697190a2666e3a 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 2/3] 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(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
+index 51fe2de0215e..191eef5c26a7 100644
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3851,8 +3851,6 @@ static void mvneta_validate(struct phylink_config *config,
+ 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. ...
+@@ -3864,34 +3862,7 @@ static void mvneta_validate(struct phylink_config *config,
+ 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,
+@@ -5295,6 +5266,9 @@ static int mvneta_probe(struct platform_device *pdev)
+
+ 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);
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/780-v5.16-3-net-mvpp2-use-phylink_generic_validate.patch b/pkgs/patches-linux-5.15/780-v5.16-3-net-mvpp2-use-phylink_generic_validate.patch
new file mode 100644
index 0000000..a41299d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/780-v5.16-3-net-mvpp2-use-phylink_generic_validate.patch
@@ -0,0 +1,107 @@
+From 7356c6e175210a721310f75915248c3148e3147d Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Mon, 15 Nov 2021 10:00:37 +0000
+Subject: [PATCH 3/3] net: mvpp2: use phylink_generic_validate()
+
+Convert mvpp2 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>
+---
+ .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 58 +++----------------
+ 1 file changed, 9 insertions(+), 49 deletions(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+index be0dcba4649b..2b8293c0c2f9 100644
+--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+@@ -6262,9 +6262,6 @@ static void mvpp2_phylink_validate(struct phylink_config *config,
+ unsigned long *supported,
+ struct phylink_link_state *state)
+ {
+- struct mvpp2_port *port = mvpp2_phylink_to_port(config);
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+-
+ /* When in 802.3z mode, we must have AN enabled:
+ * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
+ * When <PortType> = 1 (1000BASE-X) this field must be set to 1.
+@@ -6273,52 +6270,7 @@ static void mvpp2_phylink_validate(struct phylink_config *config,
+ !phylink_test(state->advertising, Autoneg))
+ goto empty_set;
+
+- phylink_set(mask, Autoneg);
+- phylink_set_port_modes(mask);
+-
+- if (port->priv->global_tx_fc) {
+- phylink_set(mask, Pause);
+- phylink_set(mask, Asym_Pause);
+- }
+-
+- switch (state->interface) {
+- case PHY_INTERFACE_MODE_10GBASER:
+- case PHY_INTERFACE_MODE_XAUI:
+- if (mvpp2_port_supports_xlg(port)) {
+- phylink_set_10g_modes(mask);
+- phylink_set(mask, 10000baseKR_Full);
+- }
+- break;
+-
+- case PHY_INTERFACE_MODE_RGMII:
+- case PHY_INTERFACE_MODE_RGMII_ID:
+- case PHY_INTERFACE_MODE_RGMII_RXID:
+- case PHY_INTERFACE_MODE_RGMII_TXID:
+- case PHY_INTERFACE_MODE_SGMII:
+- phylink_set(mask, 10baseT_Half);
+- phylink_set(mask, 10baseT_Full);
+- phylink_set(mask, 100baseT_Half);
+- phylink_set(mask, 100baseT_Full);
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- break;
+-
+- case PHY_INTERFACE_MODE_1000BASEX:
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+- break;
+-
+- case PHY_INTERFACE_MODE_2500BASEX:
+- phylink_set(mask, 2500baseT_Full);
+- phylink_set(mask, 2500baseX_Full);
+- break;
+-
+- default:
+- goto empty_set;
+- }
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
++ phylink_generic_validate(config, supported, state);
+ return;
+
+ empty_set:
+@@ -6913,12 +6865,20 @@ static int mvpp2_port_probe(struct platform_device *pdev,
+ if (!mvpp2_use_acpi_compat_mode(port_fwnode)) {
+ port->phylink_config.dev = &dev->dev;
+ port->phylink_config.type = PHYLINK_NETDEV;
++ port->phylink_config.mac_capabilities =
++ MAC_2500FD | MAC_1000FD | MAC_100 | MAC_10;
++
++ if (port->priv->global_tx_fc)
++ port->phylink_config.mac_capabilities |=
++ MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
+
+ if (mvpp2_port_supports_xlg(port)) {
+ __set_bit(PHY_INTERFACE_MODE_10GBASER,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_XAUI,
+ port->phylink_config.supported_interfaces);
++ port->phylink_config.mac_capabilities |=
++ MAC_10000FD;
+ }
+
+ if (mvpp2_port_supports_rgmii(port))
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/781-v5.17-1-net-dsa-populate-supported_interfaces-member.patch b/pkgs/patches-linux-5.15/781-v5.17-1-net-dsa-populate-supported_interfaces-member.patch
new file mode 100644
index 0000000..d4bcbb3
--- /dev/null
+++ b/pkgs/patches-linux-5.15/781-v5.17-1-net-dsa-populate-supported_interfaces-member.patch
@@ -0,0 +1,72 @@
+From e89cf45caa992df5baee7ce07431d9598ac1defc 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 1/6] 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(+)
+
+diff --git a/include/net/dsa.h b/include/net/dsa.h
+index 206ff3c4ce47..77d0d9a8e2ab 100644
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -636,6 +636,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);
+diff --git a/net/dsa/port.c b/net/dsa/port.c
+index a21015d6bd36..d1502403aeb8 100644
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1172,6 +1172,10 @@ static int dsa_port_phylink_register(struct dsa_port *dp)
+ 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)) {
+diff --git a/net/dsa/slave.c b/net/dsa/slave.c
+index 4d581bd22cd5..525140370950 100644
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1837,6 +1837,10 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev)
+ 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)) {
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/781-v5.17-2-net-dsa-consolidate-phylink-creation.patch b/pkgs/patches-linux-5.15/781-v5.17-2-net-dsa-consolidate-phylink-creation.patch
new file mode 100644
index 0000000..26606a5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/781-v5.17-2-net-dsa-consolidate-phylink-creation.patch
@@ -0,0 +1,158 @@
+From b4668037f74e9f105675fd4dc7da468702d3ea18 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 2/6] 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(-)
+
+diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
+index f0963d85b134..4a18e874779b 100644
+--- a/net/dsa/dsa_priv.h
++++ b/net/dsa/dsa_priv.h
+@@ -267,13 +267,13 @@ int dsa_port_mrp_add_ring_role(const struct dsa_port *dp,
+ 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)
+diff --git a/net/dsa/port.c b/net/dsa/port.c
+index d1502403aeb8..4d227c503837 100644
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1076,7 +1076,7 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
+ 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,
+@@ -1085,6 +1085,30 @@ const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
+ .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;
+@@ -1161,27 +1185,15 @@ static int dsa_port_phylink_register(struct dsa_port *dp)
+ {
+ 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) {
+diff --git a/net/dsa/slave.c b/net/dsa/slave.c
+index 525140370950..0520b4a2d898 100644
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -1817,14 +1817,9 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev)
+ 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 net_device *slave_dev)
+ 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);
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/781-v5.17-3-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch b/pkgs/patches-linux-5.15/781-v5.17-3-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch
new file mode 100644
index 0000000..d2f1bd8
--- /dev/null
+++ b/pkgs/patches-linux-5.15/781-v5.17-3-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch
@@ -0,0 +1,58 @@
+From 31509e304cd5cbdeaecf12cb2de78b6ffe2ac022 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 3/6] 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(-)
+
+diff --git a/include/net/dsa.h b/include/net/dsa.h
+index 77d0d9a8e2ab..1b130566ae3a 100644
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -636,8 +636,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);
+diff --git a/net/dsa/port.c b/net/dsa/port.c
+index 4d227c503837..eed1b5397dc7 100644
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1095,9 +1095,8 @@ int dsa_port_phylink_create(struct dsa_port *dp)
+ 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);
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/781-v5.17-4-net-dsa-support-use-of-phylink_generic_validate.patch b/pkgs/patches-linux-5.15/781-v5.17-4-net-dsa-support-use-of-phylink_generic_validate.patch
new file mode 100644
index 0000000..6720e55
--- /dev/null
+++ b/pkgs/patches-linux-5.15/781-v5.17-4-net-dsa-support-use-of-phylink_generic_validate.patch
@@ -0,0 +1,46 @@
+From 3943c373f8848128218a8cceba63511ffcc09a9d Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 30 Nov 2021 13:10:06 +0000
+Subject: [PATCH 4/6] net: dsa: support use of phylink_generic_validate()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Support the use of phylink_generic_validate() when there is no
+phylink_validate method given in the DSA switch operations and
+mac_capabilities have been set in the phylink_config structure by the
+DSA switch driver.
+
+This gives DSA switch drivers the option to use this if they provide
+the supported_interfaces and mac_capabilities, while still giving them
+an option to override the default implementation if necessary.
+
+Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+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/port.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/net/dsa/port.c b/net/dsa/port.c
+index eed1b5397dc7..371d25606546 100644
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -985,8 +985,11 @@ static void dsa_port_phylink_validate(struct phylink_config *config,
+ struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+ struct dsa_switch *ds = dp->ds;
+
+- if (!ds->ops->phylink_validate)
++ if (!ds->ops->phylink_validate) {
++ if (config->mac_capabilities)
++ phylink_generic_validate(config, supported, state);
+ return;
++ }
+
+ ds->ops->phylink_validate(ds, dp->index, supported, state);
+ }
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/781-v5.17-5-net-dsa-hellcreek-convert-to-phylink_generic_validat.patch b/pkgs/patches-linux-5.15/781-v5.17-5-net-dsa-hellcreek-convert-to-phylink_generic_validat.patch
new file mode 100644
index 0000000..9c72c3e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/781-v5.17-5-net-dsa-hellcreek-convert-to-phylink_generic_validat.patch
@@ -0,0 +1,78 @@
+From 7e0b9a29934d228bec9a133f504f283903333029 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 30 Nov 2021 13:10:11 +0000
+Subject: [PATCH 5/6] net: dsa: hellcreek: convert to
+ phylink_generic_validate()
+
+Populate the supported interfaces and MAC capabilities for the
+hellcreek DSA switch and remove the old validate implementation to
+allow DSA to use phylink_generic_validate() for this switch driver.
+
+The switch actually only supports MII and RGMII, but as phylib defaults
+to GMII, we need to include this interface mode to keep existing DT
+working.
+
+Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
+Tested-by: Kurt Kanzenbach <kurt@linutronix.de>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/hirschmann/hellcreek.c | 24 +++++++++++++-----------
+ 1 file changed, 13 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
+index b2bab460d2e9..a6ae2246844c 100644
+--- a/drivers/net/dsa/hirschmann/hellcreek.c
++++ b/drivers/net/dsa/hirschmann/hellcreek.c
+@@ -1457,14 +1457,19 @@ static void hellcreek_teardown(struct dsa_switch *ds)
+ dsa_devlink_resources_unregister(ds);
+ }
+
+-static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
+- unsigned long *supported,
+- struct phylink_link_state *state)
++static void hellcreek_phylink_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
+ {
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+ struct hellcreek *hellcreek = ds->priv;
+
+- dev_dbg(hellcreek->dev, "Phylink validate for port %d\n", port);
++ __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_RGMII, config->supported_interfaces);
++
++ /* Include GMII - the hardware does not support this interface
++ * mode, but it's the default interface mode for phylib, so we
++ * need it for compatibility with existing DT.
++ */
++ __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces);
+
+ /* The MAC settings are a hardware configuration option and cannot be
+ * changed at run time or by strapping. Therefore the attached PHYs
+@@ -1472,12 +1477,9 @@ static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,
+ * by the hardware.
+ */
+ if (hellcreek->pdata->is_100_mbits)
+- phylink_set(mask, 100baseT_Full);
++ config->mac_capabilities = MAC_100FD;
+ else
+- phylink_set(mask, 1000baseT_Full);
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
++ config->mac_capabilities = MAC_1000FD;
+ }
+
+ static int
+@@ -1828,7 +1830,7 @@ static const struct dsa_switch_ops hellcreek_ds_ops = {
+ .get_strings = hellcreek_get_strings,
+ .get_tag_protocol = hellcreek_get_tag_protocol,
+ .get_ts_info = hellcreek_get_ts_info,
+- .phylink_validate = hellcreek_phylink_validate,
++ .phylink_get_caps = hellcreek_phylink_get_caps,
+ .port_bridge_flags = hellcreek_bridge_flags,
+ .port_bridge_join = hellcreek_port_bridge_join,
+ .port_bridge_leave = hellcreek_port_bridge_leave,
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/781-v5.17-6-net-dsa-lantiq-convert-to-phylink_generic_validate.patch b/pkgs/patches-linux-5.15/781-v5.17-6-net-dsa-lantiq-convert-to-phylink_generic_validate.patch
new file mode 100644
index 0000000..9473bdf
--- /dev/null
+++ b/pkgs/patches-linux-5.15/781-v5.17-6-net-dsa-lantiq-convert-to-phylink_generic_validate.patch
@@ -0,0 +1,196 @@
+From 7a9dfdfe2fdd746541377f395bf0c0d54ea56ad5 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 30 Nov 2021 13:10:16 +0000
+Subject: [PATCH 6/6] net: dsa: lantiq: convert to phylink_generic_validate()
+
+Populate the supported interfaces and MAC capabilities for the Lantiq
+DSA switches and remove the old validate implementation to allow DSA to
+use phylink_generic_validate() for this switch driver.
+
+The exclusion of Gigabit linkmodes for MII, Reverse MII and Reduced MII
+links is handled within phylink_generic_validate() in phylink, so there
+is no need to make them conditional on the interface mode in the driver.
+
+Reviewed-by: Hauke Mehrtens <hauke@hauke-m.de>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/lantiq_gswip.c | 120 +++++++++++----------------------
+ 1 file changed, 38 insertions(+), 82 deletions(-)
+
+diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c
+index 4599e958fa05..de9489063824 100644
+--- a/drivers/net/dsa/lantiq_gswip.c
++++ b/drivers/net/dsa/lantiq_gswip.c
+@@ -1426,114 +1426,70 @@ static int gswip_port_fdb_dump(struct dsa_switch *ds, int port,
+ return 0;
+ }
+
+-static void gswip_phylink_set_capab(unsigned long *supported,
+- struct phylink_link_state *state)
+-{
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+-
+- /* Allow all the expected bits */
+- phylink_set(mask, Autoneg);
+- phylink_set_port_modes(mask);
+- phylink_set(mask, Pause);
+- phylink_set(mask, Asym_Pause);
+-
+- /* With the exclusion of MII, Reverse MII and Reduced MII, we
+- * support Gigabit, including Half duplex
+- */
+- if (state->interface != PHY_INTERFACE_MODE_MII &&
+- state->interface != PHY_INTERFACE_MODE_REVMII &&
+- state->interface != PHY_INTERFACE_MODE_RMII) {
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseT_Half);
+- }
+-
+- 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);
+-}
+-
+-static void gswip_xrx200_phylink_validate(struct dsa_switch *ds, int port,
+- unsigned long *supported,
+- struct phylink_link_state *state)
++static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
+ {
+ switch (port) {
+ case 0:
+ case 1:
+- if (!phy_interface_mode_is_rgmii(state->interface) &&
+- state->interface != PHY_INTERFACE_MODE_MII &&
+- state->interface != PHY_INTERFACE_MODE_REVMII &&
+- state->interface != PHY_INTERFACE_MODE_RMII)
+- goto unsupported;
++ phy_interface_set_rgmii(config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_MII,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_REVMII,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_RMII,
++ config->supported_interfaces);
+ break;
++
+ case 2:
+ case 3:
+ case 4:
+- if (state->interface != PHY_INTERFACE_MODE_INTERNAL)
+- goto unsupported;
++ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
++ config->supported_interfaces);
+ break;
++
+ case 5:
+- if (!phy_interface_mode_is_rgmii(state->interface) &&
+- state->interface != PHY_INTERFACE_MODE_INTERNAL)
+- goto unsupported;
++ phy_interface_set_rgmii(config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
++ config->supported_interfaces);
+ break;
+- default:
+- linkmode_zero(supported);
+- dev_err(ds->dev, "Unsupported port: %i\n", port);
+- return;
+ }
+
+- gswip_phylink_set_capab(supported, state);
+-
+- return;
+-
+-unsupported:
+- linkmode_zero(supported);
+- dev_err(ds->dev, "Unsupported interface '%s' for port %d\n",
+- phy_modes(state->interface), port);
++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
++ MAC_10 | MAC_100 | MAC_1000;
+ }
+
+-static void gswip_xrx300_phylink_validate(struct dsa_switch *ds, int port,
+- unsigned long *supported,
+- struct phylink_link_state *state)
++static void gswip_xrx300_phylink_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
+ {
+ switch (port) {
+ case 0:
+- if (!phy_interface_mode_is_rgmii(state->interface) &&
+- state->interface != PHY_INTERFACE_MODE_GMII &&
+- state->interface != PHY_INTERFACE_MODE_RMII)
+- goto unsupported;
++ phy_interface_set_rgmii(config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_RMII,
++ config->supported_interfaces);
+ break;
++
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+- if (state->interface != PHY_INTERFACE_MODE_INTERNAL)
+- goto unsupported;
++ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
++ config->supported_interfaces);
+ break;
++
+ case 5:
+- if (!phy_interface_mode_is_rgmii(state->interface) &&
+- state->interface != PHY_INTERFACE_MODE_INTERNAL &&
+- state->interface != PHY_INTERFACE_MODE_RMII)
+- goto unsupported;
++ phy_interface_set_rgmii(config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
++ config->supported_interfaces);
++ __set_bit(PHY_INTERFACE_MODE_RMII,
++ config->supported_interfaces);
+ break;
+- default:
+- linkmode_zero(supported);
+- dev_err(ds->dev, "Unsupported port: %i\n", port);
+- return;
+ }
+
+- gswip_phylink_set_capab(supported, state);
+-
+- return;
+-
+-unsupported:
+- linkmode_zero(supported);
+- dev_err(ds->dev, "Unsupported interface '%s' for port %d\n",
+- phy_modes(state->interface), port);
++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
++ MAC_10 | MAC_100 | MAC_1000;
+ }
+
+ static void gswip_port_set_link(struct gswip_priv *priv, int port, bool link)
+@@ -1812,7 +1768,7 @@ static const struct dsa_switch_ops gswip_xrx200_switch_ops = {
+ .port_fdb_add = gswip_port_fdb_add,
+ .port_fdb_del = gswip_port_fdb_del,
+ .port_fdb_dump = gswip_port_fdb_dump,
+- .phylink_validate = gswip_xrx200_phylink_validate,
++ .phylink_get_caps = gswip_xrx200_phylink_get_caps,
+ .phylink_mac_config = gswip_phylink_mac_config,
+ .phylink_mac_link_down = gswip_phylink_mac_link_down,
+ .phylink_mac_link_up = gswip_phylink_mac_link_up,
+@@ -1836,7 +1792,7 @@ static const struct dsa_switch_ops gswip_xrx300_switch_ops = {
+ .port_fdb_add = gswip_port_fdb_add,
+ .port_fdb_del = gswip_port_fdb_del,
+ .port_fdb_dump = gswip_port_fdb_dump,
+- .phylink_validate = gswip_xrx300_phylink_validate,
++ .phylink_get_caps = gswip_xrx300_phylink_get_caps,
+ .phylink_mac_config = gswip_phylink_mac_config,
+ .phylink_mac_link_down = gswip_phylink_mac_link_down,
+ .phylink_mac_link_up = gswip_phylink_mac_link_up,
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/782-v5.17-1-net-phylink-tidy-up-disable-bit-clearing.patch b/pkgs/patches-linux-5.15/782-v5.17-1-net-phylink-tidy-up-disable-bit-clearing.patch
new file mode 100644
index 0000000..07fbe0f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/782-v5.17-1-net-phylink-tidy-up-disable-bit-clearing.patch
@@ -0,0 +1,70 @@
+From 219b507f5c0a08507c17ecf4df80eaa1ccb0f3b7 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 30 Nov 2021 14:49:41 +0000
+Subject: [PATCH 1/2] net: phylink: tidy up disable bit clearing
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Tidy up the disable bit clearing where we clear a bit
+and then run the link resolver.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Marek Behún <kabel@kernel.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1ms4Rx-00EKEc-En@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
+index 3e1d7dea616d..e95c6086d811 100644
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1149,6 +1149,12 @@ static void phylink_run_resolve_and_disable(struct phylink *pl, int bit)
+ }
+ }
+
++static void phylink_enable_and_run_resolve(struct phylink *pl, int bit)
++{
++ clear_bit(bit, &pl->phylink_disable_state);
++ phylink_run_resolve(pl);
++}
++
+ static void phylink_fixed_poll(struct timer_list *t)
+ {
+ struct phylink *pl = container_of(t, struct phylink, link_poll);
+@@ -1636,8 +1642,7 @@ void phylink_start(struct phylink *pl)
+ */
+ phylink_mac_initial_config(pl, true);
+
+- clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
+- phylink_run_resolve(pl);
++ phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
+
+ if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
+ int irq = gpiod_to_irq(pl->link_gpio);
+@@ -1777,8 +1782,7 @@ void phylink_resume(struct phylink *pl)
+ phylink_mac_initial_config(pl, true);
+
+ /* Re-enable and re-resolve the link parameters */
+- clear_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state);
+- phylink_run_resolve(pl);
++ phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_MAC_WOL);
+ } else {
+ phylink_start(pl);
+ }
+@@ -2817,8 +2821,7 @@ static void phylink_sfp_link_up(void *upstream)
+
+ ASSERT_RTNL();
+
+- clear_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
+- phylink_run_resolve(pl);
++ phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK);
+ }
+
+ /* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/782-v5.17-2-net-mvneta-program-1ms-autonegotiation-clock-divisor.patch b/pkgs/patches-linux-5.15/782-v5.17-2-net-mvneta-program-1ms-autonegotiation-clock-divisor.patch
new file mode 100644
index 0000000..d9883ca
--- /dev/null
+++ b/pkgs/patches-linux-5.15/782-v5.17-2-net-mvneta-program-1ms-autonegotiation-clock-divisor.patch
@@ -0,0 +1,56 @@
+From 93bb797861804c03e561e72ce419f79933ce8aea Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 30 Nov 2021 14:54:05 +0000
+Subject: [PATCH 2/2] net: mvneta: program 1ms autonegotiation clock divisor
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Program the 1ms autonegotiation clock divisor according to the clocking
+rate of neta - without this, the 1ms clock ticks at about 660us on
+Armada 38x configured for 250MHz. Bring this into correct specification.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Marek Behún <kabel@kernel.org>
+Link: https://lore.kernel.org/r/E1ms4WD-00EKLK-Ld@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/marvell/mvneta.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
+index 191eef5c26a7..380e8791b805 100644
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3949,7 +3949,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode,
+ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+ /* SGMII mode receives the state from the PHY */
+ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE;
+- new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
++ new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
+ MVNETA_GMAC_FORCE_LINK_PASS |
+ MVNETA_GMAC_CONFIG_MII_SPEED |
+@@ -3961,7 +3961,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode,
+ } else {
+ /* 802.3z negotiation - only 1000base-X */
+ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X;
+- new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
++ new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
+ MVNETA_GMAC_FORCE_LINK_PASS |
+ MVNETA_GMAC_CONFIG_MII_SPEED)) |
+@@ -3974,6 +3974,10 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode,
+ new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN;
+ }
+
++ /* Set the 1ms clock divisor */
++ if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE)
++ new_clk |= clk_get_rate(pp->clk) / 1000;
++
+ /* Armada 370 documentation says we can only change the port mode
+ * and in-band enable when the link is down, so force it down
+ * while making these changes. We also do this for GMAC_CTRL2
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/784-v5.19-1-net-dsa-mv88e6xxx-add-mv88e6352_g2_scratch_port_has_.patch b/pkgs/patches-linux-5.15/784-v5.19-1-net-dsa-mv88e6xxx-add-mv88e6352_g2_scratch_port_has_.patch
new file mode 100644
index 0000000..b18c019
--- /dev/null
+++ b/pkgs/patches-linux-5.15/784-v5.19-1-net-dsa-mv88e6xxx-add-mv88e6352_g2_scratch_port_has_.patch
@@ -0,0 +1,77 @@
+From 41a26921e2cb825bb1dea2620dd5d9250eb25258 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 3 Feb 2022 13:30:36 +0000
+Subject: [PATCH 1/6] net: dsa: mv88e6xxx: add
+ mv88e6352_g2_scratch_port_has_serdes()
+
+Read the hardware configuration to determine which port is attached
+to the serdes.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mv88e6xxx/global2.h | 3 +++
+ drivers/net/dsa/mv88e6xxx/global2_scratch.c | 28 +++++++++++++++++++++
+ 2 files changed, 31 insertions(+)
+
+diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h
+index f3e27573a386..807aeaad9830 100644
+--- a/drivers/net/dsa/mv88e6xxx/global2.h
++++ b/drivers/net/dsa/mv88e6xxx/global2.h
+@@ -299,6 +299,8 @@
+ #define MV88E6352_G2_SCRATCH_CONFIG_DATA1_NO_CPU BIT(2)
+ #define MV88E6352_G2_SCRATCH_CONFIG_DATA2 0x72
+ #define MV88E6352_G2_SCRATCH_CONFIG_DATA2_P0_MODE_MASK 0x3
++#define MV88E6352_G2_SCRATCH_CONFIG_DATA3 0x73
++#define MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL BIT(1)
+
+ #define MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO 0
+ #define MV88E6352_G2_SCRATCH_GPIO_PCTL_TRIG 1
+@@ -370,6 +372,7 @@ extern const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops;
+
+ int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
+ bool external);
++int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port);
+ int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin);
+ int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats);
+
+diff --git a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
+index eda710062933..a9d6e40321a2 100644
+--- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c
++++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
+@@ -289,3 +289,31 @@ int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
+
+ return mv88e6xxx_g2_scratch_write(chip, misc_cfg, val);
+ }
++
++/**
++ * mv88e6352_g2_scratch_port_has_serdes - indicate if a port can have a serdes
++ * @chip: chip private data
++ * @port: port number to check for serdes
++ *
++ * Indicates whether the port may have a serdes attached according to the
++ * pin strapping. Returns negative error number, 0 if the port is not
++ * configured to have a serdes, and 1 if the port is configured to have a
++ * serdes attached.
++ */
++int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
++{
++ u8 config3, p;
++ int err;
++
++ err = mv88e6xxx_g2_scratch_read(chip, MV88E6352_G2_SCRATCH_CONFIG_DATA3,
++ &config3);
++ if (err)
++ return err;
++
++ if (config3 & MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL)
++ p = 5;
++ else
++ p = 4;
++
++ return port == p;
++}
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/784-v5.19-2-net-dsa-mv88e6xxx-populate-supported_interfaces-and-.patch b/pkgs/patches-linux-5.15/784-v5.19-2-net-dsa-mv88e6xxx-populate-supported_interfaces-and-.patch
new file mode 100644
index 0000000..7793bea
--- /dev/null
+++ b/pkgs/patches-linux-5.15/784-v5.19-2-net-dsa-mv88e6xxx-populate-supported_interfaces-and-.patch
@@ -0,0 +1,548 @@
+From 25845d4c0a9932a9b9308139f2e5f9de580e2243 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 3 Feb 2022 13:30:42 +0000
+Subject: [PATCH 2/6] net: dsa: mv88e6xxx: 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 the
+Marvell MV88E6xxx DSA switches in preparation to using these for the
+validation functionality.
+
+Patch co-authored by Marek.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Marek Behún <kabel@kernel.org> [ fixed 6341 and 6393x ]
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mv88e6xxx/chip.c | 275 ++++++++++++++++++++++++++++++-
+ drivers/net/dsa/mv88e6xxx/chip.h | 2 +
+ drivers/net/dsa/mv88e6xxx/port.h | 5 +
+ 3 files changed, 279 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
+index d2751f73246b..c86a08274ca3 100644
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -685,11 +685,251 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
+
+ linkmode_and(supported, supported, mask);
+ linkmode_and(state->advertising, state->advertising, mask);
++}
++
++static const u8 mv88e6185_phy_interface_modes[] = {
++ [MV88E6185_PORT_STS_CMODE_GMII_FD] = PHY_INTERFACE_MODE_GMII,
++ [MV88E6185_PORT_STS_CMODE_MII_100_FD_PS] = PHY_INTERFACE_MODE_MII,
++ [MV88E6185_PORT_STS_CMODE_MII_100] = PHY_INTERFACE_MODE_MII,
++ [MV88E6185_PORT_STS_CMODE_MII_10] = PHY_INTERFACE_MODE_MII,
++ [MV88E6185_PORT_STS_CMODE_SERDES] = PHY_INTERFACE_MODE_1000BASEX,
++ [MV88E6185_PORT_STS_CMODE_1000BASE_X] = PHY_INTERFACE_MODE_1000BASEX,
++ [MV88E6185_PORT_STS_CMODE_PHY] = PHY_INTERFACE_MODE_SGMII,
++};
++
++static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
++ struct phylink_config *config)
++{
++ u8 cmode = chip->ports[port].cmode;
++
++ if (cmode <= ARRAY_SIZE(mv88e6185_phy_interface_modes) &&
++ mv88e6185_phy_interface_modes[cmode])
++ __set_bit(mv88e6185_phy_interface_modes[cmode],
++ config->supported_interfaces);
+
+- /* We can only operate at 2500BaseX or 1000BaseX. If requested
+- * to advertise both, only report advertising at 2500BaseX.
++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 |
++ MAC_1000FD;
++}
++
++static const u8 mv88e6xxx_phy_interface_modes[] = {
++ [MV88E6XXX_PORT_STS_CMODE_MII_PHY] = PHY_INTERFACE_MODE_MII,
++ [MV88E6XXX_PORT_STS_CMODE_MII] = PHY_INTERFACE_MODE_MII,
++ [MV88E6XXX_PORT_STS_CMODE_GMII] = PHY_INTERFACE_MODE_GMII,
++ [MV88E6XXX_PORT_STS_CMODE_RMII_PHY] = PHY_INTERFACE_MODE_RMII,
++ [MV88E6XXX_PORT_STS_CMODE_RMII] = PHY_INTERFACE_MODE_RMII,
++ [MV88E6XXX_PORT_STS_CMODE_100BASEX] = PHY_INTERFACE_MODE_100BASEX,
++ [MV88E6XXX_PORT_STS_CMODE_1000BASEX] = PHY_INTERFACE_MODE_1000BASEX,
++ [MV88E6XXX_PORT_STS_CMODE_SGMII] = PHY_INTERFACE_MODE_SGMII,
++ /* higher interface modes are not needed here, since ports supporting
++ * them are writable, and so the supported interfaces are filled in the
++ * corresponding .phylink_set_interfaces() implementation below
+ */
+- phylink_helper_basex_speed(state);
++};
++
++static void mv88e6xxx_translate_cmode(u8 cmode, unsigned long *supported)
++{
++ if (cmode < ARRAY_SIZE(mv88e6xxx_phy_interface_modes) &&
++ mv88e6xxx_phy_interface_modes[cmode])
++ __set_bit(mv88e6xxx_phy_interface_modes[cmode], supported);
++ else if (cmode == MV88E6XXX_PORT_STS_CMODE_RGMII)
++ phy_interface_set_rgmii(supported);
++}
++
++static void mv88e6250_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
++ struct phylink_config *config)
++{
++ unsigned long *supported = config->supported_interfaces;
++
++ /* Translate the default cmode */
++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported);
++
++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100;
++}
++
++static int mv88e6352_get_port4_serdes_cmode(struct mv88e6xxx_chip *chip)
++{
++ u16 reg, val;
++ int err;
++
++ err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, &reg);
++ if (err)
++ return err;
++
++ /* If PHY_DETECT is zero, then we are not in auto-media mode */
++ if (!(reg & MV88E6XXX_PORT_STS_PHY_DETECT))
++ return 0xf;
++
++ val = reg & ~MV88E6XXX_PORT_STS_PHY_DETECT;
++ err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, val);
++ if (err)
++ return err;
++
++ err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, &val);
++ if (err)
++ return err;
++
++ /* Restore PHY_DETECT value */
++ err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, reg);
++ if (err)
++ return err;
++
++ return val & MV88E6XXX_PORT_STS_CMODE_MASK;
++}
++
++static void mv88e6352_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
++ struct phylink_config *config)
++{
++ unsigned long *supported = config->supported_interfaces;
++ int err, cmode;
++
++ /* Translate the default cmode */
++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported);
++
++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 |
++ MAC_1000FD;
++
++ /* Port 4 supports automedia if the serdes is associated with it. */
++ if (port == 4) {
++ mv88e6xxx_reg_lock(chip);
++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
++ if (err < 0)
++ dev_err(chip->dev, "p%d: failed to read scratch\n",
++ port);
++ if (err <= 0)
++ goto unlock;
++
++ cmode = mv88e6352_get_port4_serdes_cmode(chip);
++ if (cmode < 0)
++ dev_err(chip->dev, "p%d: failed to read serdes cmode\n",
++ port);
++ else
++ mv88e6xxx_translate_cmode(cmode, supported);
++unlock:
++ mv88e6xxx_reg_unlock(chip);
++ }
++}
++
++static void mv88e6341_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
++ struct phylink_config *config)
++{
++ unsigned long *supported = config->supported_interfaces;
++
++ /* Translate the default cmode */
++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported);
++
++ /* No ethtool bits for 200Mbps */
++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 |
++ MAC_1000FD;
++
++ /* The C_Mode field is programmable on port 5 */
++ if (port == 5) {
++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported);
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
++
++ config->mac_capabilities |= MAC_2500FD;
++ }
++}
++
++static void mv88e6390_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
++ struct phylink_config *config)
++{
++ unsigned long *supported = config->supported_interfaces;
++
++ /* Translate the default cmode */
++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported);
++
++ /* No ethtool bits for 200Mbps */
++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 |
++ MAC_1000FD;
++
++ /* The C_Mode field is programmable on ports 9 and 10 */
++ if (port == 9 || port == 10) {
++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported);
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
++
++ config->mac_capabilities |= MAC_2500FD;
++ }
++}
++
++static void mv88e6390x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
++ struct phylink_config *config)
++{
++ unsigned long *supported = config->supported_interfaces;
++
++ mv88e6390_phylink_get_caps(chip, port, config);
++
++ /* For the 6x90X, ports 2-7 can be in automedia mode.
++ * (Note that 6x90 doesn't support RXAUI nor XAUI).
++ *
++ * Port 2 can also support 1000BASE-X in automedia mode if port 9 is
++ * configured for 1000BASE-X, SGMII or 2500BASE-X.
++ * Port 3-4 can also support 1000BASE-X in automedia mode if port 9 is
++ * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X.
++ *
++ * Port 5 can also support 1000BASE-X in automedia mode if port 10 is
++ * configured for 1000BASE-X, SGMII or 2500BASE-X.
++ * Port 6-7 can also support 1000BASE-X in automedia mode if port 10 is
++ * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X.
++ *
++ * For now, be permissive (as the old code was) and allow 1000BASE-X
++ * on ports 2..7.
++ */
++ if (port >= 2 && port <= 7)
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
++
++ /* The C_Mode field can also be programmed for 10G speeds */
++ if (port == 9 || port == 10) {
++ __set_bit(PHY_INTERFACE_MODE_XAUI, supported);
++ __set_bit(PHY_INTERFACE_MODE_RXAUI, supported);
++
++ config->mac_capabilities |= MAC_10000FD;
++ }
++}
++
++static void mv88e6393x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
++ struct phylink_config *config)
++{
++ unsigned long *supported = config->supported_interfaces;
++ bool is_6191x =
++ chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X;
++
++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported);
++
++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 |
++ MAC_1000FD;
++
++ /* The C_Mode field can be programmed for ports 0, 9 and 10 */
++ if (port == 0 || port == 9 || port == 10) {
++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported);
++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
++
++ /* 6191X supports >1G modes only on port 10 */
++ if (!is_6191x || port == 10) {
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
++ __set_bit(PHY_INTERFACE_MODE_5GBASER, supported);
++ __set_bit(PHY_INTERFACE_MODE_10GBASER, supported);
++ /* FIXME: USXGMII is not supported yet */
++ /* __set_bit(PHY_INTERFACE_MODE_USXGMII, supported); */
++
++ config->mac_capabilities |= MAC_2500FD | MAC_5000FD |
++ MAC_10000FD;
++ }
++ }
++}
++
++static void mv88e6xxx_get_caps(struct dsa_switch *ds, int port,
++ struct phylink_config *config)
++{
++ struct mv88e6xxx_chip *chip = ds->priv;
++
++ chip->info->ops->phylink_get_caps(chip, port, config);
++
++ /* Internal ports need GMII for PHYLIB */
++ if (mv88e6xxx_phy_is_internal(ds, port))
++ __set_bit(PHY_INTERFACE_MODE_GMII,
++ config->supported_interfaces);
+ }
+
+ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
+@@ -3599,6 +3839,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
+ .rmu_disable = mv88e6085_g1_rmu_disable,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+@@ -3633,6 +3874,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
+ .reset = mv88e6185_g1_reset,
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+@@ -3680,6 +3922,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
+ .rmu_disable = mv88e6085_g1_rmu_disable,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+@@ -3717,6 +3960,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = {
+ .atu_set_hash = mv88e6165_g1_atu_set_hash,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+@@ -3758,6 +4002,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = {
+ .reset = mv88e6185_g1_reset,
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+@@ -3822,6 +4067,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
+ .serdes_get_stats = mv88e6390_serdes_get_stats,
+ .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
++ .phylink_get_caps = mv88e6341_phylink_get_caps,
+ .phylink_validate = mv88e6341_phylink_validate,
+ };
+
+@@ -3864,6 +4110,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .avb_ops = &mv88e6165_avb_ops,
+ .ptp_ops = &mv88e6165_ptp_ops,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+@@ -3900,6 +4147,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = {
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .avb_ops = &mv88e6165_avb_ops,
+ .ptp_ops = &mv88e6165_ptp_ops,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+@@ -3942,6 +4190,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {
+ .atu_set_hash = mv88e6165_g1_atu_set_hash,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+@@ -3997,6 +4246,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
+ .serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6352_serdes_get_regs,
+ .gpio_ops = &mv88e6352_gpio_ops,
++ .phylink_get_caps = mv88e6352_phylink_get_caps,
+ .phylink_validate = mv88e6352_phylink_validate,
+ };
+
+@@ -4039,6 +4289,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = {
+ .atu_set_hash = mv88e6165_g1_atu_set_hash,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+@@ -4097,6 +4348,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
+ .serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6352_serdes_get_regs,
+ .gpio_ops = &mv88e6352_gpio_ops,
++ .phylink_get_caps = mv88e6352_phylink_get_caps,
+ .phylink_validate = mv88e6352_phylink_validate,
+ };
+
+@@ -4136,6 +4388,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = {
+ .reset = mv88e6185_g1_reset,
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+@@ -4198,6 +4451,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
+ .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
+ .gpio_ops = &mv88e6352_gpio_ops,
++ .phylink_get_caps = mv88e6390_phylink_get_caps,
+ .phylink_validate = mv88e6390_phylink_validate,
+ };
+
+@@ -4259,6 +4513,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
+ .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
+ .gpio_ops = &mv88e6352_gpio_ops,
++ .phylink_get_caps = mv88e6390x_phylink_get_caps,
+ .phylink_validate = mv88e6390x_phylink_validate,
+ };
+
+@@ -4319,6 +4574,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
+ .avb_ops = &mv88e6390_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
++ .phylink_get_caps = mv88e6390_phylink_get_caps,
+ .phylink_validate = mv88e6390_phylink_validate,
+ };
+
+@@ -4379,6 +4635,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
++ .phylink_get_caps = mv88e6352_phylink_get_caps,
+ .phylink_validate = mv88e6352_phylink_validate,
+ };
+
+@@ -4419,6 +4676,7 @@ static const struct mv88e6xxx_ops mv88e6250_ops = {
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6250_ptp_ops,
++ .phylink_get_caps = mv88e6250_phylink_get_caps,
+ .phylink_validate = mv88e6065_phylink_validate,
+ };
+
+@@ -4481,6 +4739,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6390_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
++ .phylink_get_caps = mv88e6390_phylink_get_caps,
+ .phylink_validate = mv88e6390_phylink_validate,
+ };
+
+@@ -4525,6 +4784,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+@@ -4567,6 +4827,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+@@ -4633,6 +4894,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
+ .serdes_get_stats = mv88e6390_serdes_get_stats,
+ .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
++ .phylink_get_caps = mv88e6341_phylink_get_caps,
+ .phylink_validate = mv88e6341_phylink_validate,
+ };
+
+@@ -4675,6 +4937,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = {
+ .atu_set_hash = mv88e6165_g1_atu_set_hash,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+@@ -4719,6 +4982,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
++ .phylink_get_caps = mv88e6185_phylink_get_caps,
+ .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+@@ -4782,6 +5046,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
+ .serdes_get_stats = mv88e6352_serdes_get_stats,
+ .serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6352_serdes_get_regs,
++ .phylink_get_caps = mv88e6352_phylink_get_caps,
+ .phylink_validate = mv88e6352_phylink_validate,
+ };
+
+@@ -4847,6 +5112,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
+ .serdes_get_stats = mv88e6390_serdes_get_stats,
+ .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
++ .phylink_get_caps = mv88e6390_phylink_get_caps,
+ .phylink_validate = mv88e6390_phylink_validate,
+ };
+
+@@ -4911,6 +5177,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6390_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
++ .phylink_get_caps = mv88e6390x_phylink_get_caps,
+ .phylink_validate = mv88e6390x_phylink_validate,
+ };
+
+@@ -4975,6 +5242,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .avb_ops = &mv88e6390_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
++ .phylink_get_caps = mv88e6393x_phylink_get_caps,
+ .phylink_validate = mv88e6393x_phylink_validate,
+ };
+
+@@ -6244,6 +6512,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
+ .teardown = mv88e6xxx_teardown,
+ .port_setup = mv88e6xxx_port_setup,
+ .port_teardown = mv88e6xxx_port_teardown,
++ .phylink_get_caps = mv88e6xxx_get_caps,
+ .phylink_validate = mv88e6xxx_validate,
+ .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state,
+ .phylink_mac_config = mv88e6xxx_mac_config,
+diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
+index 8271b8aa7b71..5b0ee59b3c94 100644
+--- a/drivers/net/dsa/mv88e6xxx/chip.h
++++ b/drivers/net/dsa/mv88e6xxx/chip.h
+@@ -609,6 +609,8 @@ struct mv88e6xxx_ops {
+ const struct mv88e6xxx_ptp_ops *ptp_ops;
+
+ /* Phylink */
++ void (*phylink_get_caps)(struct mv88e6xxx_chip *chip, int port,
++ struct phylink_config *config);
+ void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port,
+ unsigned long *mask,
+ struct phylink_link_state *state);
+diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
+index 03382b66f800..ea6adfcfb42c 100644
+--- a/drivers/net/dsa/mv88e6xxx/port.h
++++ b/drivers/net/dsa/mv88e6xxx/port.h
+@@ -42,6 +42,11 @@
+ #define MV88E6XXX_PORT_STS_TX_PAUSED 0x0020
+ #define MV88E6XXX_PORT_STS_FLOW_CTL 0x0010
+ #define MV88E6XXX_PORT_STS_CMODE_MASK 0x000f
++#define MV88E6XXX_PORT_STS_CMODE_MII_PHY 0x0001
++#define MV88E6XXX_PORT_STS_CMODE_MII 0x0002
++#define MV88E6XXX_PORT_STS_CMODE_GMII 0x0003
++#define MV88E6XXX_PORT_STS_CMODE_RMII_PHY 0x0004
++#define MV88E6XXX_PORT_STS_CMODE_RMII 0x0005
+ #define MV88E6XXX_PORT_STS_CMODE_RGMII 0x0007
+ #define MV88E6XXX_PORT_STS_CMODE_100BASEX 0x0008
+ #define MV88E6XXX_PORT_STS_CMODE_1000BASEX 0x0009
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/784-v5.19-3-net-dsa-mv88e6xxx-convert-to-phylink_generic_validat.patch b/pkgs/patches-linux-5.15/784-v5.19-3-net-dsa-mv88e6xxx-convert-to-phylink_generic_validat.patch
new file mode 100644
index 0000000..ca67edc
--- /dev/null
+++ b/pkgs/patches-linux-5.15/784-v5.19-3-net-dsa-mv88e6xxx-convert-to-phylink_generic_validat.patch
@@ -0,0 +1,407 @@
+From 632d16a663154c8a02810ea266bd6cff31316f9f Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 3 Feb 2022 13:30:47 +0000
+Subject: [PATCH 3/6] net: dsa: mv88e6xxx: convert to
+ phylink_generic_validate()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Now that the mv88e6xxx chip drivers are supplying the supported
+interfaces and MAC capabilities, switch the driver to use the generic
+phylink validation implementation by removing our own validation
+implementations. This causes DSA to call phylink_generic_validate()
+on our behalf.
+
+Reviewed-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/dsa/mv88e6xxx/chip.c | 153 -------------------------------
+ drivers/net/dsa/mv88e6xxx/chip.h | 3 -
+ 2 files changed, 156 deletions(-)
+
+diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
+index c86a08274ca3..7172b3e2e323 100644
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -563,130 +563,6 @@ static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
+ return 0;
+ }
+
+-static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port,
+- unsigned long *mask,
+- struct phylink_link_state *state)
+-{
+- 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);
+- }
+-}
+-
+-static void mv88e6185_phylink_validate(struct mv88e6xxx_chip *chip, int port,
+- unsigned long *mask,
+- struct phylink_link_state *state)
+-{
+- /* FIXME: if the port is in 1000Base-X mode, then it only supports
+- * 1000M FD speeds. In this case, CMODE will indicate 5.
+- */
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+-
+- mv88e6065_phylink_validate(chip, port, mask, state);
+-}
+-
+-static void mv88e6341_phylink_validate(struct mv88e6xxx_chip *chip, int port,
+- unsigned long *mask,
+- struct phylink_link_state *state)
+-{
+- if (port >= 5)
+- phylink_set(mask, 2500baseX_Full);
+-
+- /* No ethtool bits for 200Mbps */
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+-
+- mv88e6065_phylink_validate(chip, port, mask, state);
+-}
+-
+-static void mv88e6352_phylink_validate(struct mv88e6xxx_chip *chip, int port,
+- unsigned long *mask,
+- struct phylink_link_state *state)
+-{
+- /* No ethtool bits for 200Mbps */
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+-
+- mv88e6065_phylink_validate(chip, port, mask, state);
+-}
+-
+-static void mv88e6390_phylink_validate(struct mv88e6xxx_chip *chip, int port,
+- unsigned long *mask,
+- struct phylink_link_state *state)
+-{
+- if (port >= 9) {
+- phylink_set(mask, 2500baseX_Full);
+- phylink_set(mask, 2500baseT_Full);
+- }
+-
+- /* No ethtool bits for 200Mbps */
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+-
+- mv88e6065_phylink_validate(chip, port, mask, state);
+-}
+-
+-static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port,
+- unsigned long *mask,
+- struct phylink_link_state *state)
+-{
+- if (port >= 9) {
+- phylink_set(mask, 10000baseT_Full);
+- phylink_set(mask, 10000baseKR_Full);
+- }
+-
+- mv88e6390_phylink_validate(chip, port, mask, state);
+-}
+-
+-static void mv88e6393x_phylink_validate(struct mv88e6xxx_chip *chip, int port,
+- unsigned long *mask,
+- struct phylink_link_state *state)
+-{
+- bool is_6191x =
+- chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X;
+-
+- if (((port == 0 || port == 9) && !is_6191x) || port == 10) {
+- phylink_set(mask, 10000baseT_Full);
+- phylink_set(mask, 10000baseKR_Full);
+- phylink_set(mask, 10000baseCR_Full);
+- phylink_set(mask, 10000baseSR_Full);
+- phylink_set(mask, 10000baseLR_Full);
+- phylink_set(mask, 10000baseLRM_Full);
+- phylink_set(mask, 10000baseER_Full);
+- phylink_set(mask, 5000baseT_Full);
+- phylink_set(mask, 2500baseX_Full);
+- phylink_set(mask, 2500baseT_Full);
+- }
+-
+- phylink_set(mask, 1000baseT_Full);
+- phylink_set(mask, 1000baseX_Full);
+-
+- mv88e6065_phylink_validate(chip, port, mask, state);
+-}
+-
+-static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
+- unsigned long *supported,
+- struct phylink_link_state *state)
+-{
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+- struct mv88e6xxx_chip *chip = ds->priv;
+-
+- /* Allow all the expected bits */
+- phylink_set(mask, Autoneg);
+- phylink_set(mask, Pause);
+- phylink_set_port_modes(mask);
+-
+- if (chip->info->ops->phylink_validate)
+- chip->info->ops->phylink_validate(chip, port, mask, state);
+-
+- linkmode_and(supported, supported, mask);
+- linkmode_and(state->advertising, state->advertising, mask);
+-}
+-
+ static const u8 mv88e6185_phy_interface_modes[] = {
+ [MV88E6185_PORT_STS_CMODE_GMII_FD] = PHY_INTERFACE_MODE_GMII,
+ [MV88E6185_PORT_STS_CMODE_MII_100_FD_PS] = PHY_INTERFACE_MODE_MII,
+@@ -3840,7 +3716,6 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+
+@@ -3875,7 +3750,6 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+
+@@ -3923,7 +3797,6 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+
+@@ -3961,7 +3834,6 @@ static const struct mv88e6xxx_ops mv88e6123_ops = {
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+
+@@ -4003,7 +3875,6 @@ static const struct mv88e6xxx_ops mv88e6131_ops = {
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6141_ops = {
+@@ -4068,7 +3939,6 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
+ .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
+ .phylink_get_caps = mv88e6341_phylink_get_caps,
+- .phylink_validate = mv88e6341_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6161_ops = {
+@@ -4111,7 +3981,6 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {
+ .avb_ops = &mv88e6165_avb_ops,
+ .ptp_ops = &mv88e6165_ptp_ops,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+
+@@ -4148,7 +4017,6 @@ static const struct mv88e6xxx_ops mv88e6165_ops = {
+ .avb_ops = &mv88e6165_avb_ops,
+ .ptp_ops = &mv88e6165_ptp_ops,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6171_ops = {
+@@ -4191,7 +4059,6 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6172_ops = {
+@@ -4247,7 +4114,6 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
+ .serdes_get_regs = mv88e6352_serdes_get_regs,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .phylink_get_caps = mv88e6352_phylink_get_caps,
+- .phylink_validate = mv88e6352_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6175_ops = {
+@@ -4290,7 +4156,6 @@ static const struct mv88e6xxx_ops mv88e6175_ops = {
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6176_ops = {
+@@ -4349,7 +4214,6 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
+ .serdes_get_regs = mv88e6352_serdes_get_regs,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .phylink_get_caps = mv88e6352_phylink_get_caps,
+- .phylink_validate = mv88e6352_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6185_ops = {
+@@ -4389,7 +4253,6 @@ static const struct mv88e6xxx_ops mv88e6185_ops = {
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+
+@@ -4452,7 +4315,6 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .phylink_get_caps = mv88e6390_phylink_get_caps,
+- .phylink_validate = mv88e6390_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6190x_ops = {
+@@ -4514,7 +4376,6 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
+ .gpio_ops = &mv88e6352_gpio_ops,
+ .phylink_get_caps = mv88e6390x_phylink_get_caps,
+- .phylink_validate = mv88e6390x_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6191_ops = {
+@@ -4575,7 +4436,6 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
+ .avb_ops = &mv88e6390_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_caps = mv88e6390_phylink_get_caps,
+- .phylink_validate = mv88e6390_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6240_ops = {
+@@ -4636,7 +4496,6 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_caps = mv88e6352_phylink_get_caps,
+- .phylink_validate = mv88e6352_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6250_ops = {
+@@ -4677,7 +4536,6 @@ static const struct mv88e6xxx_ops mv88e6250_ops = {
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6250_ptp_ops,
+ .phylink_get_caps = mv88e6250_phylink_get_caps,
+- .phylink_validate = mv88e6065_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6290_ops = {
+@@ -4740,7 +4598,6 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
+ .avb_ops = &mv88e6390_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_caps = mv88e6390_phylink_get_caps,
+- .phylink_validate = mv88e6390_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6320_ops = {
+@@ -4785,7 +4642,6 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6321_ops = {
+@@ -4828,7 +4684,6 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6341_ops = {
+@@ -4895,7 +4750,6 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
+ .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
+ .phylink_get_caps = mv88e6341_phylink_get_caps,
+- .phylink_validate = mv88e6341_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6350_ops = {
+@@ -4938,7 +4792,6 @@ static const struct mv88e6xxx_ops mv88e6350_ops = {
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6351_ops = {
+@@ -4983,7 +4836,6 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
+ .avb_ops = &mv88e6352_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_caps = mv88e6185_phylink_get_caps,
+- .phylink_validate = mv88e6185_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6352_ops = {
+@@ -5047,7 +4899,6 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
+ .serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6352_serdes_get_regs,
+ .phylink_get_caps = mv88e6352_phylink_get_caps,
+- .phylink_validate = mv88e6352_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6390_ops = {
+@@ -5113,7 +4964,6 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
+ .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
+ .serdes_get_regs = mv88e6390_serdes_get_regs,
+ .phylink_get_caps = mv88e6390_phylink_get_caps,
+- .phylink_validate = mv88e6390_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6390x_ops = {
+@@ -5178,7 +5028,6 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
+ .avb_ops = &mv88e6390_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_caps = mv88e6390x_phylink_get_caps,
+- .phylink_validate = mv88e6390x_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_ops mv88e6393x_ops = {
+@@ -5243,7 +5092,6 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {
+ .avb_ops = &mv88e6390_avb_ops,
+ .ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_caps = mv88e6393x_phylink_get_caps,
+- .phylink_validate = mv88e6393x_phylink_validate,
+ };
+
+ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
+@@ -6513,7 +6361,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
+ .port_setup = mv88e6xxx_port_setup,
+ .port_teardown = mv88e6xxx_port_teardown,
+ .phylink_get_caps = mv88e6xxx_get_caps,
+- .phylink_validate = mv88e6xxx_validate,
+ .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state,
+ .phylink_mac_config = mv88e6xxx_mac_config,
+ .phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart,
+diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
+index 5b0ee59b3c94..3a685d0cf07c 100644
+--- a/drivers/net/dsa/mv88e6xxx/chip.h
++++ b/drivers/net/dsa/mv88e6xxx/chip.h
+@@ -611,9 +611,6 @@ struct mv88e6xxx_ops {
+ /* Phylink */
+ void (*phylink_get_caps)(struct mv88e6xxx_chip *chip, int port,
+ struct phylink_config *config);
+- void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port,
+- unsigned long *mask,
+- struct phylink_link_state *state);
+
+ /* Max Frame Size */
+ int (*set_max_frame_size)(struct mv88e6xxx_chip *chip, int mtu);
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/784-v5.19-4-net-dsa-mv88e6xxx-improve-88e6352-serdes-statistics-.patch b/pkgs/patches-linux-5.15/784-v5.19-4-net-dsa-mv88e6xxx-improve-88e6352-serdes-statistics-.patch
new file mode 100644
index 0000000..ce63919
--- /dev/null
+++ b/pkgs/patches-linux-5.15/784-v5.19-4-net-dsa-mv88e6xxx-improve-88e6352-serdes-statistics-.patch
@@ -0,0 +1,125 @@
+From d84031286e44a08a3512a8e98a6f3a4e37e6c11d Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Thu, 3 Feb 2022 13:30:52 +0000
+Subject: [PATCH 4/6] net: dsa: mv88e6xxx: improve 88e6352 serdes statistics
+ detection
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The decision whether to report serdes statistics currently depends on
+the cached C_Mode value for the port, read at probe time or updated by
+configuration. However, port 4 can be in "automedia" mode when it is
+used as a serdes port, meaning it switches between the internal PHY and
+the serdes, changing the read-only C_Mode value depending on which
+first gains link. Consequently, the C_Mode value read at probe does not
+accurately reflect whether the port has the serdes associated with it.
+
+In "net: dsa: mv88e6xxx: add mv88e6352_g2_scratch_port_has_serdes()",
+we added a way to read the hardware configuration to determine which
+port has the serdes associated with it. Use this to determine which
+port reports the serdes statistics.
+
+Reviewed-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mv88e6xxx/serdes.c | 43 ++++++++++++++++--------------
+ 1 file changed, 23 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c
+index 6ae7a0ed9e0b..bde2228b674d 100644
+--- a/drivers/net/dsa/mv88e6xxx/serdes.c
++++ b/drivers/net/dsa/mv88e6xxx/serdes.c
+@@ -267,14 +267,6 @@ int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
+ return lane;
+ }
+
+-static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
+-{
+- if (mv88e6xxx_serdes_get_lane(chip, port) >= 0)
+- return true;
+-
+- return false;
+-}
+-
+ struct mv88e6352_serdes_hw_stat {
+ char string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+@@ -288,20 +280,24 @@ static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
+
+ int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
+ {
+- if (mv88e6352_port_has_serdes(chip, port))
+- return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
++ int err;
+
+- return 0;
++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
++ if (err <= 0)
++ return err;
++
++ return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
+ }
+
+ int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
+ int port, uint8_t *data)
+ {
+ struct mv88e6352_serdes_hw_stat *stat;
+- int i;
++ int err, i;
+
+- if (!mv88e6352_port_has_serdes(chip, port))
+- return 0;
++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
++ if (err <= 0)
++ return err;
+
+ for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
+ stat = &mv88e6352_serdes_hw_stats[i];
+@@ -343,11 +339,12 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
+ {
+ struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
+ struct mv88e6352_serdes_hw_stat *stat;
++ int i, err;
+ u64 value;
+- int i;
+
+- if (!mv88e6352_port_has_serdes(chip, port))
+- return 0;
++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
++ if (err <= 0)
++ return err;
+
+ BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
+ ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
+@@ -414,8 +411,13 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
+
+ int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
+ {
+- if (!mv88e6352_port_has_serdes(chip, port))
+- return 0;
++ int err;
++
++ mv88e6xxx_reg_lock(chip);
++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
++ mv88e6xxx_reg_unlock(chip);
++ if (err <= 0)
++ return err;
+
+ return 32 * sizeof(u16);
+ }
+@@ -427,7 +429,8 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
+ int err;
+ int i;
+
+- if (!mv88e6352_port_has_serdes(chip, port))
++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
++ if (err <= 0)
+ return;
+
+ for (i = 0 ; i < 32; i++) {
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/784-v5.19-5-net-dsa-mv88e6xxx-Fix-off-by-in-one-in-mv88e6185_phy.patch b/pkgs/patches-linux-5.15/784-v5.19-5-net-dsa-mv88e6xxx-Fix-off-by-in-one-in-mv88e6185_phy.patch
new file mode 100644
index 0000000..7679555
--- /dev/null
+++ b/pkgs/patches-linux-5.15/784-v5.19-5-net-dsa-mv88e6xxx-Fix-off-by-in-one-in-mv88e6185_phy.patch
@@ -0,0 +1,33 @@
+From f0fd1787a0db7b23ec859e50af3b66d9e8cc8c56 Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Mon, 7 Feb 2022 11:22:53 +0300
+Subject: [PATCH 5/6] net: dsa: mv88e6xxx: Fix off by in one in
+ mv88e6185_phylink_get_caps()
+
+The <= ARRAY_SIZE() needs to be < ARRAY_SIZE() to prevent an out of
+bounds error.
+
+Fixes: d4ebf12bcec4 ("net: dsa: mv88e6xxx: populate supported_interfaces and mac_capabilities")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mv88e6xxx/chip.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
+index 7172b3e2e323..5bac1d54325e 100644
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -578,7 +578,7 @@ static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
+ {
+ u8 cmode = chip->ports[port].cmode;
+
+- if (cmode <= ARRAY_SIZE(mv88e6185_phy_interface_modes) &&
++ if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) &&
+ mv88e6185_phy_interface_modes[cmode])
+ __set_bit(mv88e6185_phy_interface_modes[cmode],
+ config->supported_interfaces);
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/784-v5.19-6-net-dsa-mv88e6xxx-Fix-validation-of-built-in-PHYs-on.patch b/pkgs/patches-linux-5.15/784-v5.19-6-net-dsa-mv88e6xxx-Fix-validation-of-built-in-PHYs-on.patch
new file mode 100644
index 0000000..f3e5410
--- /dev/null
+++ b/pkgs/patches-linux-5.15/784-v5.19-6-net-dsa-mv88e6xxx-Fix-validation-of-built-in-PHYs-on.patch
@@ -0,0 +1,73 @@
+From d2056d3cd927b7c0e0f1a466f77f21b0f6658e7b Mon Sep 17 00:00:00 2001
+From: Tobias Waldekranz <tobias@waldekranz.com>
+Date: Sun, 13 Feb 2022 19:51:54 +0100
+Subject: [PATCH 6/6] net: dsa: mv88e6xxx: Fix validation of built-in PHYs on
+ 6095/6097
+
+These chips have 8 built-in FE PHYs and 3 SERDES interfaces that can
+run at 1G. With the blamed commit, the built-in PHYs could no longer
+be connected to, using an MII PHY interface mode.
+
+Create a separate .phylink_get_caps callback for these chips, which
+takes the FE/GE split into consideration.
+
+Fixes: 2ee84cfefb1e ("net: dsa: mv88e6xxx: convert to phylink_generic_validate()")
+Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Link: https://lore.kernel.org/r/20220213185154.3262207-1-tobias@waldekranz.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mv88e6xxx/chip.c | 23 +++++++++++++++++++++--
+ 1 file changed, 21 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
+index 5bac1d54325e..5c3a490a206e 100644
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -573,6 +573,25 @@ static const u8 mv88e6185_phy_interface_modes[] = {
+ [MV88E6185_PORT_STS_CMODE_PHY] = PHY_INTERFACE_MODE_SGMII,
+ };
+
++static void mv88e6095_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
++ struct phylink_config *config)
++{
++ u8 cmode = chip->ports[port].cmode;
++
++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100;
++
++ if (mv88e6xxx_phy_is_internal(chip->ds, port)) {
++ __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces);
++ } else {
++ if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) &&
++ mv88e6185_phy_interface_modes[cmode])
++ __set_bit(mv88e6185_phy_interface_modes[cmode],
++ config->supported_interfaces);
++
++ config->mac_capabilities |= MAC_1000FD;
++ }
++}
++
+ static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
+ struct phylink_config *config)
+ {
+@@ -3749,7 +3768,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
+ .reset = mv88e6185_g1_reset,
+ .vtu_getnext = mv88e6185_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+- .phylink_get_caps = mv88e6185_phylink_get_caps,
++ .phylink_get_caps = mv88e6095_phylink_get_caps,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+
+@@ -3796,7 +3815,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
+ .rmu_disable = mv88e6085_g1_rmu_disable,
+ .vtu_getnext = mv88e6352_g1_vtu_getnext,
+ .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+- .phylink_get_caps = mv88e6185_phylink_get_caps,
++ .phylink_get_caps = mv88e6095_phylink_get_caps,
+ .set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ };
+
+--
+2.35.1
+
diff --git a/pkgs/patches-linux-5.15/800-GPIO-add-named-gpio-exports.patch b/pkgs/patches-linux-5.15/800-GPIO-add-named-gpio-exports.patch
new file mode 100644
index 0000000..9fde1ea
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -715,6 +715,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);
+@@ -722,6 +723,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/pkgs/patches-linux-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/pkgs/patches-linux-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch
new file mode 100644
index 0000000..478a2cb
--- /dev/null
+++ b/pkgs/patches-linux-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch
@@ -0,0 +1,73 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Subject: [PATCH] bcma: get SoC device struct & copy its DMA params to the
+ subdevices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For bus devices to be fully usable it's required to set their DMA
+parameters.
+
+For years it has been missing and remained unnoticed because of
+mips_dma_alloc_coherent() silently handling the empty coherent_dma_mask.
+Kernel 4.19 came with a lot of DMA changes and caused a regression on
+the bcm47xx. Starting with the commit f8c55dc6e828 ("MIPS: use generic
+dma noncoherent ops for simple noncoherent platforms") DMA coherent
+allocations just fail. Example:
+[ 1.114914] bgmac_bcma bcma0:2: Allocation of TX ring 0x200 failed
+[ 1.121215] bgmac_bcma bcma0:2: Unable to alloc memory for DMA
+[ 1.127626] bgmac_bcma: probe of bcma0:2 failed with error -12
+[ 1.133838] bgmac_bcma: Broadcom 47xx GBit MAC driver loaded
+
+This change fixes above regression in addition to the MIPS bcm47xx
+commit 321c46b91550 ("MIPS: BCM47XX: Setup struct device for the SoC").
+
+It also fixes another *old* GPIO regression caused by a parent pointing
+to the NULL:
+[ 0.157054] missing gpiochip .dev parent pointer
+[ 0.157287] bcma: bus0: Error registering GPIO driver: -22
+introduced by the commit 74f4e0cc6108 ("bcma: switch GPIO portions to
+use GPIOLIB_IRQCHIP").
+
+Fixes: f8c55dc6e828 ("MIPS: use generic dma noncoherent ops for simple noncoherent platforms")
+Fixes: 74f4e0cc6108 ("bcma: switch GPIO portions to use GPIOLIB_IRQCHIP")
+Cc: linux-mips@linux-mips.org
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Linus Walleij <linus.walleij@linaro.org>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+
+--- a/drivers/bcma/host_soc.c
++++ b/drivers/bcma/host_soc.c
+@@ -191,6 +191,8 @@ int __init bcma_host_soc_init(struct bcm
+ struct bcma_bus *bus = &soc->bus;
+ int err;
+
++ bus->dev = soc->dev;
++
+ /* Scan bus and initialize it */
+ err = bcma_bus_early_register(bus);
+ if (err)
+--- a/drivers/bcma/main.c
++++ b/drivers/bcma/main.c
+@@ -236,13 +236,17 @@ EXPORT_SYMBOL(bcma_core_irq);
+
+ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
+ {
+- device_initialize(&core->dev);
++ struct device *dev = &core->dev;
++
++ device_initialize(dev);
+ core->dev.release = bcma_release_core_dev;
+ core->dev.bus = &bcma_bus_type;
+- dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
++ dev_set_name(dev, "bcma%d:%d", bus->num, core->core_index);
+ core->dev.parent = bus->dev;
+- if (bus->dev)
++ if (bus->dev) {
+ bcma_of_fill_device(bus->dev, core);
++ dma_coerce_mask_and_coherent(dev, bus->dev->coherent_dma_mask);
++ }
+
+ switch (bus->hosttype) {
+ case BCMA_HOSTTYPE_PCI:
diff --git a/pkgs/patches-linux-5.15/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch b/pkgs/patches-linux-5.15/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch
new file mode 100644
index 0000000..29f36be
--- /dev/null
+++ b/pkgs/patches-linux-5.15/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch
@@ -0,0 +1,40 @@
+From c28b2d367da8a471482e6a4aa8337ab6369a80c2 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@arm.linux.org.uk>
+Date: Sat, 3 Oct 2015 09:13:05 +0100
+Subject: cpuidle: mvebu: indicate failure to enter deeper sleep states
+
+The cpuidle ->enter method expects the return value to be the sleep
+state we entered. Returning negative numbers or other codes is not
+permissible since coupled CPU idle was merged.
+
+At least some of the mvebu_v7_cpu_suspend() implementations return the
+value from cpu_suspend(), which returns zero if the CPU vectors back
+into the kernel via cpu_resume() (the success case), or the non-zero
+return value of the suspend actor, or one (failure cases).
+
+We do not want to be returning the failure case value back to CPU idle
+as that indicates that we successfully entered one of the deeper idle
+states. Always return zero instead, indicating that we slept for the
+shortest amount of time.
+
+Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
+---
+ drivers/cpuidle/cpuidle-mvebu-v7.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/cpuidle/cpuidle-mvebu-v7.c
++++ b/drivers/cpuidle/cpuidle-mvebu-v7.c
+@@ -39,8 +39,12 @@ static int mvebu_v7_enter_idle(struct cp
+ ret = mvebu_v7_cpu_suspend(deepidle);
+ cpu_pm_exit();
+
++ /*
++ * If we failed to enter the desired state, indicate that we
++ * slept lightly.
++ */
+ if (ret)
+- return ret;
++ return 0;
+
+ return index;
+ }
diff --git a/pkgs/patches-linux-5.15/800-v5.20-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch b/pkgs/patches-linux-5.15/800-v5.20-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch
new file mode 100644
index 0000000..b1072ce
--- /dev/null
+++ b/pkgs/patches-linux-5.15/800-v5.20-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/pkgs/patches-linux-5.15/800-v5.20-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch b/pkgs/patches-linux-5.15/800-v5.20-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch
new file mode 100644
index 0000000..3765845
--- /dev/null
+++ b/pkgs/patches-linux-5.15/800-v5.20-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/pkgs/patches-linux-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch b/pkgs/patches-linux-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch
new file mode 100644
index 0000000..c1e14b9
--- /dev/null
+++ b/pkgs/patches-linux-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch
@@ -0,0 +1,222 @@
+From fc23ea48ba52c24f201fe5ca0132ee1a3de5a70a Mon Sep 17 00:00:00 2001
+From: Mauri Sandberg <maukka@ext.kapsi.fi>
+Date: Thu, 25 Mar 2021 11:48:05 +0200
+Subject: [PATCH 2/2] gpio: gpio-cascade: add generic GPIO cascade
+
+Adds support for building cascades of GPIO lines. That is, it allows
+setups when there is one upstream line and multiple cascaded lines, out
+of which one can be chosen at a time. The status of the upstream line
+can be conveyed to the selected cascaded line or, vice versa, the status
+of the cascaded line can be conveyed to the upstream line.
+
+A multiplexer is being used to select, which cascaded GPIO line is being
+used at any given time.
+
+At the moment only input direction is supported. In future it should be
+possible to add support for output direction, too.
+
+Signed-off-by: Mauri Sandberg <maukka@ext.kapsi.fi>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+---
+v7 -> v8:
+ - rearrange members in struct gpio_cascade
+ - cosmetic changes in file header and in one function declaration
+ - added Reviewed-by tags by Linus and Andy
+v6 -> v7:
+ - In Kconfig add info about module name
+ - adhere to new convention that allows lines longer than 80 chars
+ - use dev_probe_err with upstream gpio line too
+ - refactor for cleaner exit of probe function.
+v5 -> v6:
+ - In Kconfig, remove dependency to OF_GPIO and select only MULTIPLEXER
+ - refactor code preferring one-liners
+ - clean up prints, removing them from success-path.
+ - don't explicitly set gpio_chip.of_node as it's done in the GPIO library
+ - use devm_gpiochip_add_data instead of gpiochip_add
+v4 -> v5:
+ - renamed gpio-mux-input -> gpio-cascade. refactored code accordingly
+ here and there and changed to use new bindings and compatible string
+ - ambigious and vague 'pin' was rename to 'upstream_line'
+ - dropped Tested-by and Reviewed-by due to changes in bindings
+ - dropped Reported-by suggested by an automatic bot as it was not really
+ appropriate to begin with
+ - functionally it's the same as v4
+v3 -> v4:
+ - Changed author email
+ - Included Tested-by and Reviewed-by from Drew
+v2 -> v3:
+ - use managed device resources
+ - update Kconfig description
+v1 -> v2:
+ - removed .owner from platform_driver as per test bot's instruction
+ - added MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE
+ - added gpio_mux_input_get_direction as it's recommended for all chips
+ - removed because this is input only chip: gpio_mux_input_set_value
+ - removed because they are not needed for input/output only chips:
+ gpio_mux_input_direction_input
+ gpio_mux_input_direction_output
+ - fixed typo in an error message
+ - added info message about successful registration
+ - removed can_sleep flag as this does not sleep while getting GPIO value
+ like I2C or SPI do
+ - Updated description in Kconfig
+---
+ drivers/gpio/Kconfig | 15 +++++
+ drivers/gpio/Makefile | 1 +
+ drivers/gpio/gpio-cascade.c | 117 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 133 insertions(+)
+ create mode 100644 drivers/gpio/gpio-cascade.c
+
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -1683,4 +1683,19 @@ config GPIO_VIRTIO
+
+ endmenu
+
++comment "Other GPIO expanders"
++
++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/pkgs/patches-linux-5.15/801-v5.20-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch b/pkgs/patches-linux-5.15/801-v5.20-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch
new file mode 100644
index 0000000..483826a
--- /dev/null
+++ b/pkgs/patches-linux-5.15/801-v5.20-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/pkgs/patches-linux-5.15/801-v5.20-0002-leds-add-help-info-about-BCM63138-module-name.patch b/pkgs/patches-linux-5.15/801-v5.20-0002-leds-add-help-info-about-BCM63138-module-name.patch
new file mode 100644
index 0000000..5430b1f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/801-v5.20-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/pkgs/patches-linux-5.15/801-v5.20-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch b/pkgs/patches-linux-5.15/801-v5.20-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch
new file mode 100644
index 0000000..e125a54
--- /dev/null
+++ b/pkgs/patches-linux-5.15/801-v5.20-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/pkgs/patches-linux-5.15/810-pci_disable_common_quirks.patch b/pkgs/patches-linux-5.15/810-pci_disable_common_quirks.patch
new file mode 100644
index 0000000..7edbd94
--- /dev/null
+++ b/pkgs/patches-linux-5.15/810-pci_disable_common_quirks.patch
@@ -0,0 +1,62 @@
+From: Gabor Juhos <juhosg@openwrt.org>
+Subject: debloat: add kernel config option to disabling common PCI quirks
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/pci/Kconfig | 6 ++++++
+ drivers/pci/quirks.c | 6 ++++++
+ 2 files changed, 12 insertions(+)
+
+--- a/drivers/pci/Kconfig
++++ b/drivers/pci/Kconfig
+@@ -118,6 +118,13 @@ config XEN_PCIDEV_FRONTEND
+ The PCI device frontend driver allows the kernel to import arbitrary
+ PCI devices from a PCI backend to support PCI driver domains.
+
++config PCI_DISABLE_COMMON_QUIRKS
++ bool "PCI disable common quirks"
++ depends on PCI
++ help
++ If you don't know what to do here, say N.
++
++
+ 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/pkgs/patches-linux-5.15/811-pci_disable_usb_common_quirks.patch b/pkgs/patches-linux-5.15/811-pci_disable_usb_common_quirks.patch
new file mode 100644
index 0000000..67406ba
--- /dev/null
+++ b/pkgs/patches-linux-5.15/811-pci_disable_usb_common_quirks.patch
@@ -0,0 +1,115 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: debloat: disable common USB quirks
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/usb/host/pci-quirks.c | 16 ++++++++++++++++
+ drivers/usb/host/pci-quirks.h | 18 +++++++++++++++++-
+ include/linux/usb/hcd.h | 7 +++++++
+ 3 files changed, 40 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/host/pci-quirks.c
++++ b/drivers/usb/host/pci-quirks.c
+@@ -128,6 +128,8 @@ struct amd_chipset_type {
+ u8 rev;
+ };
+
++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
++
+ 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
+@@ -497,7 +497,14 @@ extern int usb_hcd_pci_probe(struct pci_
+ extern void usb_hcd_pci_remove(struct pci_dev *dev);
+ extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
+
++#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/pkgs/patches-linux-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch b/pkgs/patches-linux-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch
new file mode 100644
index 0000000..33eb34c
--- /dev/null
+++ b/pkgs/patches-linux-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch
@@ -0,0 +1,26 @@
+From d9c8bc8c1408f3e8529db6e4e04017b4c579c342 Mon Sep 17 00:00:00 2001
+From: Pawel Dembicki <paweldembicki@gmail.com>
+Date: Sun, 18 Feb 2018 17:08:04 +0100
+Subject: [PATCH] w1: gpio: fix problem with platfom data in w1-gpio
+
+In devices, where fdt is used, is impossible to apply platform data
+without proper fdt node.
+
+This patch allow to use platform data in devices with fdt.
+
+Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
+---
+ drivers/w1/masters/w1-gpio.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/drivers/w1/masters/w1-gpio.c
++++ b/drivers/w1/masters/w1-gpio.c
+@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform
+ enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
+ int err;
+
+- 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/pkgs/patches-linux-5.15/834-ledtrig-libata.patch b/pkgs/patches-linux-5.15/834-ledtrig-libata.patch
new file mode 100644
index 0000000..3977b57
--- /dev/null
+++ b/pkgs/patches-linux-5.15/834-ledtrig-libata.patch
@@ -0,0 +1,149 @@
+From: Daniel Golle <daniel@makrotopia.org>
+Subject: libata: add ledtrig support
+
+This adds a LED trigger for each ATA port indicating disk activity.
+
+As this is needed only on specific platforms (NAS SoCs and such),
+these platforms should define ARCH_WANTS_LIBATA_LEDS if there
+are boards with LED(s) intended to indicate ATA disk activity and
+need the OS to take care of that.
+In that way, if not selected, LED trigger support not will be
+included in libata-core and both, codepaths and structures remain
+untouched.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/ata/Kconfig | 16 ++++++++++++++++
+ drivers/ata/libata-core.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ include/linux/libata.h | 9 +++++++++
+ 3 files changed, 66 insertions(+)
+
+--- a/drivers/ata/Kconfig
++++ b/drivers/ata/Kconfig
+@@ -67,6 +67,22 @@ config ATA_FORCE
+
+ 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
+@@ -4576,6 +4589,9 @@ struct ata_queued_cmd *ata_qc_new_init(s
+ if (tag < 0)
+ return NULL;
+ }
++#ifdef CONFIG_ATA_LEDS
++ ata_led_act(ap);
++#endif
+
+ qc = __ata_qc_from_tag(ap, tag);
+ qc->tag = qc->hw_tag = tag;
+@@ -5354,6 +5370,9 @@ struct ata_port *ata_port_alloc(struct a
+ ap->stats.unhandled_irq = 1;
+ ap->stats.idle_irq = 1;
+ #endif
++#ifdef CONFIG_ATA_LEDS
++ ap->ledtrig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
++#endif
+ ata_sff_port_init(ap);
+
+ return ap;
+@@ -5389,6 +5408,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;
+ }
+@@ -5795,7 +5820,23 @@ int ata_host_register(struct ata_host *h
+ host->ports[i]->print_id = atomic_inc_return(&ata_print_id);
+ host->ports[i]->local_port_no = i + 1;
+ }
++#ifdef CONFIG_ATA_LEDS
++ for (i = 0; i < host->n_ports; i++) {
++ if (unlikely(!host->ports[i]->ledtrig))
++ continue;
+
++ 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
+@@ -888,6 +891,12 @@ struct ata_port {
+ #ifdef CONFIG_ATA_ACPI
+ struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */
+ #endif
++
++#ifdef CONFIG_ATA_LEDS
++ struct led_trigger *ledtrig;
++ char ledtrig_name[8];
++#endif
++
+ /* owned by EH */
+ u8 sector_buf[ATA_SECT_SIZE] ____cacheline_aligned;
+ };
diff --git a/pkgs/patches-linux-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch b/pkgs/patches-linux-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch
new file mode 100644
index 0000000..5ca8933
--- /dev/null
+++ b/pkgs/patches-linux-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch
@@ -0,0 +1,26 @@
+From d6988cf1d16faac56899918bb2b1be8d85155e3f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
+Date: Sat, 20 Feb 2021 18:36:38 +0100
+Subject: [PATCH] hwrng: bcm2835: set quality to 1000
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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/pkgs/patches-linux-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch b/pkgs/patches-linux-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch
new file mode 100644
index 0000000..530ab9b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch
@@ -0,0 +1,59 @@
+From 078c6a1cbd4cd7496048786beec2e312577bebbf Mon Sep 17 00:00:00 2001
+From: Pawel Dembicki <paweldembicki@gmail.com>
+Date: Tue, 11 Jan 2022 23:11:32 +0100
+Subject: [PATCH] net: qmi_wwan: add ZTE MF286D modem 19d2:1485
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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
+@@ -1313,6 +1313,7 @@ static const struct usb_device_id produc
+ {QMI_FIXED_INTF(0x19d2, 0x1426, 2)}, /* ZTE MF91 */
+ {QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */
+ {QMI_FIXED_INTF(0x19d2, 0x1432, 3)}, /* ZTE ME3620 */
++ {QMI_FIXED_INTF(0x19d2, 0x1485, 5)}, /* ZTE MF286D */
+ {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
+ {QMI_FIXED_INTF(0x2001, 0x7e16, 3)}, /* D-Link DWM-221 */
+ {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */
diff --git a/pkgs/patches-linux-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/pkgs/patches-linux-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch
new file mode 100644
index 0000000..de28ebe
--- /dev/null
+++ b/pkgs/patches-linux-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch
@@ -0,0 +1,102 @@
+From 663b9f99bb35dbc0c7b685f71ee3668a60d31320 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 10 Jan 2022 02:02:00 +0100
+Subject: [PATCH] PCI: aardvark: Make main irq_chip structure a static driver
+ structure
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Marc Zyngier says [1] that we should use struct irq_chip as a global
+static struct in the driver. Even though the structure currently
+contains a dynamic member (parent_device), Marc says [2] that he plans
+to kill it and make the structure completely static.
+
+We have already converted others irq_chip structures in this driver in
+this way, but we omitted this one because the .name member is
+dynamically created from device's name, and the name is displayed in
+sysfs, so changing it would break sysfs ABI.
+
+The rationale for changing the name (to "advk-INT") in spite of sysfs
+ABI, and thus allowing to convert to a static structure, is that after
+the other changes we made in this series, the IRQ chip is basically
+something different: it no logner generates ERR and PME interrupts (they
+are generated by emulated bridge's rp_irq_chip).
+
+[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
+@@ -274,7 +274,6 @@ struct advk_pcie {
+ u8 wins_count;
+ struct irq_domain *rp_irq_domain;
+ struct irq_domain *irq_domain;
+- struct irq_chip irq_chip;
+ raw_spinlock_t irq_lock;
+ struct irq_domain *msi_domain;
+ struct irq_domain *msi_inner_domain;
+@@ -1330,14 +1329,19 @@ static void advk_pcie_irq_unmask(struct
+ raw_spin_unlock_irqrestore(&pcie->irq_lock, flags);
+ }
+
++static struct irq_chip advk_irq_chip = {
++ .name = "advk-INT",
++ .irq_mask = advk_pcie_irq_mask,
++ .irq_unmask = advk_pcie_irq_unmask,
++};
++
+ 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;
+@@ -1396,7 +1400,6 @@ static int advk_pcie_init_irq_domain(str
+ struct device *dev = &pcie->pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct device_node *pcie_intc_node;
+- struct irq_chip *irq_chip;
+ int ret = 0;
+
+ raw_spin_lock_init(&pcie->irq_lock);
+@@ -1407,28 +1410,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/pkgs/patches-linux-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch b/pkgs/patches-linux-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch
new file mode 100644
index 0000000..4a963be
--- /dev/null
+++ b/pkgs/patches-linux-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch
@@ -0,0 +1,217 @@
+From a719f7ba7fcba05d85801c6f0267f389a21627c1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 24 Sep 2021 13:03:02 +0200
+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Remove port from driver
+ configuration
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Port number is encoded into argument for SMC call. It is zero for SATA,
+PCIe and also both USB 3.0 PHYs. It is non-zero only for Ethernet PHY
+(incorrectly called SGMII) on lane 0. Ethernet PHY on lane 1 also uses zero
+port number.
+
+So construct "port" bits for SMC call argument can be constructed directly
+from PHY type and lane number.
+
+Change driver code to always pass zero port number for non-ethernet PHYs
+and for ethernet PHYs determinate port number from lane number. This
+simplifies the driver.
+
+As port number from DT PHY configuration is not used anymore, remove whole
+driver code which parses it. This also simplifies the driver.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 62 +++++++++-----------
+ 1 file changed, 29 insertions(+), 33 deletions(-)
+
+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+@@ -20,7 +20,6 @@
+ #include <linux/platform_device.h>
+
+ #define MVEBU_A3700_COMPHY_LANES 3
+-#define MVEBU_A3700_COMPHY_PORTS 2
+
+ /* COMPHY Fast SMC function identifiers */
+ #define COMPHY_SIP_POWER_ON 0x82000001
+@@ -45,51 +44,47 @@
+ #define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \
+ ((idx) << 8) | \
+ ((speed) << 2))
+-#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
++#define COMPHY_FW_PCIE(mode, speed, width) (COMPHY_FW_NET(mode, 0, speed) | \
+ ((width) << 18))
+
+ struct mvebu_a3700_comphy_conf {
+ unsigned int lane;
+ enum phy_mode mode;
+ int submode;
+- unsigned int port;
+ u32 fw_mode;
+ };
+
+-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \
++#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _fw) \
+ { \
+ .lane = _lane, \
+ .mode = _mode, \
+ .submode = _smode, \
+- .port = _port, \
+ .fw_mode = _fw, \
+ }
+
+-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
+- MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
++#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _fw) \
++ MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _fw)
+
+-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
+- MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
++#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _fw) \
++ MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _fw)
+
+ static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
+ /* lane 0 */
+- MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
++ MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS,
+ COMPHY_FW_MODE_USB3H),
+- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII,
+ COMPHY_FW_MODE_SGMII),
+- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX,
+ COMPHY_FW_MODE_2500BASEX),
+ /* lane 1 */
+- MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
+- COMPHY_FW_MODE_PCIE),
+- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
++ MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE),
++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII,
+ COMPHY_FW_MODE_SGMII),
+- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX,
+ COMPHY_FW_MODE_2500BASEX),
+ /* lane 2 */
+- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
+- COMPHY_FW_MODE_SATA),
+- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, COMPHY_FW_MODE_SATA),
++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS,
+ COMPHY_FW_MODE_USB3H),
+ };
+
+@@ -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/pkgs/patches-linux-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch b/pkgs/patches-linux-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch
new file mode 100644
index 0000000..73ead1e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch
@@ -0,0 +1,1564 @@
+From 9d276da259cce20b2ed7a868b6e6a6a205f7bb04 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:20:13 +0200
+Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Add native kernel
+ implementation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Remove old RPC implementation and add a new native kernel implementation.
+
+The old implementation uses ARM SMC API to issue RPC calls to ARM Trusted
+Firmware which provides real implementation of PHY configuration.
+
+But older versions of ARM Trusted Firmware do not provide this PHY
+configuration functionality, simply returning: operation not supported; or
+worse, some versions provide the configuration functionality incorrectly.
+
+For example the firmware shipped in ESPRESSObin board has this older
+version of ARM Trusted Firmware and therefore SATA, USB 3.0 and PCIe
+functionality do not work with newer versions of Linux kernel.
+
+Due to the above reasons, the following commits were introduced into Linux,
+to workaround these issues by ignoring -EOPNOTSUPP error code from
+phy-mvebu-a3700-comphy driver function phy_power_on():
+
+commit 45aefe3d2251 ("ata: ahci: mvebu: Make SATA PHY optional for Armada
+3720")
+commit 3241929b67d2 ("usb: host: xhci: mvebu: make USB 3.0 PHY optional for
+Armada 3720")
+commit b0c6ae0f8948 ("PCI: aardvark: Fix initialization with old Marvell's
+Arm Trusted Firmware")
+
+Replace this RPC implementation with proper native kernel implementation,
+which is independent on the firmware. Never return -EOPNOTSUPP for proper
+arguments.
+
+This should solve multiple issues with real-world boards, where it is not
+possible or really inconvenient to change the firmware. Let's eliminate
+these issues.
+
+This implementation is ported directly from Armada 3720 comphy driver found
+in newest version of ARM Trusted Firmware source code, but with various
+fixes of register names, some added comments, some refactoring due to the
+original code not conforming to kernel standards. Also PCIe mode poweroff
+support was added here, and PHY reset support. These changes are also going
+to be sent to ARM Trusted Firmware.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+[ Pali did the porting from ATF.
+ I (Marek) then fixed some register names, some various other things,
+ added some comments and refactored the code to kernel standards. Also
+ fixed PHY poweroff and added PHY reset. ]
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 1351 ++++++++++++++++--
+ 1 file changed, 1234 insertions(+), 117 deletions(-)
+
+--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
+@@ -5,12 +5,16 @@
+ * Authors:
+ * Evan Wang <xswang@marvell.com>
+ * Miquèl Raynal <miquel.raynal@bootlin.com>
++ * Pali Rohár <pali@kernel.org>
++ * Marek Behún <kabel@kernel.org>
+ *
+ * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
+- * SMC call initial support done by Grzegorz Jaszczyk.
++ * Comphy code from ARM Trusted Firmware ported by Pali Rohár <pali@kernel.org>
++ * and Marek Behún <kabel@kernel.org>.
+ */
+
+-#include <linux/arm-smccc.h>
++#include <linux/bitfield.h>
++#include <linux/clk.h>
+ #include <linux/io.h>
+ #include <linux/iopoll.h>
+ #include <linux/mfd/syscon.h>
+@@ -18,103 +22,1147 @@
+ #include <linux/phy.h>
+ #include <linux/phy/phy.h>
+ #include <linux/platform_device.h>
++#include <linux/spinlock.h>
+
+-#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;
+
+- arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
+- ret = res.a0;
++ val = readl(addr);
++ val = (val & ~mask) | (data & mask);
++ writel(val, addr);
++}
+
+- switch (ret) {
+- case SMCCC_RET_SUCCESS:
+- return 0;
+- case SMCCC_RET_NOT_SUPPORTED:
+- return -EOPNOTSUPP;
++static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask)
++{
++ u16 val;
++
++ 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;
++ }
++
++ return 0;
++}
++
++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 step 14 exists (and empty) in the original initialization flow
++ * obtained from the verification team. According to the functional
++ * specification DFE_UPDATE_EN already has the default value 0x3F
++ */
++
++ /*
++ * 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. [Simulation Only] should not be used for real chip.
++ * By pass power up calibration by programming EXT_FORCE_CAL_DONE
++ * (R02h[9]) to 1 to shorten COMPHY simulation time.
++ */
++
++ /*
++ * 15. [Simulation Only: should not be used for real chip]
++ * Program COMPHY register FAST_DFE_TIMER_EN=1 to shorten RX training
++ * simulation time.
++ */
++
++ /*
++ * 16. Check the PHY Polarity invert bit
++ */
++ data = 0x0;
++ if (lane->invert_tx)
++ data |= TXD_INVERT_BIT;
++ if (lane->invert_rx)
++ data |= RXD_INVERT_BIT;
++ mask = TXD_INVERT_BIT | RXD_INVERT_BIT;
++ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask);
++
++ /*
++ * 17. Set PHY input ports PIN_PU_PLL, PIN_PU_TX and PIN_PU_RX to 1 to
++ * start PHY power up sequence. All the PHY register programming should
++ * be done before PIN_PU_PLL=1. There should be no register programming
++ * for normal PHY operation from this point.
++ */
++ data = PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT;
++ mask = data;
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask);
++
++ /*
++ * 18. Wait for PHY power up sequence to finish by checking output ports
++ * PIN_PLL_READY_TX=1 and PIN_PLL_READY_RX=1.
++ */
++ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
++ PHY_PLL_READY_TX_BIT |
++ PHY_PLL_READY_RX_BIT,
++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
++ if (ret) {
++ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
++ lane->id);
++ return ret;
++ }
++
++ /*
++ * 19. Set COMPHY input port PIN_TX_IDLE=0
++ */
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT);
++
++ /*
++ * 20. After valid data appear on PIN_RXDATA bus, set PIN_RX_INIT=1. To
++ * start RX initialization. PIN_RX_INIT_DONE will be cleared to 0 by the
++ * PHY After RX initialization is done, PIN_RX_INIT_DONE will be set to
++ * 1 by COMPHY Set PIN_RX_INIT=0 after PIN_RX_INIT_DONE= 1. Please
++ * refer to RX initialization part for details.
++ */
++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1,
++ PHY_RX_INIT_BIT, PHY_RX_INIT_BIT);
++
++ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1,
++ PHY_PLL_READY_TX_BIT |
++ PHY_PLL_READY_RX_BIT,
++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT);
++ if (ret) {
++ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n",
++ lane->id);
++ return ret;
++ }
++
++ 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;
++ }
++
++ return 0;
+ }
+
+-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;
++ }
++
++ return 0;
++}
++
++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;
++ }
++
++ return 0;
++}
++
++static void
++mvebu_a3700_comphy_usb3_power_off(struct mvebu_a3700_comphy_lane *lane)
++{
++ /*
++ * Currently the USB3 MAC sets the USB3 PHY to low state, so we do not
++ * need to power off USB3 PHY again.
++ */
++}
++
++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 +1170,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 +1180,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 +1214,68 @@ static int mvebu_a3700_comphy_set_mode(s
+ static int mvebu_a3700_comphy_power_on(struct phy *phy)
+ {
+ struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
+- u32 fw_param;
+- int fw_mode;
+- int fw_port;
+ int ret;
+
+- 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:
++ mvebu_a3700_comphy_usb3_power_off(lane);
++ return 0;
++ case PHY_MODE_SATA:
++ mvebu_a3700_comphy_sata_power_off(lane);
++ return 0;
++ case PHY_MODE_ETHERNET:
++ mvebu_a3700_comphy_ethernet_power_off(lane);
++ return 0;
++ case PHY_MODE_PCIE:
++ mvebu_a3700_comphy_pcie_power_off(lane);
++ return 0;
++ default:
++ dev_err(lane->dev, "invalid COMPHY mode\n");
++ return -EINVAL;
++ }
+ }
+
+ static const struct phy_ops mvebu_a3700_comphy_ops = {
+ .power_on = mvebu_a3700_comphy_power_on,
+ .power_off = mvebu_a3700_comphy_power_off,
++ .reset = mvebu_a3700_comphy_reset,
+ .set_mode = mvebu_a3700_comphy_set_mode,
+ .owner = THIS_MODULE,
+ };
+@@ -256,13 +1299,75 @@ static struct phy *mvebu_a3700_comphy_xl
+ return ERR_PTR(-EINVAL);
+ }
+
++ 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 +1382,7 @@ static int mvebu_a3700_comphy_probe(stru
+ continue;
+ }
+
+- if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
++ if (lane_id >= 3) {
+ dev_err(&pdev->dev, "invalid 'reg' property\n");
+ continue;
+ }
+@@ -295,11 +1400,21 @@ static int mvebu_a3700_comphy_probe(stru
+ return PTR_ERR(phy);
+ }
+
++ lane->priv = priv;
+ lane->dev = &pdev->dev;
+ lane->mode = PHY_MODE_INVALID;
+ lane->submode = PHY_INTERFACE_MODE_NA;
+ lane->id = lane_id;
++ lane->invert_tx = false;
++ lane->invert_rx = false;
+ phy_set_drvdata(phy, lane);
++
++ /*
++ * To avoid relying on the bootloader/firmware configuration,
++ * power off all comphys.
++ */
++ mvebu_a3700_comphy_reset(phy);
++ lane->needs_reset = false;
+ }
+
+ provider = devm_of_phy_provider_register(&pdev->dev,
+@@ -323,5 +1438,7 @@ static struct platform_driver mvebu_a370
+ module_platform_driver(mvebu_a3700_comphy_driver);
+
+ 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/pkgs/patches-linux-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch b/pkgs/patches-linux-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch
new file mode 100644
index 0000000..33203a1
--- /dev/null
+++ b/pkgs/patches-linux-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch
@@ -0,0 +1,31 @@
+From 66c51c39fd4bf05e99debf0e71de5704231c57dc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:26:26 +0200
+Subject: [PATCH] arm64: dts: marvell: armada-37xx: Add xtal clock to comphy
+ node
+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>
+---
+ 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/pkgs/patches-linux-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch b/pkgs/patches-linux-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch
new file mode 100644
index 0000000..3c994d2
--- /dev/null
+++ b/pkgs/patches-linux-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch
@@ -0,0 +1,61 @@
+From 750bb44dbbe9dfb4ba3e1f8a746b831b39ba3cd9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:35:57 +0200
+Subject: [PATCH] Revert "ata: ahci: mvebu: Make SATA PHY optional for Armada
+ 3720"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit 45aefe3d2251e4e229d7662052739f96ad1d08d9.
+
+Armada 3720 PHY driver (phy-mvebu-a3700-comphy.c) does not return
+-EOPNOTSUPP from phy_power_on() callback anymore.
+
+So remove AHCI_HFLAG_IGN_NOTSUPP_POWER_ON flag from Armada 3720 plat data.
+
+AHCI_HFLAG_IGN_NOTSUPP_POWER_ON is not used by any other ahci driver, so
+remove this flag completely.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/ata/ahci.h | 2 --
+ drivers/ata/ahci_mvebu.c | 2 +-
+ drivers/ata/libahci_platform.c | 2 +-
+ 3 files changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/ata/ahci.h
++++ b/drivers/ata/ahci.h
+@@ -240,8 +240,6 @@ enum {
+ as default lpm_policy */
+ AHCI_HFLAG_SUSPEND_PHYS = (1 << 26), /* handle PHYs during
+ suspend/resume */
+- AHCI_HFLAG_IGN_NOTSUPP_POWER_ON = (1 << 27), /* ignore -EOPNOTSUPP
+- from phy_power_on() */
+ AHCI_HFLAG_NO_SXS = (1 << 28), /* SXS not supported */
+
+ /* ap->flags bits */
+--- a/drivers/ata/ahci_mvebu.c
++++ b/drivers/ata/ahci_mvebu.c
+@@ -227,7 +227,7 @@ static const struct ahci_mvebu_plat_data
+
+ static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = {
+ .plat_config = ahci_mvebu_armada_3700_config,
+- .flags = AHCI_HFLAG_SUSPEND_PHYS | AHCI_HFLAG_IGN_NOTSUPP_POWER_ON,
++ .flags = AHCI_HFLAG_SUSPEND_PHYS,
+ };
+
+ static const struct of_device_id ahci_mvebu_of_match[] = {
+--- a/drivers/ata/libahci_platform.c
++++ b/drivers/ata/libahci_platform.c
+@@ -59,7 +59,7 @@ int ahci_platform_enable_phys(struct ahc
+ }
+
+ 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/pkgs/patches-linux-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch b/pkgs/patches-linux-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch
new file mode 100644
index 0000000..b8a3e88
--- /dev/null
+++ b/pkgs/patches-linux-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch
@@ -0,0 +1,163 @@
+From 9f0dfb279b1dd505d5e10b10e4a78a62030978d8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:40:06 +0200
+Subject: [PATCH] Revert "usb: host: xhci: mvebu: make USB 3.0 PHY optional for
+ Armada 3720"
+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>
+---
+ 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/pkgs/patches-linux-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch b/pkgs/patches-linux-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch
new file mode 100644
index 0000000..4425e3f
--- /dev/null
+++ b/pkgs/patches-linux-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch
@@ -0,0 +1,36 @@
+From 9a352062b7e3857742389dff6f64393481dc755e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 23 Sep 2021 19:37:05 +0200
+Subject: [PATCH] Revert "PCI: aardvark: Fix initialization with old Marvell's
+ Arm Trusted Firmware"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit b0c6ae0f8948a2be6bf4e8b4bbab9ca1343289b6.
+
+Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return
+-EOPNOTSUPP from phy_power_on() callback anymore.
+
+So remove dead code which handles -EOPNOTSUPP return value.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/pci/controller/pci-aardvark.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1616,9 +1616,7 @@ static int advk_pcie_enable_phy(struct a
+ }
+
+ 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/pkgs/patches-linux-5.15/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch b/pkgs/patches-linux-5.15/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch
new file mode 100644
index 0000000..e9d692b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch b/pkgs/patches-linux-5.15/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch
new file mode 100644
index 0000000..fabf177
--- /dev/null
+++ b/pkgs/patches-linux-5.15/870-hwmon-next-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/pkgs/patches-linux-5.15/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch b/pkgs/patches-linux-5.15/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch
new file mode 100644
index 0000000..39fdb32
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/8800-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch b/pkgs/patches-linux-5.15/8800-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch
new file mode 100644
index 0000000..3c427cd
--- /dev/null
+++ b/pkgs/patches-linux-5.15/8800-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch
@@ -0,0 +1,213 @@
+From 848dfda2610dba5c051c9114ab05449e64d5de08 Mon Sep 17 00:00:00 2001
+From: Tomas Hlavacek <tmshlvck@gmail.com>
+Date: Tue, 5 May 2020 20:40:24 +0200
+Subject: [PATCH] ARM: dts: mvebu: armada-385-turris-omnia: separate dts for
+ SFP and PHY
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The Turris Omnia board contains dual-personality ethernet NIC eth2 with
+two operation modes: 1) SFP cage and 2) metalic 1000BASE-X PHY.
+The differential pair carrying SGMII/1000BASE-X of eth2 is wired through
+a multiplexor driven by the module-detect signal from the SFP cage.
+The pin status can be read through I2C GPIO expander chip in userspace
+when the sfp driver module is unloaded and / or in U-Boot prior to the
+start of the kernel and the proper DTS file can be selected for the
+(floolowing) boot.
+
+Split DTS for Turris Omnia (that does not have any support for SFP cage)
+into three files:
+ armada-385-turris-omnia.dtsi - common base
+ armada-385-turris-omnia-sfp.dts - DT with the SFP configuration and
+PHY disabled
+ armada-385-turris-omnia-phy.dts - DT with the PHY configuration and
+SFP disabled
+
+Current DSA driver does not allow multiple CPU ports and Turris Omnia
+has two RGMII iterfaces wired between CPU and DSA switch.
+Disable the second CPU port until there is a suitable driver to use it.
+
+Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>
+Signed-off-by: Marek Behún <marek.behun@nic.cz>
+---
+ arch/arm/boot/dts/Makefile | 3 +-
+ .../boot/dts/armada-385-turris-omnia-phy.dts | 22 ++++++++++++
+ .../boot/dts/armada-385-turris-omnia-sfp.dts | 23 ++++++++++++
+ ...omnia.dts => armada-385-turris-omnia.dtsi} | 35 +++++++++++--------
+ 4 files changed, 68 insertions(+), 15 deletions(-)
+ create mode 100644 arch/arm/boot/dts/armada-385-turris-omnia-phy.dts
+ create mode 100644 arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts
+ rename arch/arm/boot/dts/{armada-385-turris-omnia.dts => armada-385-turris-omnia.dtsi} (94%)
+
+diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
+index ce66ffd5a1bb..e702896345e9 100644
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -1328,7 +1328,8 @@ dtb-$(CONFIG_MACH_ARMADA_38X) += \
+ armada-385-linksys-rango.dtb \
+ armada-385-linksys-shelby.dtb \
+ armada-385-synology-ds116.dtb \
+- armada-385-turris-omnia.dtb \
++ armada-385-turris-omnia-phy.dtb \
++ armada-385-turris-omnia-sfp.dtb \
+ armada-388-clearfog.dtb \
+ armada-388-clearfog-base.dtb \
+ armada-388-clearfog-pro.dtb \
+diff --git a/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts b/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts
+new file mode 100644
+index 000000000000..706f6a2f8065
+--- /dev/null
++++ b/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts
+@@ -0,0 +1,22 @@
++// SPDX-License-Identifier: (GPL-2.0 OR MIT)
++/*
++ * Device Tree file for the Turris Omnia
++ *
++ * Copyright (C) 2016 Uwe Kleine-König <uwe@kleine-koenig.org>
++ * Copyright (C) 2016-2019 Tomas Hlavacek <tmshlvkc@gmail.com>
++ *
++ * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf
++ */
++
++/dts-v1/;
++
++#include "armada-385-turris-omnia.dtsi"
++
++&phy1 {
++ status = "okay";
++};
++
++&eth2 {
++ phy-mode = "sgmii";
++ phy = <&phy1>;
++};
+diff --git a/arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts b/arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts
+new file mode 100644
+index 000000000000..b9f2b88834be
+--- /dev/null
++++ b/arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts
+@@ -0,0 +1,23 @@
++// SPDX-License-Identifier: (GPL-2.0 OR MIT)
++/*
++ * Device Tree file for the Turris Omnia
++ *
++ * Copyright (C) 2016 Uwe Kleine-König <uwe@kleine-koenig.org>
++ * Copyright (C) 2016-2019 Tomas Hlavacek <tmshlvkc@gmail.com>
++ *
++ * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf
++ */
++
++/dts-v1/;
++
++#include "armada-385-turris-omnia.dtsi"
++
++&sfp {
++ status = "okay";
++};
++
++&eth2 {
++ phy-mode = "sgmii";
++ managed = "in-band-status";
++ sfp = <&sfp>;
++};
+diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dtsi
+similarity index 94%
+rename from arch/arm/boot/dts/armada-385-turris-omnia.dts
+rename to arch/arm/boot/dts/armada-385-turris-omnia.dtsi
+index 5bd6a66d2c2b..6bf7d3e184ab 100644
+--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts
++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dtsi
+@@ -8,8 +8,6 @@
+ * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf
+ */
+
+-/dts-v1/;
+-
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/input/input.h>
+ #include <dt-bindings/leds/common.h>
+@@ -88,11 +86,11 @@ pcie@3,0 {
+ sfp: sfp {
+ compatible = "sff,sfp";
+ i2c-bus = <&sfp_i2c>;
+- tx-fault-gpios = <&pcawan 0 GPIO_ACTIVE_HIGH>;
+- tx-disable-gpios = <&pcawan 1 GPIO_ACTIVE_HIGH>;
+- rate-select0-gpios = <&pcawan 2 GPIO_ACTIVE_HIGH>;
+- los-gpios = <&pcawan 3 GPIO_ACTIVE_HIGH>;
+- mod-def0-gpios = <&pcawan 4 GPIO_ACTIVE_LOW>;
++ tx-fault-gpios = <&sfpgpio 0 GPIO_ACTIVE_HIGH>;
++ tx-disable-gpios = <&sfpgpio 1 GPIO_ACTIVE_HIGH>;
++ rate-select0-gpios = <&sfpgpio 2 GPIO_ACTIVE_HIGH>;
++ los-gpios = <&sfpgpio 3 GPIO_ACTIVE_HIGH>;
++ mod-def0-gpios = <&sfpgpio 4 GPIO_ACTIVE_LOW>;
+ maximum-power-milliwatt = <3000>;
+
+ /*
+@@ -144,7 +142,7 @@ fixed-link {
+ };
+ };
+
+-/* WAN port */
++/* WAN dual-personality port */
+ &eth2 {
+ /*
+ * eth2 is connected via a multiplexor to both the SFP cage and to
+@@ -155,9 +153,8 @@ &eth2 {
+ * is present, U-Boot has to enable the sfp node above, remove phy
+ * handle and add managed = "in-band-status" property.
+ */
++ phys = <&comphy5 2>;
+ status = "okay";
+- phy-mode = "sgmii";
+- phy-handle = <&phy1>;
+ phys = <&comphy5 2>;
+ sfp = <&sfp>;
+ buffer-manager = <&bm>;
+@@ -361,7 +358,7 @@ i2c@7 {
+ #size-cells = <0>;
+ reg = <7>;
+
+- pcawan: gpio@71 {
++ sfpgpio: gpio@71 {
+ /*
+ * GPIO expander for SFP+ signals and
+ * and phy irq
+@@ -370,7 +367,7 @@ pcawan: gpio@71 {
+ reg = <0x71>;
+
+ pinctrl-names = "default";
+- pinctrl-0 = <&pcawan_pins>;
++ pinctrl-0 = <&wanint_pins>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <14 IRQ_TYPE_LEVEL_LOW>;
+@@ -450,13 +447,23 @@ fixed-link {
+ };
+ };
+
+- /* port 6 is connected to eth0 */
++ ports@6 {
++ reg = <6>;
++ label = "cpu";
++ ethernet = <&eth0>;
++ phy-mode = "rgmii-id";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
+ };
+ };
+ };
+
+ &pinctrl {
+- pcawan_pins: pcawan-pins {
++ wanint_pins: wanint-pins {
+ marvell,pins = "mpp46";
+ marvell,function = "gpio";
+ };
+--
+2.30.2
+
diff --git a/pkgs/patches-linux-5.15/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch b/pkgs/patches-linux-5.15/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch
new file mode 100644
index 0000000..7da2280
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -485,10 +485,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;
+@@ -523,10 +527,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);
+@@ -600,6 +611,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 [] = {
+@@ -614,6 +636,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/pkgs/patches-linux-5.15/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch b/pkgs/patches-linux-5.15/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch
new file mode 100644
index 0000000..ebe7a43
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -418,10 +418,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);
+@@ -463,6 +460,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;
+@@ -615,7 +622,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/pkgs/patches-linux-5.15/900-v5.20-powerpc-add-Turris-1x-dts.patch b/pkgs/patches-linux-5.15/900-v5.20-powerpc-add-Turris-1x-dts.patch
new file mode 100644
index 0000000..b87db86
--- /dev/null
+++ b/pkgs/patches-linux-5.15/900-v5.20-powerpc-add-Turris-1x-dts.patch
@@ -0,0 +1,508 @@
+From 54c15ec3b738c6086f2be001dae962ec412640e5 Mon Sep 17 00:00:00 2001
+From: Pali Rohár <pali@kernel.org>
+Date: Fri, 24 Jun 2022 10:55:50 +0200
+Subject: powerpc: dts: Add DTS file for CZ.NIC Turris 1.x routers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+CZ.NIC Turris 1.0 and 1.1 are open source routers, they have dual-core
+PowerPC Freescale P2020 CPU and are based on Freescale P2020RDB-PC-A board.
+Hardware design is fully open source, all firmware and hardware design
+files are available at Turris project website:
+
+https://docs.turris.cz/hw/turris-1x/turris-1x/
+https://project.turris.cz/en/hardware.html
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
+Link: https://lore.kernel.org/r/20220624085550.20570-1-pali@kernel.org
+---
+ arch/powerpc/boot/dts/turris1x.dts | 475 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 475 insertions(+)
+ create mode 100644 arch/powerpc/boot/dts/turris1x.dts
+
+diff --git a/arch/powerpc/boot/dts/turris1x.dts b/arch/powerpc/boot/dts/turris1x.dts
+new file mode 100644
+index 0000000000000..c76b628cf0262
+--- /dev/null
++++ b/arch/powerpc/boot/dts/turris1x.dts
+@@ -0,0 +1,475 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Turris 1.x Device Tree Source
++ *
++ * Copyright 2013 - 2022 CZ.NIC z.s.p.o. (http://www.nic.cz/)
++ *
++ * Pinout, Schematics and Altium hardware design files are open source
++ * and available at: https://docs.turris.cz/hw/turris-1x/turris-1x/
++ */
++
++#include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/leds/common.h>
++/include/ "fsl/p2020si-pre.dtsi"
++
++/ {
++ model = "Turris 1.x";
++ compatible = "cznic,turris1x", "fsl,P2020RDB-PC"; /* fsl,P2020RDB-PC is required for booting Linux */
++
++ aliases {
++ ethernet0 = &enet0;
++ ethernet1 = &enet1;
++ ethernet2 = &enet2;
++ serial0 = &serial0;
++ serial1 = &serial1;
++ pci0 = &pci0;
++ pci1 = &pci1;
++ pci2 = &pci2;
++ spi0 = &spi0;
++ };
++
++ memory {
++ device_type = "memory";
++ };
++
++ soc: soc@ffe00000 {
++ ranges = <0x0 0x0 0xffe00000 0x00100000>;
++
++ i2c@3000 {
++ /* PCA9557PW GPIO controller for boot config */
++ gpio-controller@18 {
++ compatible = "nxp,pca9557";
++ label = "bootcfg";
++ reg = <0x18>;
++ #gpio-cells = <2>;
++ gpio-controller;
++ polarity = <0x00>;
++ };
++
++ /* STM32F030R8T6 MCU for power control */
++ power-control@2a {
++ /*
++ * Turris Power Control firmware runs on STM32F0 MCU.
++ * This firmware is open source and available at:
++ * https://gitlab.nic.cz/turris/hw/turris_power_control
++ */
++ reg = <0x2a>;
++ };
++
++ /* DDR3 SPD/EEPROM PSWP instruction */
++ eeprom@32 {
++ reg = <0x32>;
++ };
++
++ /* SA56004ED temperature control */
++ temperature-sensor@4c {
++ compatible = "nxp,sa56004";
++ reg = <0x4c>;
++ interrupt-parent = <&gpio>;
++ interrupts = <12 IRQ_TYPE_LEVEL_LOW>, /* GPIO12 - ALERT pin */
++ <13 IRQ_TYPE_LEVEL_LOW>; /* GPIO13 - CRIT pin */
++ };
++
++ /* DDR3 SPD/EEPROM */
++ eeprom@52 {
++ compatible = "atmel,spd";
++ reg = <0x52>;
++ };
++
++ /* MCP79402-I/ST Protected EEPROM */
++ eeprom@57 {
++ reg = <0x57>;
++ };
++
++ /* ATSHA204-TH-DA-T crypto module */
++ crypto@64 {
++ compatible = "atmel,atsha204";
++ reg = <0x64>;
++ };
++
++ /* IDT6V49205BNLGI clock generator */
++ clock-generator@69 {
++ compatible = "idt,6v49205b";
++ reg = <0x69>;
++ };
++
++ /* MCP79402-I/ST RTC */
++ rtc@6f {
++ compatible = "microchip,mcp7940x";
++ reg = <0x6f>;
++ interrupt-parent = <&gpio>;
++ interrupts = <14 0>; /* GPIO14 - MFP pin */
++ };
++ };
++
++ /* SPI on connector P1 */
++ spi0: spi@7000 {
++ };
++
++ gpio: gpio-controller@fc00 {
++ #interrupt-cells = <2>;
++ interrupt-controller;
++ };
++
++ /* Connected to SMSC USB2412-DZK 2-Port USB 2.0 Hub Controller */
++ usb@22000 {
++ phy_type = "ulpi";
++ dr_mode = "host";
++ };
++
++ enet0: ethernet@24000 {
++ /* Connected to port 6 of QCA8337N-AL3C switch */
++ phy-connection-type = "rgmii-id";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ mdio@24520 {
++ /* KSZ9031RNXCA ethernet phy for WAN port */
++ phy: ethernet-phy@7 {
++ interrupts = <3 1 0 0>;
++ reg = <0x7>;
++ };
++
++ /* QCA8337N-AL3C switch with integrated ethernet PHYs for LAN ports */
++ switch@10 {
++ compatible = "qca,qca8337";
++ interrupts = <2 1 0 0>;
++ reg = <0x10>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ port@0 {
++ reg = <0>;
++ label = "cpu1";
++ ethernet = <&enet1>;
++ phy-mode = "rgmii-id";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ port@1 {
++ reg = <1>;
++ label = "lan5";
++ };
++
++ port@2 {
++ reg = <2>;
++ label = "lan4";
++ };
++
++ port@3 {
++ reg = <3>;
++ label = "lan3";
++ };
++
++ port@4 {
++ reg = <4>;
++ label = "lan2";
++ };
++
++ port@5 {
++ reg = <5>;
++ label = "lan1";
++ };
++
++ port@6 {
++ reg = <6>;
++ label = "cpu0";
++ ethernet = <&enet0>;
++ phy-mode = "rgmii-id";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++ };
++ };
++ };
++
++ ptp_clock@24e00 {
++ fsl,tclk-period = <5>;
++ fsl,tmr-prsc = <200>;
++ fsl,tmr-add = <0xcccccccd>;
++ fsl,tmr-fiper1 = <0x3b9ac9fb>;
++ fsl,tmr-fiper2 = <0x0001869b>;
++ fsl,max-adj = <249999999>;
++ };
++
++ enet1: ethernet@25000 {
++ /* Connected to port 0 of QCA8337N-AL3C switch */
++ phy-connection-type = "rgmii-id";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++
++ mdio@25520 {
++ status = "disabled";
++ };
++
++ enet2: ethernet@26000 {
++ /* Connected to KSZ9031RNXCA ethernet phy (WAN port) */
++ label = "wan";
++ phy-handle = <&phy>;
++ phy-connection-type = "rgmii-id";
++ };
++
++ mdio@26520 {
++ status = "disabled";
++ };
++
++ sdhc@2e000 {
++ bus-width = <4>;
++ cd-gpios = <&gpio 8 GPIO_ACTIVE_LOW>;
++ };
++ };
++
++ lbc: localbus@ffe05000 {
++ reg = <0 0xffe05000 0 0x1000>;
++
++ ranges = <0x0 0x0 0x0 0xef000000 0x01000000>, /* NOR */
++ <0x1 0x0 0x0 0xff800000 0x00040000>, /* NAND */
++ <0x3 0x0 0x0 0xffa00000 0x00020000>; /* CPLD */
++
++ /* S29GL128P90TFIR10 NOR */
++ nor@0,0 {
++ compatible = "cfi-flash";
++ reg = <0x0 0x0 0x01000000>;
++ bank-width = <2>;
++ device-width = <1>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ /* 128 kB for Device Tree Blob */
++ reg = <0x00000000 0x00020000>;
++ label = "dtb";
++ };
++
++ partition@20000 {
++ /* 1.7 MB for Rescue Linux Kernel Image */
++ reg = <0x00020000 0x001a0000>;
++ label = "rescue-kernel";
++ };
++
++ partition@1c0000 {
++ /* 1.5 MB for Rescue JFFS2 Root File System */
++ reg = <0x001c0000 0x00180000>;
++ label = "rescue-rootfs";
++ };
++
++ partition@340000 {
++ /* 11 MB for TAR.XZ Backup with content of NAND Root File System */
++ reg = <0x00340000 0x00b00000>;
++ label = "backup-rootfs";
++ };
++
++ partition@e40000 {
++ /* 768 kB for Certificates JFFS2 File System */
++ reg = <0x00e40000 0x000c0000>;
++ label = "certificates";
++ };
++
++ /* free unused space 0x00f00000-0x00f20000 */
++
++ partition@f20000 {
++ /* 128 kB for U-Boot Environment Variables */
++ reg = <0x00f20000 0x00020000>;
++ label = "u-boot-env";
++ };
++
++ partition@f40000 {
++ /* 768 kB for U-Boot Bootloader Image */
++ reg = <0x00f40000 0x000c0000>;
++ label = "u-boot";
++ };
++ };
++ };
++
++ /* MT29F2G08ABAEAWP:E NAND */
++ nand@1,0 {
++ compatible = "fsl,p2020-fcm-nand", "fsl,elbc-fcm-nand";
++ reg = <0x1 0x0 0x00040000>;
++ nand-ecc-mode = "soft";
++ nand-ecc-algo = "bch";
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ /* 256 MB for UBI with one volume: UBIFS Root File System */
++ reg = <0x00000000 0x10000000>;
++ label = "rootfs";
++ };
++ };
++ };
++
++ /* LCMXO1200C-3FTN256C FPGA */
++ cpld@3,0 {
++ /*
++ * Turris CPLD firmware which runs on this Lattice FPGA,
++ * is extended version of P1021RDB-PC CPLD v4.1 firmware.
++ * It is backward compatible with its original version
++ * and the only extension is support for Turris LEDs.
++ * Turris CPLD firmware is open source and available at:
++ * https://gitlab.nic.cz/turris/hw/turris_cpld/-/blob/master/CZ_NIC_Router_CPLD.v
++ */
++ compatible = "cznic,turris1x-cpld", "fsl,p1021rdb-pc-cpld", "simple-bus";
++ reg = <0x3 0x0 0x30>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0x0 0x3 0x0 0x00020000>;
++
++ /* MAX6370KA+T watchdog */
++ watchdog@2 {
++ /*
++ * CPLD firmware maps SET0, SET1 and SET2
++ * input logic of MAX6370KA+T chip to CPLD
++ * memory space at byte offset 0x2. WDI
++ * input logic is outside of the CPLD and
++ * connected via external GPIO.
++ */
++ compatible = "maxim,max6370";
++ reg = <0x02 0x01>;
++ gpios = <&gpio 11 GPIO_ACTIVE_LOW>;
++ };
++
++ led-controller@13 {
++ /*
++ * LEDs are controlled by CPLD firmware.
++ * All five LAN LEDs share common RGB settings
++ * and so it is not possible to set different
++ * colors on different LAN ports.
++ */
++ compatible = "cznic,turris1x-leds";
++ reg = <0x13 0x1d>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ multi-led@0 {
++ reg = <0x0>;
++ color = <LED_COLOR_ID_RGB>;
++ function = LED_FUNCTION_WAN;
++ };
++
++ multi-led@1 {
++ reg = <0x1>;
++ color = <LED_COLOR_ID_RGB>;
++ function = LED_FUNCTION_LAN;
++ function-enumerator = <5>;
++ };
++
++ multi-led@2 {
++ reg = <0x2>;
++ color = <LED_COLOR_ID_RGB>;
++ function = LED_FUNCTION_LAN;
++ function-enumerator = <4>;
++ };
++
++ multi-led@3 {
++ reg = <0x3>;
++ color = <LED_COLOR_ID_RGB>;
++ function = LED_FUNCTION_LAN;
++ function-enumerator = <3>;
++ };
++
++ multi-led@4 {
++ reg = <0x4>;
++ color = <LED_COLOR_ID_RGB>;
++ function = LED_FUNCTION_LAN;
++ function-enumerator = <2>;
++ };
++
++ multi-led@5 {
++ reg = <0x5>;
++ color = <LED_COLOR_ID_RGB>;
++ function = LED_FUNCTION_LAN;
++ function-enumerator = <1>;
++ };
++
++ multi-led@6 {
++ reg = <0x6>;
++ color = <LED_COLOR_ID_RGB>;
++ function = LED_FUNCTION_WLAN;
++ };
++
++ multi-led@7 {
++ reg = <0x7>;
++ color = <LED_COLOR_ID_RGB>;
++ function = LED_FUNCTION_POWER;
++ };
++ };
++ };
++ };
++
++ pci2: pcie@ffe08000 {
++ /*
++ * PCIe bus for on-board TUSB7340RKM USB 3.0 xHCI controller.
++ * This xHCI controller is available only on Turris 1.1 boards.
++ * Turris 1.0 boards have nothing connected to this PCIe bus,
++ * so system would see only PCIe Root Port of this PCIe Root
++ * Complex. TUSB7340RKM xHCI controller has four SuperSpeed
++ * channels. Channel 0 is connected to the front USB 3.0 port,
++ * channel 1 (but only USB 2.0 subset) to USB 2.0 pins on mPCIe
++ * slot 1 (CN5), channels 2 and 3 to connector P600.
++ *
++ * P2020 PCIe Root Port uses 1MB of PCIe MEM and xHCI controller
++ * uses 64kB + 8kB of PCIe MEM. No PCIe IO is used or required.
++ * So allocate 2MB of PCIe MEM for this PCIe bus.
++ */
++ reg = <0 0xffe08000 0 0x1000>;
++ ranges = <0x02000000 0x0 0xc0000000 0 0xc0000000 0x0 0x00200000>, /* MEM */
++ <0x01000000 0x0 0x00000000 0 0xffc20000 0x0 0x00010000>; /* IO */
++
++ pcie@0 {
++ ranges;
++ };
++ };
++
++ pci1: pcie@ffe09000 {
++ /* PCIe bus on mPCIe slot 2 (CN6) for expansion mPCIe card */
++ reg = <0 0xffe09000 0 0x1000>;
++ ranges = <0x02000000 0x0 0xa0000000 0 0xa0000000 0x0 0x20000000>, /* MEM */
++ <0x01000000 0x0 0x00000000 0 0xffc10000 0x0 0x00010000>; /* IO */
++
++ pcie@0 {
++ ranges;
++ };
++ };
++
++ pci0: pcie@ffe0a000 {
++ /*
++ * PCIe bus on mPCIe slot 1 (CN5) for expansion mPCIe card.
++ * Turris 1.1 boards have in this mPCIe slot additional USB 2.0
++ * pins via channel 1 of TUSB7340RKM xHCI controller and also
++ * additional SIM card slot, both for USB-based WWAN cards.
++ */
++ reg = <0 0xffe0a000 0 0x1000>;
++ ranges = <0x02000000 0x0 0x80000000 0 0x80000000 0x0 0x20000000>, /* MEM */
++ <0x01000000 0x0 0x00000000 0 0xffc00000 0x0 0x00010000>; /* IO */
++
++ pcie@0 {
++ ranges;
++ };
++ };
++};
++
++/include/ "fsl/p2020si-post.dtsi"
+--
+cgit
+
diff --git a/pkgs/patches-linux-5.15/901-debloat_sock_diag.patch b/pkgs/patches-linux-5.15/901-debloat_sock_diag.patch
new file mode 100644
index 0000000..ab629d5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -414,6 +414,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/pkgs/patches-linux-5.15/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch b/pkgs/patches-linux-5.15/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch
new file mode 100644
index 0000000..3446086
--- /dev/null
+++ b/pkgs/patches-linux-5.15/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch
@@ -0,0 +1,218 @@
+From aa4a0ccc41997f2da172165c92803abace43bd1c Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:32 +0000
+Subject: [PATCH 1/7] dt-bindings: Add IEI vendor prefix and IEI WT61P803
+ PUZZLE driver bindings
+
+Add the IEI WT61P803 PUZZLE Device Tree bindings for MFD, HWMON and LED
+drivers. A new vendor prefix is also added accordingly for
+IEI Integration Corp.
+
+Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
+Cc: Luka Perkov <luka.perkov@sartura.hr>
+Cc: Robert Marko <robert.marko@sartura.hr>
+---
+ .../hwmon/iei,wt61p803-puzzle-hwmon.yaml | 53 ++++++++++++
+ .../leds/iei,wt61p803-puzzle-leds.yaml | 39 +++++++++
+ .../bindings/mfd/iei,wt61p803-puzzle.yaml | 82 +++++++++++++++++++
+ .../devicetree/bindings/vendor-prefixes.yaml | 2 +
+ 4 files changed, 176 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml
+ create mode 100644 Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml
+ create mode 100644 Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml
+@@ -0,0 +1,53 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/hwmon/iei,wt61p803-puzzle-hwmon.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: IEI WT61P803 PUZZLE MCU HWMON module from IEI Integration Corp.
++
++maintainers:
++ - Luka Kovacic <luka.kovacic@sartura.hr>
++
++description: |
++ This module is a part of the IEI WT61P803 PUZZLE MFD device. For more details
++ see Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml.
++
++ The HWMON module is a sub-node of the MCU node in the Device Tree.
++
++properties:
++ compatible:
++ const: iei,wt61p803-puzzle-hwmon
++
++ "#address-cells":
++ const: 1
++
++ "#size-cells":
++ const: 0
++
++patternProperties:
++ "^fan-group@[0-1]$":
++ type: object
++ properties:
++ reg:
++ minimum: 0
++ maximum: 1
++ description:
++ Fan group ID
++
++ cooling-levels:
++ minItems: 1
++ maxItems: 255
++ description:
++ Cooling levels for the fans (PWM value mapping)
++ description: |
++ Properties for each fan group.
++ required:
++ - reg
++
++required:
++ - compatible
++ - "#address-cells"
++ - "#size-cells"
++
++additionalProperties: false
+--- /dev/null
++++ b/Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml
+@@ -0,0 +1,39 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/leds/iei,wt61p803-puzzle-leds.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: IEI WT61P803 PUZZLE MCU LED module from IEI Integration Corp.
++
++maintainers:
++ - Luka Kovacic <luka.kovacic@sartura.hr>
++
++description: |
++ This module is a part of the IEI WT61P803 PUZZLE MFD device. For more details
++ see Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml.
++
++ The LED module is a sub-node of the MCU node in the Device Tree.
++
++properties:
++ compatible:
++ const: iei,wt61p803-puzzle-leds
++
++ "#address-cells":
++ const: 1
++
++ "#size-cells":
++ const: 0
++
++ led@0:
++ type: object
++ $ref: common.yaml
++ description: |
++ Properties for a single LED.
++
++required:
++ - compatible
++ - "#address-cells"
++ - "#size-cells"
++
++additionalProperties: false
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml
+@@ -0,0 +1,82 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mfd/iei,wt61p803-puzzle.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: IEI WT61P803 PUZZLE MCU from IEI Integration Corp.
++
++maintainers:
++ - Luka Kovacic <luka.kovacic@sartura.hr>
++
++description: |
++ IEI WT61P803 PUZZLE MCU is embedded in some IEI Puzzle series boards.
++ It's used for controlling system power states, fans, LEDs and temperature
++ sensors.
++
++ For Device Tree bindings of other sub-modules (HWMON, LEDs) refer to the
++ binding documents under the respective subsystem directories.
++
++properties:
++ compatible:
++ const: iei,wt61p803-puzzle
++
++ current-speed:
++ description:
++ Serial bus speed in bps
++ maxItems: 1
++
++ enable-beep: true
++
++ hwmon:
++ $ref: /schemas/hwmon/iei,wt61p803-puzzle-hwmon.yaml
++
++ leds:
++ $ref: /schemas/leds/iei,wt61p803-puzzle-leds.yaml
++
++required:
++ - compatible
++ - current-speed
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/leds/common.h>
++ serial {
++ mcu {
++ compatible = "iei,wt61p803-puzzle";
++ current-speed = <115200>;
++ enable-beep;
++
++ leds {
++ compatible = "iei,wt61p803-puzzle-leds";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ led@0 {
++ reg = <0>;
++ function = LED_FUNCTION_POWER;
++ color = <LED_COLOR_ID_BLUE>;
++ };
++ };
++
++ hwmon {
++ compatible = "iei,wt61p803-puzzle-hwmon";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ fan-group@0 {
++ #cooling-cells = <2>;
++ reg = <0x00>;
++ cooling-levels = <64 102 170 230 250>;
++ };
++
++ fan-group@1 {
++ #cooling-cells = <2>;
++ reg = <0x01>;
++ cooling-levels = <64 102 170 230 250>;
++ };
++ };
++ };
++ };
+--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
++++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
+@@ -519,6 +519,8 @@ patternProperties:
+ description: IC Plus Corp.
+ "^idt,.*":
+ description: Integrated Device Technologies, Inc.
++ "^iei,.*":
++ description: IEI Integration Corp.
+ "^ifi,.*":
+ description: Ingenieurburo Fur Ic-Technologie (I/F/I)
+ "^ilitek,.*":
diff --git a/pkgs/patches-linux-5.15/902-debloat_proc.patch b/pkgs/patches-linux-5.15/902-debloat_proc.patch
new file mode 100644
index 0000000..c58370e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -2929,6 +2929,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
+@@ -3962,6 +3962,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
+@@ -3857,6 +3857,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
+@@ -3022,11 +3022,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;
+
+@@ -3037,17 +3039,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/pkgs/patches-linux-5.15/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch b/pkgs/patches-linux-5.15/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch
new file mode 100644
index 0000000..84d995b
--- /dev/null
+++ b/pkgs/patches-linux-5.15/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch
@@ -0,0 +1,1034 @@
+From 692cfa85272dd12995b427c0a7a585ced5d54f32 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:33 +0000
+Subject: [PATCH 2/7] drivers: mfd: Add a driver for IEI WT61P803 PUZZLE MCU
+
+Add a driver for the IEI WT61P803 PUZZLE microcontroller, used in some
+IEI Puzzle series devices. The microcontroller controls system power,
+temperature sensors, fans and LEDs.
+
+This driver implements the core functionality for device communication
+over the system serial (serdev bus). It handles MCU messages and the
+internal MCU properties. Some properties can be managed over sysfs.
+
+Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
+Cc: Luka Perkov <luka.perkov@sartura.hr>
+Cc: Robert Marko <robert.marko@sartura.hr>
+---
+ drivers/mfd/Kconfig | 8 +
+ drivers/mfd/Makefile | 1 +
+ drivers/mfd/iei-wt61p803-puzzle.c | 908 ++++++++++++++++++++++++
+ include/linux/mfd/iei-wt61p803-puzzle.h | 66 ++
+ 4 files changed, 983 insertions(+)
+ create mode 100644 drivers/mfd/iei-wt61p803-puzzle.c
+ create mode 100644 include/linux/mfd/iei-wt61p803-puzzle.h
+
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -2187,6 +2187,15 @@ config SGI_MFD_IOC3
+ If you have an SGI Origin, Octane, or a PCI IOC3 card,
+ then say Y. Otherwise say N.
+
++config MFD_IEI_WT61P803_PUZZLE
++ tristate "IEI WT61P803 PUZZLE MCU driver"
++ depends on SERIAL_DEV_BUS
++ select MFD_CORE
++ help
++ IEI WT61P803 PUZZLE is a system power management microcontroller
++ used for fan control, temperature sensor reading, LED control
++ and system identification.
++
+ config MFD_INTEL_M10_BMC
+ tristate "Intel MAX 10 Board Management Controller"
+ depends on SPI_MASTER
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -237,6 +237,7 @@ obj-$(CONFIG_MFD_DLN2) += dln2.o
+ obj-$(CONFIG_MFD_RT4831) += rt4831.o
+ obj-$(CONFIG_MFD_RT5033) += rt5033.o
+ obj-$(CONFIG_MFD_SKY81452) += sky81452.o
++obj-$(CONFIG_MFD_IEI_WT61P803_PUZZLE) += iei-wt61p803-puzzle.o
+
+ intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
+ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
+--- /dev/null
++++ b/drivers/mfd/iei-wt61p803-puzzle.c
+@@ -0,0 +1,908 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* IEI WT61P803 PUZZLE MCU Driver
++ * System management microcontroller for fan control, temperature sensor reading,
++ * LED control and system identification on IEI Puzzle series ARM-based appliances.
++ *
++ * Copyright (C) 2020 Sartura Ltd.
++ * Author: Luka Kovacic <luka.kovacic@sartura.hr>
++ */
++
++#include <linux/atomic.h>
++#include <linux/delay.h>
++#include <linux/export.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/mfd/core.h>
++#include <linux/mfd/iei-wt61p803-puzzle.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/of_platform.h>
++#include <linux/property.h>
++#include <linux/sched.h>
++#include <linux/serdev.h>
++#include <linux/slab.h>
++#include <linux/sysfs.h>
++#include <asm/unaligned.h>
++
++/* start, payload and XOR checksum at end */
++#define IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH (1 + 20 + 1)
++#define IEI_WT61P803_PUZZLE_RESP_BUF_SIZE 512
++
++#define IEI_WT61P803_PUZZLE_MAC_LENGTH 17
++#define IEI_WT61P803_PUZZLE_SN_LENGTH 36
++#define IEI_WT61P803_PUZZLE_VERSION_LENGTH 6
++#define IEI_WT61P803_PUZZLE_BUILD_INFO_LENGTH 16
++#define IEI_WT61P803_PUZZLE_PROTOCOL_VERSION_LENGTH 8
++#define IEI_WT61P803_PUZZLE_NB_MAC 8
++
++/* Use HZ as a timeout value throughout the driver */
++#define IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT HZ
++
++enum iei_wt61p803_puzzle_attribute_type {
++ IEI_WT61P803_PUZZLE_VERSION,
++ IEI_WT61P803_PUZZLE_BUILD_INFO,
++ IEI_WT61P803_PUZZLE_BOOTLOADER_MODE,
++ IEI_WT61P803_PUZZLE_PROTOCOL_VERSION,
++ IEI_WT61P803_PUZZLE_SERIAL_NUMBER,
++ IEI_WT61P803_PUZZLE_MAC_ADDRESS,
++ IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS,
++ IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY,
++ IEI_WT61P803_PUZZLE_POWER_STATUS,
++};
++
++struct iei_wt61p803_puzzle_device_attribute {
++ struct device_attribute dev_attr;
++ enum iei_wt61p803_puzzle_attribute_type type;
++ u8 index;
++};
++
++/**
++ * struct iei_wt61p803_puzzle_mcu_status - MCU flags state
++ * @ac_recovery_status_flag: AC Recovery Status Flag
++ * @power_loss_recovery: System recovery after power loss
++ * @power_status: System Power-on Method
++ */
++struct iei_wt61p803_puzzle_mcu_status {
++ u8 ac_recovery_status_flag;
++ u8 power_loss_recovery;
++ u8 power_status;
++};
++
++/**
++ * struct iei_wt61p803_puzzle_reply - MCU reply
++ * @size: Size of the MCU reply
++ * @data: Full MCU reply buffer
++ * @state: Current state of the packet
++ * @received: Was the response fullfilled
++ */
++struct iei_wt61p803_puzzle_reply {
++ size_t size;
++ unsigned char data[IEI_WT61P803_PUZZLE_RESP_BUF_SIZE];
++ struct completion received;
++};
++
++/**
++ * struct iei_wt61p803_puzzle_mcu_version - MCU version status
++ * @version: Primary firmware version
++ * @build_info: Build date and time
++ * @bootloader_mode: Status of the MCU operation
++ * @protocol_version: MCU communication protocol version
++ * @serial_number: Device factory serial number
++ * @mac_address: Device factory MAC addresses
++ *
++ * Last element of arrays is reserved for '\0'.
++ */
++struct iei_wt61p803_puzzle_mcu_version {
++ char version[IEI_WT61P803_PUZZLE_VERSION_LENGTH + 1];
++ char build_info[IEI_WT61P803_PUZZLE_BUILD_INFO_LENGTH + 1];
++ bool bootloader_mode;
++ char protocol_version[IEI_WT61P803_PUZZLE_PROTOCOL_VERSION_LENGTH + 1];
++ char serial_number[IEI_WT61P803_PUZZLE_SN_LENGTH + 1];
++ char mac_address[IEI_WT61P803_PUZZLE_NB_MAC][IEI_WT61P803_PUZZLE_MAC_LENGTH + 1];
++};
++
++/**
++ * struct iei_wt61p803_puzzle - IEI WT61P803 PUZZLE MCU Driver
++ * @serdev: Pointer to underlying serdev device
++ * @dev: Pointer to underlying dev device
++ * @reply_lock: Reply mutex lock
++ * @reply: Pointer to the iei_wt61p803_puzzle_reply struct
++ * @version: MCU version related data
++ * @status: MCU status related data
++ * @response_buffer Command response buffer allocation
++ * @lock General member mutex lock
++ */
++struct iei_wt61p803_puzzle {
++ struct serdev_device *serdev;
++ struct device *dev;
++ struct mutex reply_lock; /* lock to prevent multiple firmware calls */
++ struct iei_wt61p803_puzzle_reply *reply;
++ struct iei_wt61p803_puzzle_mcu_version version;
++ struct iei_wt61p803_puzzle_mcu_status status;
++ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
++ struct mutex lock; /* lock to protect response buffer */
++};
++
++static unsigned char iei_wt61p803_puzzle_checksum(unsigned char *buf, size_t len)
++{
++ unsigned char checksum = 0;
++ size_t i;
++
++ for (i = 0; i < len; i++)
++ checksum ^= buf[i];
++ return checksum;
++}
++
++static int iei_wt61p803_puzzle_process_resp(struct iei_wt61p803_puzzle *mcu,
++ const unsigned char *raw_resp_data, size_t size)
++{
++ unsigned char checksum;
++
++ /* Check the incoming frame header */
++ if (!(raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START ||
++ raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER ||
++ (raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM &&
++ raw_resp_data[1] == IEI_WT61P803_PUZZLE_CMD_EEPROM_READ))) {
++ if (mcu->reply->size + size >= sizeof(mcu->reply->data))
++ return -EIO;
++
++ /* Append the frame to existing data */
++ memcpy(mcu->reply->data + mcu->reply->size, raw_resp_data, size);
++ mcu->reply->size += size;
++ } else {
++ if (size >= sizeof(mcu->reply->data))
++ return -EIO;
++
++ /* Start processing a new frame */
++ memcpy(mcu->reply->data, raw_resp_data, size);
++ mcu->reply->size = size;
++ }
++
++ checksum = iei_wt61p803_puzzle_checksum(mcu->reply->data, mcu->reply->size - 1);
++ if (checksum != mcu->reply->data[mcu->reply->size - 1]) {
++ /* The checksum isn't matched yet, wait for new frames */
++ return size;
++ }
++
++ /* Received all the data */
++ complete(&mcu->reply->received);
++
++ return size;
++}
++
++static int iei_wt61p803_puzzle_recv_buf(struct serdev_device *serdev,
++ const unsigned char *data, size_t size)
++{
++ struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev);
++ int ret;
++
++ ret = iei_wt61p803_puzzle_process_resp(mcu, data, size);
++ /* Return the number of processed bytes if function returns error,
++ * discard the remaining incoming data, since the frame this data
++ * belongs to is broken anyway
++ */
++ if (ret < 0)
++ return size;
++
++ return ret;
++}
++
++static const struct serdev_device_ops iei_wt61p803_puzzle_serdev_device_ops = {
++ .receive_buf = iei_wt61p803_puzzle_recv_buf,
++ .write_wakeup = serdev_device_write_wakeup,
++};
++
++/**
++ * iei_wt61p803_puzzle_write_command_watchdog() - Watchdog of the normal cmd
++ * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct
++ * @cmd: Pointer to the char array to send (size should be content + 1 (xor))
++ * @size: Size of the cmd char array
++ * @reply_data: Pointer to the reply/response data array (should be allocated)
++ * @reply_size: Pointer to size_t (size of reply_data)
++ * @retry_count: Number of times to retry sending the command to the MCU
++ */
++int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu,
++ unsigned char *cmd, size_t size,
++ unsigned char *reply_data,
++ size_t *reply_size, int retry_count)
++{
++ struct device *dev = &mcu->serdev->dev;
++ int ret, i;
++
++ for (i = 0; i < retry_count; i++) {
++ ret = iei_wt61p803_puzzle_write_command(mcu, cmd, size,
++ reply_data, reply_size);
++ if (ret != -ETIMEDOUT)
++ return ret;
++ }
++
++ dev_err(dev, "Command response timed out. Retries: %d\n", retry_count);
++
++ return -ETIMEDOUT;
++}
++EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command_watchdog);
++
++/**
++ * iei_wt61p803_puzzle_write_command() - Send a structured command to the MCU
++ * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct
++ * @cmd: Pointer to the char array to send (size should be content + 1 (xor))
++ * @size: Size of the cmd char array
++ * @reply_data: Pointer to the reply/response data array (should be allocated)
++ *
++ * Sends a structured command to the MCU.
++ */
++int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu,
++ unsigned char *cmd, size_t size,
++ unsigned char *reply_data,
++ size_t *reply_size)
++{
++ struct device *dev = &mcu->serdev->dev;
++ int ret;
++
++ if (size <= 1 || size > IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH)
++ return -EINVAL;
++
++ mutex_lock(&mcu->reply_lock);
++
++ cmd[size - 1] = iei_wt61p803_puzzle_checksum(cmd, size - 1);
++
++ /* Initialize reply struct */
++ reinit_completion(&mcu->reply->received);
++ mcu->reply->size = 0;
++ usleep_range(2000, 10000);
++ serdev_device_write_flush(mcu->serdev);
++ ret = serdev_device_write_buf(mcu->serdev, cmd, size);
++ if (ret < 0)
++ goto exit;
++
++ serdev_device_wait_until_sent(mcu->serdev, IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT);
++ ret = wait_for_completion_timeout(&mcu->reply->received,
++ IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT);
++ if (ret == 0) {
++ dev_err(dev, "Command reply receive timeout\n");
++ ret = -ETIMEDOUT;
++ goto exit;
++ }
++
++ *reply_size = mcu->reply->size;
++ /* Copy the received data, as it will not be available after a new frame is received */
++ memcpy(reply_data, mcu->reply->data, mcu->reply->size);
++ ret = 0;
++exit:
++ mutex_unlock(&mcu->reply_lock);
++ return ret;
++}
++EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command);
++
++static int iei_wt61p803_puzzle_buzzer(struct iei_wt61p803_puzzle *mcu, bool long_beep)
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char buzzer_cmd[4] = {};
++ size_t reply_size;
++ int ret;
++
++ buzzer_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ buzzer_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_SINGLE;
++ buzzer_cmd[2] = long_beep ? '3' : '2'; /* Buzzer 1.5 / 0.5 second beep */
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, buzzer_cmd, sizeof(buzzer_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto exit;
++
++ if (reply_size != 3) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) {
++ ret = -EPROTO;
++ goto exit;
++ }
++exit:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_get_version(struct iei_wt61p803_puzzle *mcu)
++{
++ unsigned char version_cmd[3] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_OTHER_VERSION,
++ };
++ unsigned char build_info_cmd[3] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_OTHER_BUILD,
++ };
++ unsigned char bootloader_mode_cmd[3] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_OTHER_BOOTLOADER_MODE,
++ };
++ unsigned char protocol_version_cmd[3] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_OTHER_PROTOCOL_VERSION,
++ };
++ unsigned char *rb = mcu->response_buffer;
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu->lock);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, version_cmd, sizeof(version_cmd),
++ rb, &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size < 7) {
++ ret = -EIO;
++ goto err;
++ }
++ sprintf(mcu->version.version, "v%c.%.3s", rb[2], &rb[3]);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, build_info_cmd,
++ sizeof(build_info_cmd), rb,
++ &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size < 15) {
++ ret = -EIO;
++ goto err;
++ }
++ sprintf(mcu->version.build_info, "%c%c/%c%c/%.4s %c%c:%c%c",
++ rb[8], rb[9], rb[6], rb[7], &rb[2], rb[10], rb[11],
++ rb[12], rb[13]);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, bootloader_mode_cmd,
++ sizeof(bootloader_mode_cmd), rb,
++ &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size < 4) {
++ ret = -EIO;
++ goto err;
++ }
++ if (rb[2] == IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_APPS)
++ mcu->version.bootloader_mode = false;
++ else if (rb[2] == IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_BOOTLOADER)
++ mcu->version.bootloader_mode = true;
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, protocol_version_cmd,
++ sizeof(protocol_version_cmd), rb,
++ &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size < 9) {
++ ret = -EIO;
++ goto err;
++ }
++ sprintf(mcu->version.protocol_version, "v%c.%c%c%c%c%c",
++ rb[7], rb[6], rb[5], rb[4], rb[3], rb[2]);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_get_mcu_status(struct iei_wt61p803_puzzle *mcu)
++{
++ unsigned char mcu_status_cmd[5] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START,
++ IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER,
++ IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS,
++ IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS,
++ };
++ unsigned char *resp_buf = mcu->response_buffer;
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, mcu_status_cmd, sizeof(mcu_status_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto exit;
++ if (reply_size < 20) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ /* Response format:
++ * (IDX RESPONSE)
++ * 0 @
++ * 1 O
++ * 2 S
++ * 3 S
++ * ...
++ * 5 AC Recovery Status Flag
++ * ...
++ * 10 Power Loss Recovery
++ * ...
++ * 19 Power Status (system power on method)
++ * 20 XOR checksum
++ */
++ if (resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS &&
++ resp_buf[3] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS) {
++ mcu->status.ac_recovery_status_flag = resp_buf[5];
++ mcu->status.power_loss_recovery = resp_buf[10];
++ mcu->status.power_status = resp_buf[19];
++ }
++exit:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_get_serial_number(struct iei_wt61p803_puzzle *mcu)
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char serial_number_cmd[5] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM,
++ IEI_WT61P803_PUZZLE_CMD_EEPROM_READ,
++ 0x00, /* EEPROM read address */
++ 0x24, /* Data length */
++ };
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd,
++ sizeof(serial_number_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto err;
++
++ if (reply_size < IEI_WT61P803_PUZZLE_SN_LENGTH + 4) {
++ ret = -EIO;
++ goto err;
++ }
++
++ sprintf(mcu->version.serial_number, "%.*s",
++ IEI_WT61P803_PUZZLE_SN_LENGTH, resp_buf + 4);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_write_serial_number(struct iei_wt61p803_puzzle *mcu,
++ unsigned char serial_number[36])
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char serial_number_header[4] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM,
++ IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE,
++ 0x00, /* EEPROM write address */
++ 0xC, /* Data length */
++ };
++ unsigned char serial_number_cmd[4 + 12 + 1]; /* header, serial number, XOR checksum */
++ int ret, sn_counter;
++ size_t reply_size;
++
++ /* The MCU can only handle 22 byte messages, send the S/N in 12 byte chunks */
++ mutex_lock(&mcu->lock);
++ for (sn_counter = 0; sn_counter < 3; sn_counter++) {
++ serial_number_header[2] = 0x0 + 0xC * sn_counter;
++
++ memcpy(serial_number_cmd, serial_number_header, sizeof(serial_number_header));
++ memcpy(serial_number_cmd + sizeof(serial_number_header),
++ serial_number + 0xC * sn_counter, 0xC);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd,
++ sizeof(serial_number_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size != 3) {
++ ret = -EIO;
++ goto err;
++ }
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) {
++ ret = -EPROTO;
++ goto err;
++ }
++ }
++
++ sprintf(mcu->version.serial_number, "%.*s",
++ IEI_WT61P803_PUZZLE_SN_LENGTH, serial_number);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_get_mac_address(struct iei_wt61p803_puzzle *mcu, int index)
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char mac_address_cmd[5] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM,
++ IEI_WT61P803_PUZZLE_CMD_EEPROM_READ,
++ 0x00, /* EEPROM read address */
++ 0x11, /* Data length */
++ };
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu->lock);
++ mac_address_cmd[2] = 0x24 + 0x11 * index;
++
++ ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd,
++ sizeof(mac_address_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto err;
++
++ if (reply_size < 22) {
++ ret = -EIO;
++ goto err;
++ }
++
++ sprintf(mcu->version.mac_address[index], "%.*s",
++ IEI_WT61P803_PUZZLE_MAC_LENGTH, resp_buf + 4);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int
++iei_wt61p803_puzzle_write_mac_address(struct iei_wt61p803_puzzle *mcu,
++ unsigned char mac_address[IEI_WT61P803_PUZZLE_MAC_LENGTH],
++ int mac_address_idx)
++{
++ unsigned char mac_address_cmd[4 + IEI_WT61P803_PUZZLE_MAC_LENGTH + 1];
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char mac_address_header[4] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM,
++ IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE,
++ 0x00, /* EEPROM write address */
++ 0x11, /* Data length */
++ };
++ size_t reply_size;
++ int ret;
++
++ if (mac_address_idx < 0 || mac_address_idx >= IEI_WT61P803_PUZZLE_NB_MAC)
++ return -EINVAL;
++
++ mac_address_header[2] = 0x24 + 0x11 * mac_address_idx;
++
++ /* Concat mac_address_header, mac_address to mac_address_cmd */
++ memcpy(mac_address_cmd, mac_address_header, sizeof(mac_address_header));
++ memcpy(mac_address_cmd + sizeof(mac_address_header), mac_address,
++ IEI_WT61P803_PUZZLE_MAC_LENGTH);
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd,
++ sizeof(mac_address_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto err;
++ if (reply_size != 3) {
++ ret = -EIO;
++ goto err;
++ }
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) {
++ ret = -EPROTO;
++ goto err;
++ }
++
++ sprintf(mcu->version.mac_address[mac_address_idx], "%.*s",
++ IEI_WT61P803_PUZZLE_MAC_LENGTH, mac_address);
++err:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_write_power_loss_recovery(struct iei_wt61p803_puzzle *mcu,
++ int power_loss_recovery_action)
++{
++ unsigned char *resp_buf = mcu->response_buffer;
++ unsigned char power_loss_recovery_cmd[5] = {};
++ size_t reply_size;
++ int ret;
++
++ if (power_loss_recovery_action < 0 || power_loss_recovery_action > 4)
++ return -EINVAL;
++
++ power_loss_recovery_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ power_loss_recovery_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER;
++ power_loss_recovery_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS;
++ power_loss_recovery_cmd[3] = hex_asc[power_loss_recovery_action];
++
++ mutex_lock(&mcu->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu, power_loss_recovery_cmd,
++ sizeof(power_loss_recovery_cmd),
++ resp_buf, &reply_size);
++ if (ret)
++ goto exit;
++ mcu->status.power_loss_recovery = power_loss_recovery_action;
++exit:
++ mutex_unlock(&mcu->lock);
++ return ret;
++}
++
++#define to_puzzle_dev_attr(_attr) \
++ container_of(_attr, struct iei_wt61p803_puzzle_device_attribute, dev_attr)
++
++static ssize_t show_output(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev);
++ struct iei_wt61p803_puzzle_device_attribute *pattr = to_puzzle_dev_attr(attr);
++ int ret;
++
++ switch (pattr->type) {
++ case IEI_WT61P803_PUZZLE_VERSION:
++ return scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.version);
++ case IEI_WT61P803_PUZZLE_BUILD_INFO:
++ return scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.build_info);
++ case IEI_WT61P803_PUZZLE_BOOTLOADER_MODE:
++ return scnprintf(buf, PAGE_SIZE, "%d\n", mcu->version.bootloader_mode);
++ case IEI_WT61P803_PUZZLE_PROTOCOL_VERSION:
++ return scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.protocol_version);
++ case IEI_WT61P803_PUZZLE_SERIAL_NUMBER:
++ ret = iei_wt61p803_puzzle_get_serial_number(mcu);
++ if (!ret)
++ ret = scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.serial_number);
++ else
++ ret = 0;
++ return ret;
++ case IEI_WT61P803_PUZZLE_MAC_ADDRESS:
++ ret = iei_wt61p803_puzzle_get_mac_address(mcu, pattr->index);
++ if (!ret)
++ ret = scnprintf(buf, PAGE_SIZE, "%s\n",
++ mcu->version.mac_address[pattr->index]);
++ else
++ ret = 0;
++ return ret;
++ case IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS:
++ case IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY:
++ case IEI_WT61P803_PUZZLE_POWER_STATUS:
++ ret = iei_wt61p803_puzzle_get_mcu_status(mcu);
++ if (ret)
++ return ret;
++
++ mutex_lock(&mcu->lock);
++ switch (pattr->type) {
++ case IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS:
++ ret = scnprintf(buf, PAGE_SIZE, "%x\n",
++ mcu->status.ac_recovery_status_flag);
++ break;
++ case IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY:
++ ret = scnprintf(buf, PAGE_SIZE, "%x\n", mcu->status.power_loss_recovery);
++ break;
++ case IEI_WT61P803_PUZZLE_POWER_STATUS:
++ ret = scnprintf(buf, PAGE_SIZE, "%x\n", mcu->status.power_status);
++ break;
++ default:
++ ret = 0;
++ break;
++ }
++ mutex_unlock(&mcu->lock);
++ return ret;
++ default:
++ return 0;
++ }
++
++ return 0;
++}
++
++static ssize_t store_output(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t len)
++{
++ unsigned char serial_number[IEI_WT61P803_PUZZLE_SN_LENGTH];
++ unsigned char mac_address[IEI_WT61P803_PUZZLE_MAC_LENGTH];
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev);
++ struct iei_wt61p803_puzzle_device_attribute *pattr = to_puzzle_dev_attr(attr);
++ int power_loss_recovery_action = 0;
++ int ret;
++
++ switch (pattr->type) {
++ case IEI_WT61P803_PUZZLE_SERIAL_NUMBER:
++ if (len != (size_t)(IEI_WT61P803_PUZZLE_SN_LENGTH + 1))
++ return -EINVAL;
++ memcpy(serial_number, buf, sizeof(serial_number));
++ ret = iei_wt61p803_puzzle_write_serial_number(mcu, serial_number);
++ if (ret)
++ return ret;
++ return len;
++ case IEI_WT61P803_PUZZLE_MAC_ADDRESS:
++ if (len != (size_t)(IEI_WT61P803_PUZZLE_MAC_LENGTH + 1))
++ return -EINVAL;
++
++ memcpy(mac_address, buf, sizeof(mac_address));
++
++ if (strlen(attr->attr.name) != 13)
++ return -EIO;
++
++ ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, pattr->index);
++ if (ret)
++ return ret;
++ return len;
++ case IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY:
++ ret = kstrtoint(buf, 10, &power_loss_recovery_action);
++ if (ret)
++ return ret;
++ ret = iei_wt61p803_puzzle_write_power_loss_recovery(mcu,
++ power_loss_recovery_action);
++ if (ret)
++ return ret;
++ return len;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++#define IEI_WT61P803_PUZZLE_ATTR(_name, _mode, _show, _store, _type, _index) \
++ struct iei_wt61p803_puzzle_device_attribute dev_attr_##_name = \
++ { .dev_attr = __ATTR(_name, _mode, _show, _store), \
++ .type = _type, \
++ .index = _index }
++
++#define IEI_WT61P803_PUZZLE_ATTR_RO(_name, _type, _id) \
++ IEI_WT61P803_PUZZLE_ATTR(_name, 0444, show_output, NULL, _type, _id)
++
++#define IEI_WT61P803_PUZZLE_ATTR_RW(_name, _type, _id) \
++ IEI_WT61P803_PUZZLE_ATTR(_name, 0644, show_output, store_output, _type, _id)
++
++static IEI_WT61P803_PUZZLE_ATTR_RO(version, IEI_WT61P803_PUZZLE_VERSION, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RO(build_info, IEI_WT61P803_PUZZLE_BUILD_INFO, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RO(bootloader_mode, IEI_WT61P803_PUZZLE_BOOTLOADER_MODE, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RO(protocol_version, IEI_WT61P803_PUZZLE_PROTOCOL_VERSION, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RW(serial_number, IEI_WT61P803_PUZZLE_SERIAL_NUMBER, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_0, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_1, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 1);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_2, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 2);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_3, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 3);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_4, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 4);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_5, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 5);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_6, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 6);
++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_7, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 7);
++static IEI_WT61P803_PUZZLE_ATTR_RO(ac_recovery_status, IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RW(power_loss_recovery, IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY, 0);
++static IEI_WT61P803_PUZZLE_ATTR_RO(power_status, IEI_WT61P803_PUZZLE_POWER_STATUS, 0);
++
++static struct attribute *iei_wt61p803_puzzle_attrs[] = {
++ &dev_attr_version.dev_attr.attr,
++ &dev_attr_build_info.dev_attr.attr,
++ &dev_attr_bootloader_mode.dev_attr.attr,
++ &dev_attr_protocol_version.dev_attr.attr,
++ &dev_attr_serial_number.dev_attr.attr,
++ &dev_attr_mac_address_0.dev_attr.attr,
++ &dev_attr_mac_address_1.dev_attr.attr,
++ &dev_attr_mac_address_2.dev_attr.attr,
++ &dev_attr_mac_address_3.dev_attr.attr,
++ &dev_attr_mac_address_4.dev_attr.attr,
++ &dev_attr_mac_address_5.dev_attr.attr,
++ &dev_attr_mac_address_6.dev_attr.attr,
++ &dev_attr_mac_address_7.dev_attr.attr,
++ &dev_attr_ac_recovery_status.dev_attr.attr,
++ &dev_attr_power_loss_recovery.dev_attr.attr,
++ &dev_attr_power_status.dev_attr.attr,
++ NULL
++};
++ATTRIBUTE_GROUPS(iei_wt61p803_puzzle);
++
++static int iei_wt61p803_puzzle_sysfs_create(struct device *dev,
++ struct iei_wt61p803_puzzle *mcu)
++{
++ int ret;
++
++ ret = sysfs_create_groups(&mcu->dev->kobj, iei_wt61p803_puzzle_groups);
++ if (ret)
++ mfd_remove_devices(mcu->dev);
++
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_sysfs_remove(struct device *dev,
++ struct iei_wt61p803_puzzle *mcu)
++{
++ /* Remove sysfs groups */
++ sysfs_remove_groups(&mcu->dev->kobj, iei_wt61p803_puzzle_groups);
++ mfd_remove_devices(mcu->dev);
++
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_probe(struct serdev_device *serdev)
++{
++ struct device *dev = &serdev->dev;
++ struct iei_wt61p803_puzzle *mcu;
++ u32 baud;
++ int ret;
++
++ /* Read the baud rate from 'current-speed', because the MCU supports different rates */
++ if (device_property_read_u32(dev, "current-speed", &baud)) {
++ dev_err(dev,
++ "'current-speed' is not specified in device node\n");
++ return -EINVAL;
++ }
++ dev_dbg(dev, "Driver baud rate: %d\n", baud);
++
++ /* Allocate the memory */
++ mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL);
++ if (!mcu)
++ return -ENOMEM;
++
++ mcu->reply = devm_kzalloc(dev, sizeof(*mcu->reply), GFP_KERNEL);
++ if (!mcu->reply)
++ return -ENOMEM;
++
++ /* Initialize device struct data */
++ mcu->serdev = serdev;
++ mcu->dev = dev;
++ init_completion(&mcu->reply->received);
++ mutex_init(&mcu->reply_lock);
++ mutex_init(&mcu->lock);
++
++ /* Setup UART interface */
++ serdev_device_set_drvdata(serdev, mcu);
++ serdev_device_set_client_ops(serdev, &iei_wt61p803_puzzle_serdev_device_ops);
++ ret = devm_serdev_device_open(dev, serdev);
++ if (ret)
++ return ret;
++ serdev_device_set_baudrate(serdev, baud);
++ serdev_device_set_flow_control(serdev, false);
++ ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
++ if (ret) {
++ dev_err(dev, "Failed to set parity\n");
++ return ret;
++ }
++
++ ret = iei_wt61p803_puzzle_get_version(mcu);
++ if (ret)
++ return ret;
++
++ dev_dbg(dev, "MCU version: %s\n", mcu->version.version);
++ dev_dbg(dev, "MCU firmware build info: %s\n", mcu->version.build_info);
++ dev_dbg(dev, "MCU in bootloader mode: %s\n",
++ mcu->version.bootloader_mode ? "true" : "false");
++ dev_dbg(dev, "MCU protocol version: %s\n", mcu->version.protocol_version);
++
++ if (device_property_read_bool(dev, "enable-beep")) {
++ ret = iei_wt61p803_puzzle_buzzer(mcu, false);
++ if (ret)
++ return ret;
++ }
++
++ ret = iei_wt61p803_puzzle_sysfs_create(dev, mcu);
++ if (ret)
++ return ret;
++
++ return devm_of_platform_populate(dev);
++}
++
++static void iei_wt61p803_puzzle_remove(struct serdev_device *serdev)
++{
++ struct device *dev = &serdev->dev;
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev);
++
++ iei_wt61p803_puzzle_sysfs_remove(dev, mcu);
++}
++
++static const struct of_device_id iei_wt61p803_puzzle_dt_ids[] = {
++ { .compatible = "iei,wt61p803-puzzle" },
++ { }
++};
++
++MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_dt_ids);
++
++static struct serdev_device_driver iei_wt61p803_puzzle_drv = {
++ .probe = iei_wt61p803_puzzle_probe,
++ .remove = iei_wt61p803_puzzle_remove,
++ .driver = {
++ .name = "iei-wt61p803-puzzle",
++ .of_match_table = iei_wt61p803_puzzle_dt_ids,
++ },
++};
++
++module_serdev_device_driver(iei_wt61p803_puzzle_drv);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Luka Kovacic <luka.kovacic@sartura.hr>");
++MODULE_DESCRIPTION("IEI WT61P803 PUZZLE MCU Driver");
+--- /dev/null
++++ b/include/linux/mfd/iei-wt61p803-puzzle.h
+@@ -0,0 +1,66 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* IEI WT61P803 PUZZLE MCU Driver
++ * System management microcontroller for fan control, temperature sensor reading,
++ * LED control and system identification on IEI Puzzle series ARM-based appliances.
++ *
++ * Copyright (C) 2020 Sartura Ltd.
++ * Author: Luka Kovacic <luka.kovacic@sartura.hr>
++ */
++
++#ifndef _MFD_IEI_WT61P803_PUZZLE_H_
++#define _MFD_IEI_WT61P803_PUZZLE_H_
++
++#define IEI_WT61P803_PUZZLE_BUF_SIZE 512
++
++/* Command magic numbers */
++#define IEI_WT61P803_PUZZLE_CMD_HEADER_START 0x40 /* @ */
++#define IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER 0x25 /* % */
++#define IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM 0xF7
++
++#define IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK 0x30 /* 0 */
++#define IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK 0x70
++
++#define IEI_WT61P803_PUZZLE_CMD_EEPROM_READ 0xA1
++#define IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE 0xA0
++
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_VERSION 0x56 /* V */
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_BUILD 0x42 /* B */
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_BOOTLOADER_MODE 0x4D /* M */
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_BOOTLOADER 0x30
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_APPS 0x31
++#define IEI_WT61P803_PUZZLE_CMD_OTHER_PROTOCOL_VERSION 0x50 /* P */
++
++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_SINGLE 0x43 /* C */
++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER 0x4F /* O */
++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS 0x53 /* S */
++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS 0x41 /* A */
++
++#define IEI_WT61P803_PUZZLE_CMD_LED 0x52 /* R */
++#define IEI_WT61P803_PUZZLE_CMD_LED_POWER 0x31 /* 1 */
++
++#define IEI_WT61P803_PUZZLE_CMD_TEMP 0x54 /* T */
++#define IEI_WT61P803_PUZZLE_CMD_TEMP_ALL 0x41 /* A */
++
++#define IEI_WT61P803_PUZZLE_CMD_FAN 0x46 /* F */
++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ 0x5A /* Z */
++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_WRITE 0x57 /* W */
++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_BASE 0x30
++#define IEI_WT61P803_PUZZLE_CMD_FAN_RPM_BASE 0x41 /* A */
++
++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM(x) (IEI_WT61P803_PUZZLE_CMD_FAN_PWM_BASE + (x)) /* 0 - 1 */
++#define IEI_WT61P803_PUZZLE_CMD_FAN_RPM(x) (IEI_WT61P803_PUZZLE_CMD_FAN_RPM_BASE + (x)) /* 0 - 5 */
++
++struct iei_wt61p803_puzzle_mcu_version;
++struct iei_wt61p803_puzzle_reply;
++struct iei_wt61p803_puzzle;
++
++int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu,
++ unsigned char *cmd, size_t size,
++ unsigned char *reply_data, size_t *reply_size,
++ int retry_count);
++
++int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu,
++ unsigned char *cmd, size_t size,
++ unsigned char *reply_data, size_t *reply_size);
++
++#endif /* _MFD_IEI_WT61P803_PUZZLE_H_ */
diff --git a/pkgs/patches-linux-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch b/pkgs/patches-linux-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch
new file mode 100644
index 0000000..c22314e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch
@@ -0,0 +1,469 @@
+From e3310a638cd310bfd93dbbc6d2732ab6aea18dd2 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:34 +0000
+Subject: [PATCH 3/7] drivers: hwmon: Add the IEI WT61P803 PUZZLE HWMON driver
+
+Add the IEI WT61P803 PUZZLE HWMON driver, that handles the fan speed
+control via PWM, reading fan speed and reading on-board temperature
+sensors.
+
+The driver registers a HWMON device and a simple thermal cooling device to
+enable in-kernel fan management.
+
+This driver depends on the IEI WT61P803 PUZZLE MFD driver.
+
+Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
+Acked-by: Guenter Roeck <linux@roeck-us.net>
+Cc: Luka Perkov <luka.perkov@sartura.hr>
+Cc: Robert Marko <robert.marko@sartura.hr>
+---
+ drivers/hwmon/Kconfig | 8 +
+ drivers/hwmon/Makefile | 1 +
+ drivers/hwmon/iei-wt61p803-puzzle-hwmon.c | 413 ++++++++++++++++++++++
+ 3 files changed, 422 insertions(+)
+ create mode 100644 drivers/hwmon/iei-wt61p803-puzzle-hwmon.c
+
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -732,6 +732,14 @@ config SENSORS_IBMPOWERNV
+ This driver can also be built as a module. If so, the module
+ will be called ibmpowernv.
+
++config SENSORS_IEI_WT61P803_PUZZLE_HWMON
++ tristate "IEI WT61P803 PUZZLE MFD HWMON Driver"
++ depends on MFD_IEI_WT61P803_PUZZLE
++ help
++ The IEI WT61P803 PUZZLE MFD HWMON Driver handles reading fan speed
++ and writing fan PWM values. It also supports reading on-board
++ temperature sensors.
++
+ config SENSORS_IIO_HWMON
+ tristate "Hwmon driver that uses channels specified via iio maps"
+ depends on IIO
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -84,6 +84,7 @@ obj-$(CONFIG_SENSORS_HIH6130) += hih6130
+ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
+ obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o
+ obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
++obj-$(CONFIG_SENSORS_IEI_WT61P803_PUZZLE_HWMON) += iei-wt61p803-puzzle-hwmon.o
+ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
+ obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
+ obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o
+--- /dev/null
++++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c
+@@ -0,0 +1,413 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* IEI WT61P803 PUZZLE MCU HWMON Driver
++ *
++ * Copyright (C) 2020 Sartura Ltd.
++ * Author: Luka Kovacic <luka.kovacic@sartura.hr>
++ */
++
++#include <linux/err.h>
++#include <linux/hwmon.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/math64.h>
++#include <linux/mfd/iei-wt61p803-puzzle.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++#include <linux/slab.h>
++#include <linux/thermal.h>
++
++#define IEI_WT61P803_PUZZLE_HWMON_MAX_PWM 2
++#define IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL 255
++
++/**
++ * struct iei_wt61p803_puzzle_thermal_cooling_device - Thermal cooling device instance
++ * @mcu_hwmon: Parent driver struct pointer
++ * @tcdev: Thermal cooling device pointer
++ * @name: Thermal cooling device name
++ * @pwm_channel: Controlled PWM channel (0 or 1)
++ * @cooling_levels: Thermal cooling device cooling levels (DT)
++ */
++struct iei_wt61p803_puzzle_thermal_cooling_device {
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon;
++ struct thermal_cooling_device *tcdev;
++ char name[THERMAL_NAME_LENGTH];
++ int pwm_channel;
++ u8 *cooling_levels;
++};
++
++/**
++ * struct iei_wt61p803_puzzle_hwmon - MCU HWMON Driver
++ * @mcu: MCU struct pointer
++ * @response_buffer Global MCU response buffer
++ * @thermal_cooling_dev_present: Per-channel thermal cooling device control indicator
++ * @cdev: Per-channel thermal cooling device private structure
++ */
++struct iei_wt61p803_puzzle_hwmon {
++ struct iei_wt61p803_puzzle *mcu;
++ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
++ bool thermal_cooling_dev_present[IEI_WT61P803_PUZZLE_HWMON_MAX_PWM];
++ struct iei_wt61p803_puzzle_thermal_cooling_device
++ *cdev[IEI_WT61P803_PUZZLE_HWMON_MAX_PWM];
++ struct mutex lock; /* mutex to protect response_buffer array */
++};
++
++#define raw_temp_to_milidegree_celsius(x) (((x) - 0x80) * 1000)
++static int iei_wt61p803_puzzle_read_temp_sensor(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon,
++ int channel, long *value)
++{
++ unsigned char *resp_buf = mcu_hwmon->response_buffer;
++ unsigned char temp_sensor_ntc_cmd[4] = {
++ IEI_WT61P803_PUZZLE_CMD_HEADER_START,
++ IEI_WT61P803_PUZZLE_CMD_TEMP,
++ IEI_WT61P803_PUZZLE_CMD_TEMP_ALL,
++ };
++ size_t reply_size;
++ int ret;
++
++ mutex_lock(&mcu_hwmon->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, temp_sensor_ntc_cmd,
++ sizeof(temp_sensor_ntc_cmd), resp_buf,
++ &reply_size);
++ if (ret)
++ goto exit;
++
++ if (reply_size != 7) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ /* Check the number of NTC values */
++ if (resp_buf[3] != '2') {
++ ret = -EIO;
++ goto exit;
++ }
++
++ *value = raw_temp_to_milidegree_celsius(resp_buf[4 + channel]);
++exit:
++ mutex_unlock(&mcu_hwmon->lock);
++ return ret;
++}
++
++#define raw_fan_val_to_rpm(x, y) ((((x) << 8 | (y)) / 2) * 60)
++static int iei_wt61p803_puzzle_read_fan_speed(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon,
++ int channel, long *value)
++{
++ unsigned char *resp_buf = mcu_hwmon->response_buffer;
++ unsigned char fan_speed_cmd[4] = {};
++ size_t reply_size;
++ int ret;
++
++ fan_speed_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ fan_speed_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN;
++ fan_speed_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_RPM(channel);
++
++ mutex_lock(&mcu_hwmon->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, fan_speed_cmd,
++ sizeof(fan_speed_cmd), resp_buf,
++ &reply_size);
++ if (ret)
++ goto exit;
++
++ if (reply_size != 7) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ *value = raw_fan_val_to_rpm(resp_buf[3], resp_buf[4]);
++exit:
++ mutex_unlock(&mcu_hwmon->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_write_pwm_channel(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon,
++ int channel, long pwm_set_val)
++{
++ unsigned char *resp_buf = mcu_hwmon->response_buffer;
++ unsigned char pwm_set_cmd[6] = {};
++ size_t reply_size;
++ int ret;
++
++ pwm_set_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ pwm_set_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN;
++ pwm_set_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM_WRITE;
++ pwm_set_cmd[3] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM(channel);
++ pwm_set_cmd[4] = pwm_set_val;
++
++ mutex_lock(&mcu_hwmon->lock);
++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, pwm_set_cmd,
++ sizeof(pwm_set_cmd), resp_buf,
++ &reply_size);
++ if (ret)
++ goto exit;
++
++ if (reply_size != 3) {
++ ret = -EIO;
++ goto exit;
++ }
++
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) {
++ ret = -EIO;
++ goto exit;
++ }
++exit:
++ mutex_unlock(&mcu_hwmon->lock);
++ return ret;
++}
++
++static int iei_wt61p803_puzzle_read_pwm_channel(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon,
++ int channel, long *value)
++{
++ unsigned char *resp_buf = mcu_hwmon->response_buffer;
++ unsigned char pwm_get_cmd[5] = {};
++ size_t reply_size;
++ int ret;
++
++ pwm_get_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ pwm_get_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN;
++ pwm_get_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ;
++ pwm_get_cmd[3] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM(channel);
++
++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, pwm_get_cmd,
++ sizeof(pwm_get_cmd), resp_buf,
++ &reply_size);
++ if (ret)
++ return ret;
++
++ if (reply_size != 5)
++ return -EIO;
++
++ if (resp_buf[2] != IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ)
++ return -EIO;
++
++ *value = resp_buf[3];
++
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_read(struct device *dev, enum hwmon_sensor_types type,
++ u32 attr, int channel, long *val)
++{
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = dev_get_drvdata(dev->parent);
++
++ switch (type) {
++ case hwmon_pwm:
++ return iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, channel, val);
++ case hwmon_fan:
++ return iei_wt61p803_puzzle_read_fan_speed(mcu_hwmon, channel, val);
++ case hwmon_temp:
++ return iei_wt61p803_puzzle_read_temp_sensor(mcu_hwmon, channel, val);
++ default:
++ return -EINVAL;
++ }
++}
++
++static int iei_wt61p803_puzzle_write(struct device *dev, enum hwmon_sensor_types type,
++ u32 attr, int channel, long val)
++{
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = dev_get_drvdata(dev->parent);
++
++ return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, channel, val);
++}
++
++static umode_t iei_wt61p803_puzzle_is_visible(const void *data, enum hwmon_sensor_types type,
++ u32 attr, int channel)
++{
++ const struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = data;
++
++ switch (type) {
++ case hwmon_pwm:
++ if (mcu_hwmon->thermal_cooling_dev_present[channel])
++ return 0444;
++ if (attr == hwmon_pwm_input)
++ return 0644;
++ break;
++ case hwmon_fan:
++ if (attr == hwmon_fan_input)
++ return 0444;
++ break;
++ case hwmon_temp:
++ if (attr == hwmon_temp_input)
++ return 0444;
++ break;
++ default:
++ return 0;
++ }
++
++ return 0;
++}
++
++static const struct hwmon_ops iei_wt61p803_puzzle_hwmon_ops = {
++ .is_visible = iei_wt61p803_puzzle_is_visible,
++ .read = iei_wt61p803_puzzle_read,
++ .write = iei_wt61p803_puzzle_write,
++};
++
++static const struct hwmon_channel_info *iei_wt61p803_puzzle_info[] = {
++ HWMON_CHANNEL_INFO(pwm,
++ HWMON_PWM_INPUT,
++ HWMON_PWM_INPUT),
++ HWMON_CHANNEL_INFO(fan,
++ HWMON_F_INPUT,
++ HWMON_F_INPUT,
++ HWMON_F_INPUT,
++ HWMON_F_INPUT,
++ HWMON_F_INPUT),
++ HWMON_CHANNEL_INFO(temp,
++ HWMON_T_INPUT,
++ HWMON_T_INPUT),
++ NULL
++};
++
++static const struct hwmon_chip_info iei_wt61p803_puzzle_chip_info = {
++ .ops = &iei_wt61p803_puzzle_hwmon_ops,
++ .info = iei_wt61p803_puzzle_info,
++};
++
++static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev,
++ unsigned long *state)
++{
++ *state = IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL;
++
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_get_cur_state(struct thermal_cooling_device *tcdev,
++ unsigned long *state)
++{
++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata;
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon;
++ long value;
++ int ret;
++
++ ret = iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, cdev->pwm_channel, &value);
++ if (ret)
++ return ret;
++ *state = value;
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_set_cur_state(struct thermal_cooling_device *tcdev,
++ unsigned long state)
++{
++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata;
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon;
++
++ return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, cdev->pwm_channel, state);
++}
++
++static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = {
++ .get_max_state = iei_wt61p803_puzzle_get_max_state,
++ .get_cur_state = iei_wt61p803_puzzle_get_cur_state,
++ .set_cur_state = iei_wt61p803_puzzle_set_cur_state,
++};
++
++static int
++iei_wt61p803_puzzle_enable_thermal_cooling_dev(struct device *dev,
++ struct fwnode_handle *child,
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon)
++{
++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev;
++ u32 pwm_channel;
++ u8 num_levels;
++ int ret;
++
++ ret = fwnode_property_read_u32(child, "reg", &pwm_channel);
++ if (ret)
++ return ret;
++
++ mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true;
++
++ num_levels = fwnode_property_count_u8(child, "cooling-levels");
++ if (!num_levels)
++ return -EINVAL;
++
++ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
++ if (!cdev)
++ return -ENOMEM;
++
++ cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u8), GFP_KERNEL);
++ if (!cdev->cooling_levels)
++ return -ENOMEM;
++
++ ret = fwnode_property_read_u8_array(child, "cooling-levels",
++ cdev->cooling_levels,
++ num_levels);
++ if (ret) {
++ dev_err(dev, "Couldn't read property 'cooling-levels'\n");
++ return ret;
++ }
++
++ snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel);
++ cdev->tcdev = devm_thermal_of_cooling_device_register(dev, NULL, cdev->name, cdev,
++ &iei_wt61p803_puzzle_cooling_ops);
++ if (IS_ERR(cdev->tcdev))
++ return PTR_ERR(cdev->tcdev);
++
++ cdev->mcu_hwmon = mcu_hwmon;
++ cdev->pwm_channel = pwm_channel;
++ mcu_hwmon->cdev[pwm_channel] = cdev;
++
++ return 0;
++}
++
++static int iei_wt61p803_puzzle_hwmon_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent);
++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon;
++ struct fwnode_handle *child;
++ struct device *hwmon_dev;
++ int ret;
++
++ mcu_hwmon = devm_kzalloc(dev, sizeof(*mcu_hwmon), GFP_KERNEL);
++ if (!mcu_hwmon)
++ return -ENOMEM;
++
++ mcu_hwmon->mcu = mcu;
++ platform_set_drvdata(pdev, mcu_hwmon);
++ mutex_init(&mcu_hwmon->lock);
++
++ hwmon_dev = devm_hwmon_device_register_with_info(dev, "iei_wt61p803_puzzle",
++ mcu_hwmon,
++ &iei_wt61p803_puzzle_chip_info,
++ NULL);
++ if (IS_ERR(hwmon_dev))
++ return PTR_ERR(hwmon_dev);
++
++ /* Control fans via PWM lines via Linux Kernel */
++ if (IS_ENABLED(CONFIG_THERMAL)) {
++ device_for_each_child_node(dev, child) {
++ ret = iei_wt61p803_puzzle_enable_thermal_cooling_dev(dev, child, mcu_hwmon);
++ if (ret) {
++ dev_err(dev, "Enabling the PWM fan failed\n");
++ fwnode_handle_put(child);
++ return ret;
++ }
++ }
++ }
++ return 0;
++}
++
++static const struct of_device_id iei_wt61p803_puzzle_hwmon_id_table[] = {
++ { .compatible = "iei,wt61p803-puzzle-hwmon" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_hwmon_id_table);
++
++static struct platform_driver iei_wt61p803_puzzle_hwmon_driver = {
++ .driver = {
++ .name = "iei-wt61p803-puzzle-hwmon",
++ .of_match_table = iei_wt61p803_puzzle_hwmon_id_table,
++ },
++ .probe = iei_wt61p803_puzzle_hwmon_probe,
++};
++
++module_platform_driver(iei_wt61p803_puzzle_hwmon_driver);
++
++MODULE_DESCRIPTION("IEI WT61P803 PUZZLE MCU HWMON Driver");
++MODULE_AUTHOR("Luka Kovacic <luka.kovacic@sartura.hr>");
++MODULE_LICENSE("GPL v2");
diff --git a/pkgs/patches-linux-5.15/904-debloat_dma_buf.patch b/pkgs/patches-linux-5.15/904-debloat_dma_buf.patch
new file mode 100644
index 0000000..302c000
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -4184,6 +4184,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/pkgs/patches-linux-5.15/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch b/pkgs/patches-linux-5.15/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch
new file mode 100644
index 0000000..0452bd6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch
@@ -0,0 +1,207 @@
+From f3b44eb69cc561cf05d00506dcec0dd9be003ed8 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:35 +0000
+Subject: [PATCH 4/7] drivers: leds: Add the IEI WT61P803 PUZZLE LED driver
+
+Add support for the IEI WT61P803 PUZZLE LED driver.
+Currently only the front panel power LED is supported,
+since it is the only LED on this board wired through the
+MCU.
+
+The LED is wired directly to the on-board MCU controller
+and is toggled using an MCU command.
+
+Support for more LEDs is going to be added in case more
+boards implement this microcontroller, as LEDs use many
+different GPIOs.
+
+This driver depends on the IEI WT61P803 PUZZLE MFD driver.
+
+Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
+Cc: Luka Perkov <luka.perkov@sartura.hr>
+Cc: Robert Marko <robert.marko@sartura.hr>
+---
+ drivers/leds/Kconfig | 8 ++
+ drivers/leds/Makefile | 1 +
+ drivers/leds/leds-iei-wt61p803-puzzle.c | 147 ++++++++++++++++++++++++
+ 3 files changed, 156 insertions(+)
+ create mode 100644 drivers/leds/leds-iei-wt61p803-puzzle.c
+
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -305,6 +305,14 @@ config LEDS_IPAQ_MICRO
+ Choose this option if you want to use the notification LED on
+ Compaq/HP iPAQ h3100 and h3600.
+
++config LEDS_IEI_WT61P803_PUZZLE
++ tristate "LED Support for the IEI WT61P803 PUZZLE MCU"
++ depends on LEDS_CLASS
++ depends on MFD_IEI_WT61P803_PUZZLE
++ help
++ This option enables support for LEDs controlled by the IEI WT61P803
++ M801 MCU.
++
+ config LEDS_HP6XX
+ tristate "LED Support for the HP Jornada 6xx"
+ depends on LEDS_CLASS
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -33,6 +33,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.
+ obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o
+ obj-$(CONFIG_LEDS_IP30) += leds-ip30.o
+ obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o
++obj-$(CONFIG_LEDS_IEI_WT61P803_PUZZLE) += leds-iei-wt61p803-puzzle.o
+ obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
+ obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
+ obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
+--- /dev/null
++++ b/drivers/leds/leds-iei-wt61p803-puzzle.c
+@@ -0,0 +1,147 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* IEI WT61P803 PUZZLE MCU LED Driver
++ *
++ * Copyright (C) 2020 Sartura Ltd.
++ * Author: Luka Kovacic <luka.kovacic@sartura.hr>
++ */
++
++#include <linux/leds.h>
++#include <linux/mfd/iei-wt61p803-puzzle.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++#include <linux/slab.h>
++
++enum iei_wt61p803_puzzle_led_state {
++ IEI_LED_OFF = 0x30,
++ IEI_LED_ON = 0x31,
++ IEI_LED_BLINK_5HZ = 0x32,
++ IEI_LED_BLINK_1HZ = 0x33,
++};
++
++/**
++ * struct iei_wt61p803_puzzle_led - MCU LED Driver
++ * @cdev: LED classdev
++ * @mcu: MCU struct pointer
++ * @response_buffer Global MCU response buffer
++ * @lock: General mutex lock to protect simultaneous R/W access to led_power_state
++ * @led_power_state: State of the front panel power LED
++ */
++struct iei_wt61p803_puzzle_led {
++ struct led_classdev cdev;
++ struct iei_wt61p803_puzzle *mcu;
++ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
++ struct mutex lock; /* mutex to protect led_power_state */
++ int led_power_state;
++};
++
++static inline struct iei_wt61p803_puzzle_led *cdev_to_iei_wt61p803_puzzle_led
++ (struct led_classdev *led_cdev)
++{
++ return container_of(led_cdev, struct iei_wt61p803_puzzle_led, cdev);
++}
++
++static int iei_wt61p803_puzzle_led_brightness_set_blocking(struct led_classdev *cdev,
++ enum led_brightness brightness)
++{
++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev);
++ unsigned char *resp_buf = priv->response_buffer;
++ unsigned char led_power_cmd[5] = {};
++ size_t reply_size;
++ int ret;
++
++ led_power_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ led_power_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
++ led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_POWER;
++ led_power_cmd[3] = brightness == LED_OFF ? IEI_LED_OFF : IEI_LED_ON;
++
++ ret = iei_wt61p803_puzzle_write_command(priv->mcu, led_power_cmd,
++ sizeof(led_power_cmd),
++ resp_buf,
++ &reply_size);
++ if (ret)
++ return ret;
++
++ if (reply_size != 3)
++ return -EIO;
++
++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START &&
++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK &&
++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK))
++ return -EIO;
++
++ mutex_lock(&priv->lock);
++ priv->led_power_state = brightness;
++ mutex_unlock(&priv->lock);
++
++ return 0;
++}
++
++static enum led_brightness iei_wt61p803_puzzle_led_brightness_get(struct led_classdev *cdev)
++{
++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev);
++ int led_state;
++
++ mutex_lock(&priv->lock);
++ led_state = priv->led_power_state;
++ mutex_unlock(&priv->lock);
++
++ return led_state;
++}
++
++static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent);
++ struct iei_wt61p803_puzzle_led *priv;
++ struct led_init_data init_data = {};
++ struct fwnode_handle *child;
++ int ret;
++
++ if (device_get_child_node_count(dev) != 1)
++ return -EINVAL;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->mcu = mcu;
++ priv->led_power_state = 1;
++ mutex_init(&priv->lock);
++ dev_set_drvdata(dev, priv);
++
++ child = device_get_next_child_node(dev, NULL);
++ init_data.fwnode = child;
++
++ priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
++ priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
++ priv->cdev.max_brightness = 1;
++
++ ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
++ if (ret)
++ dev_err(dev, "Could not register LED\n");
++
++ fwnode_handle_put(child);
++ return ret;
++}
++
++static const struct of_device_id iei_wt61p803_puzzle_led_of_match[] = {
++ { .compatible = "iei,wt61p803-puzzle-leds" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_led_of_match);
++
++static struct platform_driver iei_wt61p803_puzzle_led_driver = {
++ .driver = {
++ .name = "iei-wt61p803-puzzle-led",
++ .of_match_table = iei_wt61p803_puzzle_led_of_match,
++ },
++ .probe = iei_wt61p803_puzzle_led_probe,
++};
++module_platform_driver(iei_wt61p803_puzzle_led_driver);
++
++MODULE_DESCRIPTION("IEI WT61P803 PUZZLE front panel LED driver");
++MODULE_AUTHOR("Luka Kovacic <luka.kovacic@sartura.hr>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:leds-iei-wt61p803-puzzle");
diff --git a/pkgs/patches-linux-5.15/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch b/pkgs/patches-linux-5.15/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch
new file mode 100644
index 0000000..b1d420e
--- /dev/null
+++ b/pkgs/patches-linux-5.15/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch
@@ -0,0 +1,82 @@
+From 2fab3b4956c5b2f83c1e1abffc1df39de2933d83 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:36 +0000
+Subject: [PATCH 5/7] Documentation/ABI: Add iei-wt61p803-puzzle driver sysfs
+ interface documentation
+
+Add the iei-wt61p803-puzzle driver sysfs interface documentation to allow
+monitoring and control of the microcontroller from user space.
+
+Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
+Cc: Luka Perkov <luka.perkov@sartura.hr>
+Cc: Robert Marko <robert.marko@sartura.hr>
+---
+ .../testing/sysfs-driver-iei-wt61p803-puzzle | 61 +++++++++++++++++++
+ 1 file changed, 61 insertions(+)
+ create mode 100644 Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle
+
+--- /dev/null
++++ b/Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle
+@@ -0,0 +1,61 @@
++What: /sys/bus/serial/devices/.../mac_address_*
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic@sartura.hr>
++Description: (RW) Internal factory assigned MAC address values
++
++What: /sys/bus/serial/devices/.../serial_number
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic@sartura.hr>
++Description: (RW) Internal factory assigned serial number
++
++What: /sys/bus/serial/devices/.../version
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic@sartura.hr>
++Description: (RO) Internal MCU firmware version
++
++What: /sys/bus/serial/devices/.../protocol_version
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic@sartura.hr>
++Description: (RO) Internal MCU communication protocol version
++
++What: /sys/bus/serial/devices/.../power_loss_recovery
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic@sartura.hr>
++Description: (RW) Host platform power loss recovery settings
++ Value mapping: 0 - Always-On, 1 - Always-Off, 2 - Always-AC, 3 - Always-WA
++
++What: /sys/bus/serial/devices/.../bootloader_mode
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic@sartura.hr>
++Description: (RO) Internal MCU bootloader mode status
++ Value mapping:
++ 0 - normal mode
++ 1 - bootloader mode
++
++What: /sys/bus/serial/devices/.../power_status
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic@sartura.hr>
++Description: (RO) Power status indicates the host platform power on method.
++ Value mapping (bitwise list):
++ 0x80 - Null
++ 0x40 - Firmware flag
++ 0x20 - Power loss detection flag (powered off)
++ 0x10 - Power loss detection flag (AC mode)
++ 0x08 - Button power on
++ 0x04 - Wake-on-LAN power on
++ 0x02 - RTC alarm power on
++ 0x01 - AC recover power on
++
++What: /sys/bus/serial/devices/.../build_info
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic@sartura.hr>
++Description: (RO) Internal MCU firmware build date
++ Format: yyyy/mm/dd hh:mm
++
++What: /sys/bus/serial/devices/.../ac_recovery_status
++Date: September 2020
++Contact: Luka Kovacic <luka.kovacic@sartura.hr>
++Description: (RO) Host platform AC recovery status value
++ Value mapping:
++ 0 - board has not been recovered from power down
++ 1 - board has been recovered from power down
diff --git a/pkgs/patches-linux-5.15/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch b/pkgs/patches-linux-5.15/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch
new file mode 100644
index 0000000..9015436
--- /dev/null
+++ b/pkgs/patches-linux-5.15/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch
@@ -0,0 +1,74 @@
+From 0aff3e5923fecc6842473ad07a688d6e2f2c2d55 Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:37 +0000
+Subject: [PATCH 6/7] Documentation/hwmon: Add iei-wt61p803-puzzle hwmon driver
+ documentation
+
+Add the iei-wt61p803-puzzle driver hwmon driver interface documentation.
+
+Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
+Cc: Luka Perkov <luka.perkov@sartura.hr>
+Cc: Robert Marko <robert.marko@sartura.hr>
+---
+ .../hwmon/iei-wt61p803-puzzle-hwmon.rst | 43 +++++++++++++++++++
+ Documentation/hwmon/index.rst | 1 +
+ 2 files changed, 44 insertions(+)
+ create mode 100644 Documentation/hwmon/iei-wt61p803-puzzle-hwmon.rst
+
+--- /dev/null
++++ b/Documentation/hwmon/iei-wt61p803-puzzle-hwmon.rst
+@@ -0,0 +1,43 @@
++.. SPDX-License-Identifier: GPL-2.0-only
++
++Kernel driver iei-wt61p803-puzzle-hwmon
++=======================================
++
++Supported chips:
++ * IEI WT61P803 PUZZLE for IEI Puzzle M801
++
++ Prefix: 'iei-wt61p803-puzzle-hwmon'
++
++Author: Luka Kovacic <luka.kovacic@sartura.hr>
++
++
++Description
++-----------
++
++This driver adds fan and temperature sensor reading for some IEI Puzzle
++series boards.
++
++Sysfs attributes
++----------------
++
++The following attributes are supported:
++
++- IEI WT61P803 PUZZLE for IEI Puzzle M801
++
++/sys files in hwmon subsystem
++-----------------------------
++
++================= == =====================================================
++fan[1-5]_input RO files for fan speed (in RPM)
++pwm[1-2] RW files for fan[1-2] target duty cycle (0..255)
++temp[1-2]_input RO files for temperature sensors, in millidegree Celsius
++================= == =====================================================
++
++/sys files in thermal subsystem
++-------------------------------
++
++================= == =====================================================
++cur_state RW file for current cooling state of the cooling device
++ (0..max_state)
++max_state RO file for maximum cooling state of the cooling device
++================= == =====================================================
+--- a/Documentation/hwmon/index.rst
++++ b/Documentation/hwmon/index.rst
+@@ -74,6 +74,7 @@ Hardware Monitoring Kernel Drivers
+ ibmaem
+ ibm-cffps
+ ibmpowernv
++ iei-wt61p803-puzzle-hwmon
+ ina209
+ ina2xx
+ ina3221
diff --git a/pkgs/patches-linux-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch b/pkgs/patches-linux-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch
new file mode 100644
index 0000000..14b928d
--- /dev/null
+++ b/pkgs/patches-linux-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch
@@ -0,0 +1,41 @@
+From 12479baad28d2a08c6cb9e83471057635fa1635c Mon Sep 17 00:00:00 2001
+From: Luka Kovacic <luka.kovacic () sartura ! hr>
+Date: Tue, 24 Aug 2021 12:44:38 +0000
+Subject: [PATCH 7/7] MAINTAINERS: Add an entry for the IEI WT61P803 PUZZLE
+ driver
+
+Add an entry for the IEI WT61P803 PUZZLE driver (MFD, HWMON, LED drivers).
+
+Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr>
+Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr>
+Cc: Luka Perkov <luka.perkov@sartura.hr>
+Cc: Robert Marko <robert.marko@sartura.hr>
+---
+ MAINTAINERS | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -9060,6 +9060,22 @@ F: include/net/nl802154.h
+ F: net/ieee802154/
+ F: net/mac802154/
+
++IEI WT61P803 M801 MFD DRIVER
++M: Luka Kovacic <luka.kovacic@sartura.hr>
++M: Luka Perkov <luka.perkov@sartura.hr>
++M: Goran Medic <goran.medic@sartura.hr>
++L: linux-kernel@vger.kernel.org
++S: Maintained
++F: Documentation/ABI/stable/sysfs-driver-iei-wt61p803-puzzle
++F: Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml
++F: Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml
++F: Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml
++F: Documentation/hwmon/iei-wt61p803-puzzle-hwmon.rst
++F: drivers/hwmon/iei-wt61p803-puzzle-hwmon.c
++F: drivers/leds/leds-iei-wt61p803-puzzle.c
++F: drivers/mfd/iei-wt61p803-puzzle.c
++F: include/linux/mfd/iei-wt61p803-puzzle.h
++
+ IFE PROTOCOL
+ M: Yotam Gigi <yotam.gi@gmail.com>
+ M: Jamal Hadi Salim <jhs@mojatatu.com>
diff --git a/pkgs/patches-linux-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch b/pkgs/patches-linux-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch
new file mode 100644
index 0000000..150a654
--- /dev/null
+++ b/pkgs/patches-linux-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch
@@ -0,0 +1,271 @@
+--- a/drivers/leds/leds-iei-wt61p803-puzzle.c
++++ b/drivers/leds/leds-iei-wt61p803-puzzle.c
+@@ -9,9 +9,13 @@
+ #include <linux/mfd/iei-wt61p803-puzzle.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
++#include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/property.h>
+ #include <linux/slab.h>
++#include <linux/workqueue.h>
++
++#define IEI_LEDS_MAX 4
+
+ enum iei_wt61p803_puzzle_led_state {
+ IEI_LED_OFF = 0x30,
+@@ -33,7 +37,11 @@ struct iei_wt61p803_puzzle_led {
+ struct iei_wt61p803_puzzle *mcu;
+ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE];
+ struct mutex lock; /* mutex to protect led_power_state */
++ struct work_struct work;
+ int led_power_state;
++ int id;
++ u8 blinking;
++ bool active_low;
+ };
+
+ static inline struct iei_wt61p803_puzzle_led *cdev_to_iei_wt61p803_puzzle_led
+@@ -51,10 +59,18 @@ static int iei_wt61p803_puzzle_led_brigh
+ size_t reply_size;
+ int ret;
+
++ if (priv->blinking) {
++ if (brightness == LED_OFF)
++ priv->blinking = 0;
++ else
++ return 0;
++ }
++
+ led_power_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
+ led_power_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
+- led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_POWER;
+- led_power_cmd[3] = brightness == LED_OFF ? IEI_LED_OFF : IEI_LED_ON;
++ led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id);
++ led_power_cmd[3] = ((brightness == LED_OFF) ^ priv->active_low) ?
++ IEI_LED_OFF : priv->blinking?priv->blinking:IEI_LED_ON;
+
+ ret = iei_wt61p803_puzzle_write_command(priv->mcu, led_power_cmd,
+ sizeof(led_power_cmd),
+@@ -90,39 +106,166 @@ static enum led_brightness iei_wt61p803_
+ return led_state;
+ }
+
++static void iei_wt61p803_puzzle_led_apply_blink(struct work_struct *work)
++{
++ struct iei_wt61p803_puzzle_led *priv = container_of(work, struct iei_wt61p803_puzzle_led, work);
++ unsigned char led_blink_cmd[5] = {};
++ unsigned char resp_buf[IEI_WT61P803_PUZZLE_BUF_SIZE];
++ size_t reply_size;
++
++ led_blink_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START;
++ led_blink_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED;
++ led_blink_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id);
++ led_blink_cmd[3] = priv->blinking;
++
++ iei_wt61p803_puzzle_write_command(priv->mcu, led_blink_cmd,
++ sizeof(led_blink_cmd),
++ resp_buf,
++ &reply_size);
++
++ return;
++}
++
++static int iei_wt61p803_puzzle_led_set_blink(struct led_classdev *cdev,
++ unsigned long *delay_on,
++ unsigned long *delay_off)
++{
++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev);
++ u8 blink_mode = 0;
++ int ret = 0;
++
++ /* set defaults */
++ if (!*delay_on && !*delay_off) {
++ *delay_on = 500;
++ *delay_off = 500;
++ }
++
++ /* minimum delay for soft-driven blinking is 100ms to keep load low */
++ if (*delay_on < 100)
++ *delay_on = 100;
++
++ if (*delay_off < 100)
++ *delay_off = 100;
++
++ /* offload blinking to hardware, if possible */
++ if (*delay_on != *delay_off) {
++ ret = -EINVAL;
++ } else if (*delay_on == 100) {
++ blink_mode = IEI_LED_BLINK_5HZ;
++ *delay_on = 100;
++ *delay_off = 100;
++ } else if (*delay_on <= 500) {
++ blink_mode = IEI_LED_BLINK_1HZ;
++ *delay_on = 500;
++ *delay_off = 500;
++ } else {
++ ret = -EINVAL;
++ }
++
++ mutex_lock(&priv->lock);
++ priv->blinking = blink_mode;
++ mutex_unlock(&priv->lock);
++
++ if (blink_mode)
++ schedule_work(&priv->work);
++
++ return ret;
++}
++
++
++static int iei_wt61p803_puzzle_led_set_dt_default(struct led_classdev *cdev,
++ struct device_node *np)
++{
++ const char *state;
++ int ret = 0;
++
++ state = of_get_property(np, "default-state", NULL);
++ if (state) {
++ if (!strcmp(state, "on")) {
++ ret =
++ iei_wt61p803_puzzle_led_brightness_set_blocking(
++ cdev, cdev->max_brightness);
++ } else {
++ ret = iei_wt61p803_puzzle_led_brightness_set_blocking(
++ cdev, LED_OFF);
++ }
++ }
++
++ return ret;
++}
++
+ static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
++ struct device_node *np = dev_of_node(dev);
++ struct device_node *child;
+ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent);
+ struct iei_wt61p803_puzzle_led *priv;
+- struct led_init_data init_data = {};
+- struct fwnode_handle *child;
+ int ret;
++ u32 reg;
+
+- if (device_get_child_node_count(dev) != 1)
++ if (device_get_child_node_count(dev) > IEI_LEDS_MAX)
+ return -EINVAL;
+
+- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- priv->mcu = mcu;
+- priv->led_power_state = 1;
+- mutex_init(&priv->lock);
+- dev_set_drvdata(dev, priv);
+-
+- child = device_get_next_child_node(dev, NULL);
+- init_data.fwnode = child;
+-
+- priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
+- priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
+- priv->cdev.max_brightness = 1;
++ for_each_available_child_of_node(np, child) {
++ struct led_init_data init_data = {};
+
+- ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
+- if (ret)
+- dev_err(dev, "Could not register LED\n");
++ ret = of_property_read_u32(child, "reg", &reg);
++ if (ret) {
++ dev_err(dev, "Failed to read led 'reg' property\n");
++ goto put_child_node;
++ }
++
++ if (reg > IEI_LEDS_MAX) {
++ dev_err(dev, "Invalid led reg %u\n", reg);
++ ret = -EINVAL;
++ goto put_child_node;
++ }
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv) {
++ ret = -ENOMEM;
++ goto put_child_node;
++ }
++
++ mutex_init(&priv->lock);
++
++ dev_set_drvdata(dev, priv);
++
++ if (of_property_read_bool(child, "active-low"))
++ priv->active_low = true;
++
++ priv->mcu = mcu;
++ priv->id = reg;
++ priv->led_power_state = 1;
++ priv->blinking = 0;
++ init_data.fwnode = of_fwnode_handle(child);
++
++ priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking;
++ priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get;
++ priv->cdev.blink_set = iei_wt61p803_puzzle_led_set_blink;
++
++ priv->cdev.max_brightness = 1;
++
++ INIT_WORK(&priv->work, iei_wt61p803_puzzle_led_apply_blink);
++
++ ret = iei_wt61p803_puzzle_led_set_dt_default(&priv->cdev, child);
++ if (ret) {
++ dev_err(dev, "Could apply default from DT\n");
++ goto put_child_node;
++ }
++
++ ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data);
++ if (ret) {
++ dev_err(dev, "Could not register LED\n");
++ goto put_child_node;
++ }
++ }
++
++ return ret;
+
+- fwnode_handle_put(child);
++put_child_node:
++ of_node_put(child);
+ return ret;
+ }
+
+--- a/include/linux/mfd/iei-wt61p803-puzzle.h
++++ b/include/linux/mfd/iei-wt61p803-puzzle.h
+@@ -36,7 +36,7 @@
+ #define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS 0x41 /* A */
+
+ #define IEI_WT61P803_PUZZLE_CMD_LED 0x52 /* R */
+-#define IEI_WT61P803_PUZZLE_CMD_LED_POWER 0x31 /* 1 */
++#define IEI_WT61P803_PUZZLE_CMD_LED_SET(n) (0x30 | (n))
+
+ #define IEI_WT61P803_PUZZLE_CMD_TEMP 0x54 /* T */
+ #define IEI_WT61P803_PUZZLE_CMD_TEMP_ALL 0x41 /* A */
+--- a/drivers/mfd/iei-wt61p803-puzzle.c
++++ b/drivers/mfd/iei-wt61p803-puzzle.c
+@@ -176,6 +176,9 @@ static int iei_wt61p803_puzzle_recv_buf(
+ struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev);
+ int ret;
+
++ print_hex_dump_debug("puzzle-mcu rx: ", DUMP_PREFIX_NONE,
++ 16, 1, data, size, false);
++
+ ret = iei_wt61p803_puzzle_process_resp(mcu, data, size);
+ /* Return the number of processed bytes if function returns error,
+ * discard the remaining incoming data, since the frame this data
+@@ -246,6 +249,9 @@ int iei_wt61p803_puzzle_write_command(st
+
+ cmd[size - 1] = iei_wt61p803_puzzle_checksum(cmd, size - 1);
+
++ print_hex_dump_debug("puzzle-mcu tx: ", DUMP_PREFIX_NONE,
++ 16, 1, cmd, size, false);
++
+ /* Initialize reply struct */
+ reinit_completion(&mcu->reply->received);
+ mcu->reply->size = 0;
diff --git a/pkgs/patches-linux-5.15/910-kobject_uevent.patch b/pkgs/patches-linux-5.15/910-kobject_uevent.patch
new file mode 100644
index 0000000..c4c41ca
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/911-kobject_add_broadcast_uevent.patch b/pkgs/patches-linux-5.15/911-kobject_add_broadcast_uevent.patch
new file mode 100644
index 0000000..a487d55
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/920-device_tree_cmdline.patch b/pkgs/patches-linux-5.15/920-device_tree_cmdline.patch
new file mode 100644
index 0000000..d1f36e7
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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/pkgs/patches-linux-5.15/920-mangle_bootargs.patch b/pkgs/patches-linux-5.15/920-mangle_bootargs.patch
new file mode 100644
index 0000000..dbcd6a5
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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
+@@ -1810,6 +1810,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
+@@ -615,6 +615,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
+@@ -955,6 +978,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();
diff --git a/pkgs/patches-linux-5.15/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch b/pkgs/patches-linux-5.15/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch
new file mode 100644
index 0000000..4f4d6c7
--- /dev/null
+++ b/pkgs/patches-linux-5.15/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)